From 2dd726723e9551571699216c863ad59cb223668f Mon Sep 17 00:00:00 2001 From: Lightos1 <124387232+Lightos1@users.noreply.github.com> Date: Fri, 17 Apr 2026 20:26:32 +0200 Subject: [PATCH] add latency switching --- .../loader/source/oc/customize.cpp | 20 ++++- .../loader/source/oc/customize.hpp | 3 + .../source/oc/mariko/calculate_timings.cpp | 74 ++++++++++++++++++- .../source/oc/mariko/calculate_timings.hpp | 2 +- .../loader/source/oc/mtc_timing_value.hpp | 3 + .../stratosphere/loader/source/oc/pcv/pcv.cpp | 2 +- .../loader/source/oc/pcv/pcv_mariko.cpp | 24 ++---- 7 files changed, 105 insertions(+), 23 deletions(-) diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index a22c0c70..06298463 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -44,9 +44,9 @@ volatile CustomizeTable C = { /* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */ .stepMode = StepMode_66MHz, -.marikoEmcMaxClock = 1866000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */ +.marikoEmcMaxClock = 2933000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */ .marikoEmcVddqVolt = 600000, -.emcDvbShift = 0, +.emcDvbShift = 10, // Primary .t1_tRCD = 0, @@ -59,6 +59,20 @@ volatile CustomizeTable C = { .t7_tWTR = 0, .t8_tREFI = 0, +.readLatency = { + 2133000, + 2400000, + 2600000, + C.marikoEmcMaxClock, +}, + +.writeLatency = { + 2133000, + 2400000, + 2600000, + C.marikoEmcMaxClock, +}, + /* You can mix and match different latencies if needed */ /* * Read: @@ -79,7 +93,7 @@ volatile CustomizeTable C = { .eristaCpuUV = 0, .eristaCpuVmin = 800, .eristaCpuMaxVolt = 1200, -/* Unlocks up to 2295 Mhz CPU, usage is not recommended. */ +/* Unlocks up to 2397 Mhz CPU, usage is not recommended. */ .eristaCpuUnlock = DISABLED, .marikoCpuUVLow = 0, // No undervolt diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index d500db15..93169bca 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -103,6 +103,9 @@ typedef struct CustomizeTable { u32 t7_tWTR; u32 t8_tREFI; + u32 readLatency[4]; + u32 writeLatency[4]; + u32 mem_burst_read_latency; u32 mem_burst_write_latency; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp index 91a49bbb..7d58178c 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp @@ -30,6 +30,73 @@ namespace ams::ldr::hoc::pcv::mariko { rext = 0x1A; } + void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) { + latency += index * latencyStep; + } + + static u32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) { + u32 maxIndex = 0; + for (u32 i = 0; i < latencySize; ++i) { + if (latencyArray[i]) { + maxIndex = i; + } + } + + return maxIndex; + } + + void AutoLatency(volatile u32 &latency, u32 freq, u32 latencyStep) { + if (freq >= 1866'000 && freq < 2133000) { + latency += latencyStep * 2; + } else if (freq >= 2133'000) { + latency += latencyStep * 3; + } else { + latency += latencyStep; + } + /* 1333 latency is not possible with this config. */ + } + + void HandleLatency(u32 freq, volatile u32 &latency, volatile u32 *latencyArray, u32 indexMax, u32 latencyStep) { + for (u32 i = 0; i <= indexMax; ++i) { + if (latencyArray[i] != 0 && freq <= latencyArray[i]) { + SwitchLatency(latency, i, latencyStep); + return; + } + } + + SwitchLatency(latency, indexMax, latencyStep); + } + + void HandleLatency(u32 freq) { + static u32 rlIndexMax = GetMaxLatencyIndex(C.readLatency, std::size(C.readLatency)); + static u32 wlIndexMax = GetMaxLatencyIndex(C.writeLatency, std::size(C.writeLatency)); + constexpr u32 ReadLatencyStep = 4; + constexpr u32 WriteLatencyStep = 2; + bool autoLatencyRead = false, autoLatencyWrite = false; + + if (rlIndexMax == 0) { + AutoLatency(RL, freq, ReadLatencyStep); + autoLatencyRead = true; + } + + if (wlIndexMax == 0) { + AutoLatency(WL, freq, WriteLatencyStep); + autoLatencyWrite = true; + } + + if (autoLatencyRead && autoLatencyWrite) { + return; + } + + if (!autoLatencyRead) { + HandleLatency(freq, RL, C.readLatency, rlIndexMax, ReadLatencyStep); + } + + if (!autoLatencyWrite) { + HandleLatency(freq, WL, C.writeLatency, wlIndexMax, WriteLatencyStep); + } + } + void CalculateMrw2() { static const u8 rlMapDBI[8] = { 6, 12, 16, 22, 28, 32, 36, 40 @@ -59,7 +126,12 @@ namespace ams::ldr::hoc::pcv::mariko { mrw2 = static_cast(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6))); } - void CalculateTimings(double tCK_avg) { + void CalculateTimings(double tCK_avg, u32 freq) { + RL = 28; + WL = 12; + + HandleLatency(freq); + GetRext(); tR2P = CEIL((RL * 0.426) - 2.0); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp index 61b324de..84165e07 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp @@ -18,7 +18,7 @@ namespace ams::ldr::hoc::pcv::mariko { - void CalculateTimings(double tCK_avg); + void CalculateTimings(double tCK_avg, u32 freq); } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp index 765e66c0..609901d3 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp @@ -126,6 +126,9 @@ namespace ams::ldr::hoc { const u32 tFAW = static_cast(tRRD * 4.0); const double tRPab = tRPpb + 3; + inline u32 RL; + inline u32 WL; + inline u32 tR2P; inline u32 tR2W; inline u32 tRTM; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp index 0b9ff6c8..c635b435 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp @@ -152,7 +152,7 @@ namespace ams::ldr::hoc::pcv { { marikoCpuDvfsMaxFreq, 1785'000, 2703'000, false, panic::Cpu }, { C.commonEmcMemVolt, 912'500, 1350'000, false, panic::Emc }, // Official burst vmax for the RAMs is 1500mV { GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000, false, panic::Emc }, - { C.marikoEmcMaxClock, 1600'000, 3500'000, false, panic::Emc }, + { C.marikoEmcMaxClock, 1600'000, 3700'000, false, panic::Emc }, { C.marikoEmcVddqVolt, 250'000, 700'000, false, panic::Emc }, { eristaGpuDvfsMaxFreq, 768'000, 1152'000, false, panic::Gpu }, { marikoGpuDvfsMaxFreq, 768'000, 1536'000, false, panic::Gpu }, 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 a6ee1268..51217fe2 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -375,7 +375,7 @@ namespace ams::ldr::hoc::pcv::mariko { const u32 dyn_self_ref_control = (static_cast(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000); - CalculateTimings(tCK_avg); + CalculateTimings(tCK_avg, table->rate_khz); WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD)); WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD)); @@ -592,7 +592,7 @@ namespace ams::ldr::hoc::pcv::mariko { } Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) { - Log("Rate_khz: %u, revision: %u\n", tableStart->rate_khz, tableStart->rev); + // Log("Rate_khz: %u, revision: %u\n", tableStart->rate_khz, tableStart->rev); R_UNLESS(tableStart->rate_khz == expectedFreq, ldr::ResultInvalidMtcTable()); R_UNLESS(tableStart->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); @@ -624,8 +624,8 @@ namespace ams::ldr::hoc::pcv::mariko { } NORETURN void AbortInvalidDramId() { - // Log("Invalid dram id\n"); - // ViewLog(); + Log("Invalid dram id\n"); + ViewLog(); panic::SmcError(panic::Emc); CRASH("Invalid dram id\n"); } @@ -688,16 +688,7 @@ namespace ams::ldr::hoc::pcv::mariko { newEmcList.push_back(newFreq); } - Log("Size = %u\n", newEmcList.size()); - for (u32 i = 0; i < newEmcList.size(); ++i) { - Log("Freq: %u\n", newEmcList[i]); - } - - Log("C.marikoEmcMaxClock: %u", C.marikoEmcMaxClock); - - /* TODO: Test */ - //ViewLog(); - newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryCount)); + newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit)); } void MtcExtendTables(MarikoMtcTable *table) { @@ -716,7 +707,7 @@ namespace ams::ldr::hoc::pcv::mariko { static const DramId dramId = [] { DramId id = GetDramId(); id = IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL; - //Log("Dram id: %u\n", id); + // Log("Dram id: %u\n", id); return id; }(); @@ -747,7 +738,7 @@ namespace ams::ldr::hoc::pcv::mariko { MtcExtendTables(table); for (u32 i = 0; i < newEmcList.size(); ++i) { - Log("freqList[%u] = %u\n", i, newEmcList[i]); + // Log("freqList[%u] = %u\n", i, newEmcList[i]); } if (R_FAILED(MtcValidateAllTables(table, newEmcList.data(), newEmcList.size()))) { @@ -946,7 +937,6 @@ namespace ams::ldr::hoc::pcv::mariko { CRASH(entry.description); } } - // ViewLog(); } }