From 51f935c2520d239d00bef499e753752bef8fecbe Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Thu, 16 Apr 2026 20:51:26 +0200
Subject: [PATCH 01/12] initial mrf implementation with some bugs to fix
---
.../loader/source/oc/customize.cpp | 2 +-
.../oc/erista/calculate_timings_erista.cpp | 2 +-
.../source/oc/mariko/calculate_timings.cpp | 42 ++-
.../source/oc/mariko/calculate_timings.hpp | 2 +-
.../loader/source/oc/mtc_timing_value.hpp | 58 ++--
.../loader/source/oc/oc_common.hpp | 1 +
.../stratosphere/loader/source/oc/pcv/pcv.cpp | 2 +-
.../loader/source/oc/pcv/pcv_asm.hpp | 19 +-
.../loader/source/oc/pcv/pcv_erista.hpp | 33 +-
.../loader/source/oc/pcv/pcv_mariko.cpp | 290 +++++++++++++-----
.../loader/source/oc/pcv/pcv_mariko.hpp | 153 ++++++++-
11 files changed, 493 insertions(+), 111 deletions(-)
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
index 1be77755..9f85de34 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
@@ -39,7 +39,7 @@ volatile CustomizeTable C = {
.eristaEmcMaxClock1 = 1600000,
.eristaEmcMaxClock2 = 1600000,
-.marikoEmcMaxClock = 1866000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
+.marikoEmcMaxClock = 1633000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
.marikoEmcVddqVolt = 600000, /* Micron: 600mV, other manafacturers: 640mV */
.emcDvbShift = 0,
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp
index 721c1a92..ad482924 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp
@@ -14,7 +14,7 @@
* along with this program. If not, see .
*/
-#include "../oc_common.hpp"
+#include
#include "../mtc_timing_value.hpp"
namespace ams::ldr::hoc::pcv::erista {
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 78f8e1f3..91a49bbb 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp
@@ -14,7 +14,7 @@
* along with this program. If not, see .
*/
-#include "../oc_common.hpp"
+#include
#include "../mtc_timing_value.hpp"
#include "timing_tables.hpp"
@@ -59,8 +59,46 @@ namespace ams::ldr::hoc::pcv::mariko {
mrw2 = static_cast(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6)));
}
- void CalculateTimings() {
+ void CalculateTimings(double tCK_avg) {
GetRext();
+
+ tR2P = CEIL((RL * 0.426) - 2.0);
+ tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW;
+ tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
+ tRATM = CEIL((tRTM - 10.0) + (RL * 0.426));
+
+ rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017);
+ qpop = rdv - 14;
+ quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782);
+ quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg);
+ einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width;
+ einput = quse - CEIL(9.928 / tCK_avg);
+ u32 qrst_duration = FLOOR(8.399 - tCK_avg);
+ u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0));
+ qrst = PACK_U32(qrst_duration, qrstLow);
+ ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0);
+ qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput);
+ tW2P = (CEIL(WL * 1.7303) * 2) - 5;
+ tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast(tW2P))) + (BL / 2));
+ tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR;
+ tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753));
+ tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0;
+
+ wdv = WL;
+ wsv = WL - 2;
+ wev = 0xA + (WL - 14);
+
+ u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7)));
+ u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0);
+ obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow);
+
+ pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361)));
+
+ tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0;
+
+ u32 tMMRI = tRCD + (tCK_avg * 3);
+ pdex2mrr = tMMRI + 10;
+
CalculateMrw2();
}
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 dbf63bae..61b324de 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();
+ void CalculateTimings(double tCK_avg);
}
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 b6a72cc6..765e66c0 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp
@@ -111,8 +111,6 @@ namespace ams::ldr::hoc {
const std::array tWTR_values = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
const std::array tREFpb_values = { 3900, 5850, 7800, 11700, 15600, 99999 };
- const double tCK_avg = 1000'000.0 / C.marikoEmcMaxClock;
-
const u32 tRCD = tRCD_values[C.t1_tRCD];
const u32 tRPpb = tRP_values[C.t2_tRP];
const u32 tRAS = tRAS_values[C.t3_tRAS];
@@ -128,43 +126,39 @@ namespace ams::ldr::hoc {
const u32 tFAW = static_cast(tRRD * 4.0);
const double tRPab = tRPpb + 3;
- const u32 tR2P = CEIL((RL * 0.426) - 2.0);
- const u32 tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW;
- const u32 tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
- const u32 tRATM = CEIL((tRTM - 10.0) + (RL * 0.426));
+ inline u32 tR2P;
+ inline u32 tR2W;
+ inline u32 tRTM;
+ inline u32 tRATM;
inline u32 rext;
- const u32 rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017);
- const u32 qpop = rdv - 14;
- const u32 quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782);
- const u32 quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg);
- const u32 einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width;
- const u32 einput = quse - CEIL(9.928 / tCK_avg);
- const u32 qrst_duration = FLOOR(8.399 - tCK_avg);
- const u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0));
- const u32 qrst = PACK_U32(qrst_duration, qrstLow);
- const u32 ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0);
- const u32 qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput);
- const u32 tW2P = (CEIL(WL * 1.7303) * 2) - 5;
- const u32 tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast(tW2P))) + (BL / 2));
- const u32 tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR;
- const u32 tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753));
- const u32 tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0;
+ inline u32 rdv;
+ inline u32 qpop;
+ inline u32 quse_width;
+ inline u32 quse;
+ inline u32 einput_duration;
+ inline u32 einput;
+ inline u32 qrst;
+ inline u32 ibdly;
+ inline u32 qsafe;
- const u32 wdv = WL;
- const u32 wsv = WL - 2;
- const u32 wev = 0xA + (WL - 14);
+ inline u32 tW2P;
+ inline u32 tWTPDEN;
+ inline u32 tW2R;
+ inline u32 tWTM;
+ inline u32 tWATM;
- const u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7)));
- const u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0);
- const u32 obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow);
+ inline u32 wdv;
+ inline u32 wsv;
+ inline u32 wev;
- const u32 pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361)));
+ inline u32 obdly;
- const u32 tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0;
+ inline u32 pdex2rw;
- const double tMMRI = tRCD + (tCK_avg * 3);
- const double pdex2mrr = tMMRI + 10; /* Do this properly? */
+ inline u32 tCLKSTOP;
+
+ inline u32 pdex2mrr;
inline u8 mrw2;
}
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp
index 2e900f72..2205c4a3 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp
@@ -51,6 +51,7 @@ namespace ams::ldr {
R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013);
R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014);
R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015);
+ R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
}
namespace ams::ldr::hoc {
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
index 3b29b705..0b9ff6c8 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
@@ -178,7 +178,7 @@ namespace ams::ldr::hoc::pcv {
if (isMariko) {
mariko::Patch(mapped_nso, nso_size);
} else {
- erista::Patch(mapped_nso, nso_size);
+ mariko::Patch(mapped_nso, nso_size);
}
#endif
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp
index bf6d0b1d..ac96bb19 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp
@@ -20,10 +20,12 @@
#pragma once
-#include "../oc_common.hpp"
+#include
namespace ams::ldr::hoc::pcv {
+ constexpr u32 NopIns = 0x1f2003d5;
+
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) {
return ((ins1 ^ ins2) >> 5) == 0;
};
@@ -44,4 +46,19 @@ namespace ams::ldr::hoc::pcv {
return static_cast((ins >> 5) & 0xFFFF);
};
+ inline auto AsmCompareBrNoRd = [](u32 ins1, u32 ins2) {
+ constexpr u32 RegMask = ~(((1 << 5) - 1) << 5);
+ return ((ins1 & RegMask) ^ (ins2 & RegMask)) == 0;
+ };
+
+ inline auto AsmCompareAddNoImm12 = [](u32 ins1, u32 ins2) {
+ constexpr u32 Imm12Mask = ~(((1 << 12) - 1) << 10);
+ return ((ins1 & Imm12Mask) ^ (ins2 & Imm12Mask)) == 0;
+ };
+
+ inline auto AsmCompareAdrpNoImm = [](u32 ins1, u32 ins2) {
+ constexpr u32 ImmMask = ~((((1 << 2) - 1) << 29) | (((1 << 19) - 1) << 5));
+ return ((ins1 & ImmMask) ^ (ins2 & ImmMask)) == 0;
+ };
+
}
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp
index 35dd3326..c9b180ac 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp
@@ -113,7 +113,38 @@ namespace ams::ldr::hoc::pcv::erista {
constexpr u32 EmcClkMinFreq = 40800; /* 40.8 MHz table only exists on erista. */
constexpr u32 EmcClkPllmLimit = 1866'000'000;
- constexpr u32 MTC_TABLE_REV = 7;
+ constexpr u32 MTC_TABLE_REV = 7;
+ constexpr u32 MtcTableCountDefault = 10;
+
+ constexpr size_t MtcFullTableSize = sizeof(EristaMtcTable) * MtcTableCountDefault;
+ constexpr u32 MtcFullTableCount = 3;
+
+ /* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
+ enum DramId {
+ ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH = 0,
+ ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE = 1,
+ ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC = 2, /* This doesn't have a table in pcv? Wtf? */
+ ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH = 4,
+ ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX = 7, /* No table, but expected */
+ };
+
+ enum MtcTableIndex {
+ T210SdevEmcDvfsTableS4gb01 = 0, /* HB-MGCH */
+ T210SdevEmcDvfsTableS6gb01 = 1, /* HM-MGCH */
+ T210SdevEmcDvfsTableH4gb01 = 2, /* HR-NLE */
+ MtcTableIndex_Invalid = 3,
+ };
+
+ struct MtcDramIndex {
+ DramId dramId;
+ MtcTableIndex index;
+ };
+
+ constexpr MtcDramIndex mtcIndexTable[] = {
+ { ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, },
+ { ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, },
+ { ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, },
+ };
void Patch(uintptr_t mapped_nso, size_t nso_size);
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 6e9313f5..c55b545a 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
@@ -18,6 +18,7 @@
* along with this program. If not, see .
*/
+#include
#include "pcv.hpp"
#include "../mtc_timing_value.hpp"
#include "../mariko/calculate_timings.hpp"
@@ -53,7 +54,7 @@ namespace ams::ldr::hoc::pcv::mariko {
R_SKIP();
}
- PATCH_OFFSET(ptr + 0, C.marikoGpuVmin);
+ PATCH_OFFSET(ptr, C.marikoGpuVmin);
PATCH_OFFSET(ptr + 3, C.marikoGpuVmin);
PATCH_OFFSET(ptr + 6, C.marikoGpuVmin);
PATCH_OFFSET(ptr + 9, C.marikoGpuVmin);
@@ -349,6 +350,8 @@ namespace ams::ldr::hoc::pcv::mariko {
TABLE->shadow_regs_ca_train.PARAM = VALUE; \
TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
+ const double tCK_avg = 1000'000.0 / table->rate_khz;
+
#define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg))
/* Ram power down */
@@ -372,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();
+ CalculateTimings(tCK_avg);
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
@@ -399,7 +402,7 @@ namespace ams::ldr::hoc::pcv::mariko {
WRITE_PARAM_ALL_REG(table, emc_twtm, tWTM);
WRITE_PARAM_ALL_REG(table, emc_twatm, tWATM);
WRITE_PARAM_ALL_REG(table, emc_rext, rext);
- WRITE_PARAM_ALL_REG(table, emc_wext, (C.marikoEmcMaxClock >= 2533000) ? 0x19 : 0x16);
+ WRITE_PARAM_ALL_REG(table, emc_wext, (table->rate_khz >= 2533000) ? 0x19 : 0x16);
WRITE_PARAM_ALL_REG(table, emc_refresh, refresh_raw);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, refresh_raw / 4);
WRITE_PARAM_ALL_REG(table, emc_trefbw, trefbw);
@@ -443,7 +446,7 @@ namespace ams::ldr::hoc::pcv::mariko {
constexpr double MC_ARB_DIV = 4.0;
constexpr u32 MC_ARB_SFA = 2;
- table->burst_mc_regs.mc_emem_arb_cfg = C.marikoEmcMaxClock / (33.3 * 1000) / MC_ARB_DIV;
+ table->burst_mc_regs.mc_emem_arb_cfg = table->rate_khz / (33.3 * 1000) / MC_ARB_DIV;
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
@@ -485,7 +488,7 @@ namespace ams::ldr::hoc::pcv::mariko {
table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = 0x115;
- if (C.marikoEmcMaxClock >= 2133000) {
+ if (table->rate_khz >= 2133000) {
table->la_scale_regs.mc_ftop_ptsa_rate = 0x1F;
} else {
table->la_scale_regs.mc_ftop_ptsa_rate = 0x1B;
@@ -494,14 +497,14 @@ namespace ams::ldr::hoc::pcv::mariko {
table->la_scale_regs.mc_ptsa_grant_decrement = 0x17ff;
constexpr u32 MaskHigh = 0xFF00FFFF;
- constexpr u32 Mask2 = 0xFFFFFF00;
- constexpr u32 Mask3 = 0xFF00FF00;
+ constexpr u32 Mask2 = 0xFFFFFF00;
+ constexpr u32 Mask3 = 0xFF00FF00;
- const u32 allowance1 = static_cast(0x32000 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
- const u32 allowance2 = static_cast(0x9C40 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
- const u32 allowance3 = static_cast(0xB540 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
- const u32 allowance4 = static_cast(0x9600 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
- const u32 allowance5 = static_cast(0x8980 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
+ const u32 allowance1 = static_cast(0x32000 / (table->rate_khz / 0x3E8)) & 0xFF;
+ const u32 allowance2 = static_cast(0x9C40 / (table->rate_khz / 0x3E8)) & 0xFF;
+ const u32 allowance3 = static_cast(0xB540 / (table->rate_khz / 0x3E8)) & 0xFF;
+ const u32 allowance4 = static_cast(0x9600 / (table->rate_khz / 0x3E8)) & 0xFF;
+ const u32 allowance5 = static_cast(0x8980 / (table->rate_khz / 0x3E8)) & 0xFF;
table->la_scale_regs.mc_latency_allowance_xusb_0 = (table->la_scale_regs.mc_latency_allowance_xusb_0 & MaskHigh) | (allowance1 << 16);
table->la_scale_regs.mc_latency_allowance_xusb_1 = (table->la_scale_regs.mc_latency_allowance_xusb_1 & MaskHigh) | (allowance1 << 16);
@@ -525,8 +528,8 @@ namespace ams::ldr::hoc::pcv::mariko {
table->dram_timings.t_rp = tRFCpb;
table->dram_timings.t_rfc = tRFCab;
- table->dram_timings.rl = RL;
+ table->dram_timings.rl = RL;
table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast(mrw2);
table->emc_cfg_2 = 0x11083D;
}
@@ -535,24 +538,24 @@ namespace ams::ldr::hoc::pcv::mariko {
constexpr u32 PllOscInKHz = 38400;
constexpr u32 PllOscHalfKHz = 19200;
- double target_freq_d = static_cast(C.marikoEmcMaxClock);
+ double target_freq_d = static_cast(table->rate_khz);
- s32 divm_candidate_half = static_cast(C.marikoEmcMaxClock / PllOscHalfKHz);
+ s32 divm_candidate_half = static_cast(table->rate_khz / PllOscHalfKHz);
- bool remainder_check = (C.marikoEmcMaxClock - PllOscInKHz * (C.marikoEmcMaxClock / PllOscInKHz)) > (C.marikoEmcMaxClock - PllOscHalfKHz * divm_candidate_half) && static_cast(((target_freq_d / PllOscHalfKHz - divm_candidate_half - 0.5) * 8192.0)) != 0;
+ bool remainder_check = (table->rate_khz - PllOscInKHz * (table->rate_khz / PllOscInKHz)) > (table->rate_khz - PllOscHalfKHz * divm_candidate_half) && static_cast(((target_freq_d / PllOscHalfKHz - divm_candidate_half - 0.5) * 8192.0)) != 0;
u32 divm_final = remainder_check + 1;
table->pllmb_divm = divm_final;
double div_step_d = static_cast(PllOscInKHz) / divm_final;
- s32 divn_integer = static_cast(C.marikoEmcMaxClock / div_step_d);
+ s32 divn_integer = static_cast(table->rate_khz / div_step_d);
table->pllmb_divn = divn_integer;
u32 divn_fraction = static_cast((target_freq_d / div_step_d - divn_integer - 0.5) * 8192.0);
u32 actual_freq_khz = static_cast((divn_integer + 0.5 + divn_fraction * 0.000122070312) * div_step_d);
- if (C.marikoEmcMaxClock - 2366001 < 133999) {
+ if (table->rate_khz - 2366001 < 133999) {
s32 divn_fraction_ssc = static_cast((actual_freq_khz * 0.997 / div_step_d - divn_integer - 0.5) * 8192.0);
double delta_scaled = (0.3 / div_step_d + 0.3 / div_step_d) * (divn_fraction - divn_fraction_ssc);
@@ -580,7 +583,7 @@ namespace ams::ldr::hoc::pcv::mariko {
table->pllm_ss_cfg &= 0xBFFFFFFF;
table->pllmb_ss_cfg &= 0xBFFFFFFF;
- u64 pair = (static_cast(divn_fraction) << 32) | static_cast(C.marikoEmcMaxClock);
+ u64 pair = (static_cast(divn_fraction) << 32) | static_cast(table->rate_khz);
u32 pll_misc = (table->pllm_ss_ctrl2 & 0xFFFF0000) | static_cast((pair - actual_freq_khz) >> 32);
table->pllm_ss_ctrl2 = pll_misc;
@@ -588,52 +591,168 @@ namespace ams::ldr::hoc::pcv::mariko {
}
}
- Result MemFreqMtcTable(u32 *ptr) {
- u32 khz_list[] = {1600000, 1331200, 204000};
- u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
+ Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) {
+ 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());
- // Generate list for mtc table pointers
- MarikoMtcTable *table_list[khz_list_size];
- for (u32 i = 0; i < khz_list_size; i++) {
- u8 *table = reinterpret_cast(ptr) - offsetof(MarikoMtcTable, rate_khz) - i * sizeof(MarikoMtcTable);
- 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());
+ R_SUCCEED();
+ }
+
+ Result MtcValidateAllTables(MarikoMtcTable *tableStart, const u32 *validationList, u32 tableCount) {
+ for (u32 i = 0; i < tableCount; ++i) {
+ R_UNLESS(R_SUCCEEDED(VerifyMtcTable(&tableStart[i], validationList[i])), ldr::ResultInvalidMtcTable());
}
- if (C.marikoEmcMaxClock <= EmcClkOSLimit)
+ R_SUCCEED();
+ }
+
+ DramId GetDramId() {
+ u64 id64;
+ splGetConfig(SplConfigItem_DramId, &id64);
+ return static_cast(id64);
+ }
+
+ MtcTableIndex GetMtcDramIndex(DramId dramId) {
+ for (u32 i = 0; i < std::size(mtcIndexTable); ++i) {
+ if (mtcIndexTable[i].dramId == dramId) {
+ return mtcIndexTable[i].index;
+ }
+ }
+
+ return MtcTableIndex_Invalid;
+ }
+
+ NORETURN void AbortInvalidDramId() {
+ // Log("Invalid dram id\n");
+ // ViewLog();
+ panic::SmcError(panic::Emc);
+ CRASH("Invalid dram id\n");
+ }
+
+ u32 GetMtcOffset(MtcTableIndex index) {
+ if (index < T210b0SdevEmcDvfsTableS4gb03) {
+ return index * mariko::MtcFullTableSize;
+ }
+
+ /* There are 2 erista mtc tables between T210b0SdevEmcDvfsTableS4gb01 and T210b0SdevEmcDvfsTableS4gb03, so we have to do this adjustment. */
+ return mariko::MtcFullTableSize * index + (2 * erista::MtcFullTableSize);
+ }
+
+ void PrepareMtcMemoryRegion(u8 *firstTable, MarikoMtcTable *usedTable) {
+ memmove(firstTable, usedTable, mariko::MtcFullTableSize);
+
+ /* Clear all other tables */
+ /* 1 erista table is excluded because it's always before firstTable. */
+ /* We also exclude the used table obviously. */
+ constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * 16 + (erista::MtcFullTableSize * 2);
+ memset(firstTable + mariko::MtcFullTableSize, 0, RemainingRegionSize);
+ }
+
+ std::vector newEmcList;
+ void MtcGenerateFreqTables() {
+ constexpr u32 OverwrittenEristaTables = erista::MtcFullTableCount - 1; /* We don't overwrite the first table */
+ constexpr size_t MaxFreqSize = (OverwrittenEristaTables * erista::MtcTableCountDefault) + (mariko::MtcFullTableCount * mariko::MtcTableCountDefault);
+ constexpr u32 StepHz = 100000;
+ constexpr u32 RoundHz = 1000;
+
+ newEmcList.clear();
+ newEmcList.reserve(MaxFreqSize);
+ newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault));
+
+ if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
+ return;
+ }
+
+ for (u32 stepIndex = 1;; ++stepIndex) {
+ u32 newFreq = EmcClkOSLimit + (stepIndex * StepHz + 1) / 3;
+ newFreq = (newFreq / RoundHz) * RoundHz;
+
+ if (newFreq > C.marikoEmcMaxClock) {
+ break;
+ }
+
+ newEmcList.push_back(newFreq);
+ }
+
+ newEmcList.resize(std::min(newEmcList.size(), MaxFreqSize));
+ }
+
+ void MtcExtendTables(MarikoMtcTable *table) {
+ for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) {
+ std::memcpy(&table[i], &table[i - 1], sizeof(MarikoMtcTable));
+ table[i].rate_khz = newEmcList[i];
+ }
+ }
+
+ bool patchedMtc = false;
+ Result MemFreqMtcTable(u32 *ptr) {
+ if (C.marikoEmcMaxClock <= EmcClkOSLimit || patchedMtc) {
R_SKIP();
+ }
- MarikoMtcTable *table_alt = table_list[1], *table_max = table_list[0];
- MarikoMtcTable *tmp = new MarikoMtcTable;
+ static const DramId dramId = [] {
+ DramId id = GetDramId();
+ id = IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL;
+ //Log("Dram id: %u\n", id);
+ return id;
+ }();
- // Copy unmodified 1600000 table to tmp
- std::memcpy(reinterpret_cast(tmp), reinterpret_cast(table_max), sizeof(MarikoMtcTable));
+ static const MtcTableIndex mtcIndex = [] {
+ MtcTableIndex idx = GetMtcDramIndex(dramId);
+ /* If for some reason this happens, there is chance of recovering this. */
+ if (idx == MtcTableIndex_Invalid) {
+ AbortInvalidDramId();
+ }
+ return idx;
+ }();
- /* Adjust timings properly according to the new frequency. */
- MemMtcTableAutoAdjust(table_max);
+ /* Offset to dram id specific mtc table. */
+ static const u32 mtcOffset = GetMtcOffset(mtcIndex);
- MemMtcPllmbDivisor(table_max);
- // Overwrite 13312000 table with unmodified 1600000 table copied back
- std::memcpy(reinterpret_cast(table_alt), reinterpret_cast(tmp), sizeof(MarikoMtcTable));
+ /* Offset from 1600MHz pointer to 204Mhz table start. */
+ constexpr u32 StartAdjustment = offsetof(MarikoMtcTable, rate_khz) + sizeof(MarikoMtcTable) * 2;
+ u8 *startPtr = reinterpret_cast(ptr) - StartAdjustment;
+ MarikoMtcTable *table = reinterpret_cast(startPtr + mtcOffset);
+ R_UNLESS(R_SUCCEEDED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault)), ldr::ResultInvalidMtcTable());
- delete tmp;
+ table = reinterpret_cast(startPtr);
- PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
+ PrepareMtcMemoryRegion(startPtr, table);
+ if (R_FAILED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault))) {
+ panic::SmcError(panic::Emc);
+ }
+
+ MtcExtendTables(table);
+ for (u32 i = 0; i < newEmcList.size(); ++i) {
+ Log("freqList[%u] = %u\n", i, newEmcList[i]);
+ }
+
+ if (R_FAILED(MtcValidateAllTables(table, newEmcList.data(), newEmcList.size()))) {
+ panic::SmcError(panic::Emc);
+ }
+
+ for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) {
+ MemMtcTableAutoAdjust(&table[i]);
+ MemMtcPllmbDivisor(&table[i]);
+ }
+
+ patchedMtc = true;
R_SUCCEED();
}
Result MemFreqDvbTable(u32 *ptr) {
emc_dvb_dvfs_table_t *default_end = reinterpret_cast(ptr);
- emc_dvb_dvfs_table_t *new_start = default_end + 1;
+ emc_dvb_dvfs_table_t *new_start = default_end + 1;
// Validate existing table
void *mem_dvb_table_head = reinterpret_cast(new_start) - sizeof(EmcDvbTableDefault);
- bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0;
+ bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0;
R_UNLESS(validated, ldr::ResultInvalidDvbTable());
- if (C.marikoEmcMaxClock <= EmcClkOSLimit)
+ if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
R_SKIP();
+ }
int32_t voltAdd = 25 * C.emcDvbShift;
@@ -670,8 +789,9 @@ namespace ams::ldr::hoc::pcv::mariko {
}
Result MemFreqMax(u32 *ptr) {
- if (C.marikoEmcMaxClock <= EmcClkOSLimit)
+ if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
R_SKIP();
+ }
PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
R_SUCCEED();
@@ -685,12 +805,13 @@ namespace ams::ldr::hoc::pcv::mariko {
I2cSession _session;
Result res = i2cOpenSession(&_session, dev);
- if (R_FAILED(res))
+ if (R_FAILED(res)) {
return res;
+ }
cmd.reg = reg;
cmd.val = val;
- res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
+ res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
i2csessionClose(&_session);
return res;
}
@@ -702,21 +823,24 @@ namespace ams::ldr::hoc::pcv::mariko {
constexpr u32 uv_min = 250'000;
auto validator = [entry]() {
- R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
- R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
+ R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
+ R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
- R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
+ R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
R_SUCCEED();
};
R_TRY(validator());
u32 emc_uv = C.marikoEmcVddqVolt;
- if (!emc_uv)
- R_SKIP();
- if (emc_uv % uv_step)
+ if (!emc_uv) {
+ R_SKIP();
+ }
+
+ if (emc_uv % uv_step) {
emc_uv = (emc_uv + uv_step - 1) / uv_step * uv_step; // rounding
+ }
PATCH_OFFSET(ptr, emc_uv);
@@ -731,28 +855,53 @@ namespace ams::ldr::hoc::pcv::mariko {
return resultI2C;
}
+ Result MemMtcTableAsm(u32 *ptr) {
+ u32 adrp = *(ptr - 1);
+ R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTable());
+
+ /* We don't check for matching register because both registers must be x0 in order to pass the previous checks. */
+ /* The correct instructions will always be x0 since the mtcTable pointer is returned. */
+
+ /* Pray this does not break. */
+ u32 br = *(ptr - 12);
+ R_UNLESS(AsmCompareBrNoRd(br, MtcBrAsm), ldr::ResultInvalidMtcTable());
+
+ /* Pray this does not break either. */
+ u32 mov = *(ptr - 10);
+ R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTable());
+
+ u8 movRd = asm_get_rd(mov);
+ u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), movRd);
+
+ PATCH_OFFSET(ptr - 12, NopIns);
+ PATCH_OFFSET(ptr - 10, movCountPatch);
+ R_SUCCEED();
+ }
+
void Patch(uintptr_t mapped_nso, size_t nso_size) {
+ MtcGenerateFreqTables();
u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
PatcherEntry patches[] = {
- {"CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit},
- {"CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq},
- {"CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial},
- {"CPU Volt Thermals", &CpuVoltThermals, 1, 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},
- {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit},
- {"MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit},
- {"MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit},
- {"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit},
- {"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit},
- {"MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault},
- {"MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default},
+ { "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit },
+ { "CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq },
+ { "CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial },
+ { "CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial },
+ { "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low },
+ { "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 },
+ { "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
+ { "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit },
+ { "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit },
+ { "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
+ { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
+ { "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault },
+ { "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default },
+ { "Mem Table Asm", &MemMtcTableAsm, 0, &MemMtcGetGetTablePatternFn },
};
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) {
@@ -774,6 +923,7 @@ namespace ams::ldr::hoc::pcv::mariko {
CRASH(entry.description);
}
}
+ // ViewLog();
}
}
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
index 4c57bf1c..2d61861d 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
@@ -52,6 +52,8 @@ namespace ams::ldr::hoc::pcv::mariko {
constexpr u32 CpuVoltOfficial = 1120;
constexpr u32 CpuVminOfficial = 620;
+ constexpr u32 CpuTune0Low = 0xFFCF;
+
static const u32 cpuVoltagePatchValues[] = { 850, 38, 1120, 1000, 100, 1000, 0 };
static const s32 cpuVoltagePatchOffsets[] = { -2, -1, 5, 6, 7, 8, 9 };
static_assert(sizeof(cpuVoltagePatchValues) == sizeof(cpuVoltagePatchOffsets), "Invalid cpuVoltagePatch size");
@@ -117,12 +119,161 @@ namespace ams::ldr::hoc::pcv::mariko {
{ 1600000, { 675, 650, 637, } },
};
+ constexpr u32 EmcListDefault[] = { 204000, 1331200, 1600000, };
+ constexpr u32 EmcListSizeDefault = std::size(EmcListDefault);
+ constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1;
+ constexpr u32 EmcRateStep = 33'000;
+ constexpr u32 EmcRateStepScale = 33'200;
+
constexpr u32 EmcClkOSAlt = 1331'200;
constexpr u32 EmcClkPllmLimit = 2133'000'000;
constexpr u32 EmcVddqDefault = 600'000;
constexpr u32 MemVdd2Default = 1100'000;
- constexpr u32 MTC_TABLE_REV = 3;
+ constexpr u32 MTC_TABLE_REV = 3;
+ constexpr u32 MtcTableCountDefault = 3;
+
+ constexpr size_t MtcFullTableSize = sizeof(MarikoMtcTable) * MtcTableCountDefault;
+ constexpr u32 MtcFullTableCount = 17;
+
+ /* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
+ enum DramId : u64 {
+ HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 3,
+ AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 5,
+ IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 6,
+
+ IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 8,
+ IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 9,
+ IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 10,
+ IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE = 11,
+
+ HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 12,
+ HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 13,
+ HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 14,
+ HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE = 15,
+
+ IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 17,
+ IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 18,
+ HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 19,
+
+ IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 20,
+ HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 21,
+ AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 22,
+
+ HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 23,
+ AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 24,
+
+ IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 25,
+ HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF = 26,
+ AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 27,
+
+ AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 28,
+
+ IOWA_4GB_HYNIX_H54G46CYRBX267 = 29,
+ HOAG_4GB_HYNIX_H54G46CYRBX267 = 30,
+ AULA_4GB_HYNIX_H54G46CYRBX267 = 31,
+
+ IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 32,
+ HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB = 33,
+ AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 34,
+ };
+
+ enum MtcTableIndex {
+ T210b0SdevEmcDvfsTableS4gb01 = 0, /* Samsung */
+ T210b0SdevEmcDvfsTableS4gb03 = 1, /* Samsung AM-MGCJ 4Gb */ // guessed
+ T210b0SdevEmcDvfsTableS8gb03 = 2, /* Samsung AM-MGCJ 8Gb */ // Guesed
+ T210b0SdevEmcDvfsTableH4gb03 = 3, /* Hynix */
+ T210b0SdevEmcDvfsTableM4gb03 = 4, /* Micron */
+ T210b0SdevEmcDvfsTableS4gbY01 = 5, /* Samsung */
+ T210b0SdevEmcDvfsTableS1y4gbY01 = 6, /* Samsung */
+ T210b0SdevEmcDvfsTableS1y8gbY01 = 7, /* Samsung */
+ T210b0SdevEmcDvfsTableS1y4gbX03 = 8, /* Samsung AA-MGCL 4GB */
+ T210b0SdevEmcDvfsTableS1y8gbX03 = 9, /* Samsung AA-MGCL 8GB */
+ T210b0SdevEmcDvfsTableS1y4gb01 = 10, /* Samsung */
+ T210b0SdevEmcDvfsTableM1y4gb01 = 11, /* Micron */
+ T210b0SdevEmcDvfsTableH1y4gb01 = 12, /* Hynix */
+ T210b0SdevEmcDvfsTableS1y8gb04 = 13, /* Samsung */
+ T210b0SdevEmcDvfsTableS1z4gb01 = 14, /* Samsung */
+ T210b0SdevEmcDvfsTableH1a4gb01 = 15, /* Hynix */
+ T210b0SdevEmcDvfsTableM1a4gb01 = 16, /* Micron */
+ MtcTableIndex_Invalid = 17,
+ };
+
+ struct MtcDramIndex {
+ DramId dramId;
+ MtcTableIndex index;
+ };
+
+ constexpr MtcDramIndex mtcIndexTable[] = {
+ { HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), },
+ { AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), },
+ { IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), },
+ { IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, static_cast(0), },
+ { IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, static_cast(0), },
+ { IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME, static_cast(0), },
+ { IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE, static_cast(0), },
+ { HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, static_cast(0), },
+ { HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, static_cast(0), },
+ { HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME, static_cast(0), },
+ { HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE, static_cast(0), },
+ { IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
+ { IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), },
+ { HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL, static_cast(0), },
+ { IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), },
+ { HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), },
+ { AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), },
+ { HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), },
+ { AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, static_cast(0), },
+ { IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), },
+ { HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), },
+ { AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), },
+ { AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), },
+ { IOWA_4GB_HYNIX_H54G46CYRBX267, static_cast(0), },
+ { HOAG_4GB_HYNIX_H54G46CYRBX267, static_cast(0), },
+ { AULA_4GB_HYNIX_H54G46CYRBX267, static_cast(0), },
+ { IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), },
+ { HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), },
+ { AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), },
+ };
+
+ /*
+ 710006abfc 40 01 1f d6 br x10
+ */
+
+ /*
+ 710006ac28 a0 03 00 90 adrp x0,0x71000de000
+ 710006ac2c 00 80 16 91 add x0=>SdevEmcDvfsTableS4gb01,x0,#0x5a0
+ */
+
+ /* Br */
+ /*
+ | Z | OP | Fixed | A | M | RN | RM
+ 31 30 29 28 27 26 25 | 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 |11 | 10 | 9 8 7 6 5 | 4 3 2 1 0
+ 1 1 0 1 0 1 1 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 Rn 0 0 0 0 0
+ Z op A M Rm
+ */
+
+ /* Adrp */
+ /*
+ OP | ImmLow | ImmHigh | RD
+ 31 | 30 29 28 27 26 25 24 | 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 | 4 3 2 1 0
+ */
+
+ /* ADD (immediate) */
+ /*
+ SF | OP | S | Fixed value | Sh | Imm12 | RN | RD
+ 31 | 30 | 29 | 28 27 26 25 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0
+ */
+
+ constexpr u32 MtcBrAsm = 0xD61F0140;
+ constexpr u32 MtcMovAsm = 0x52800068;
+ constexpr u32 MtcAdrpAsm = 0x900003A0;
+ constexpr u32 MtcAddAsm = 0x91168000;
+
+ ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) {
+ /* This builds an address that gets returned, so the register must be x0 by convention. */
+ return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
+ }
void Patch(uintptr_t mapped_nso, size_t nso_size);
From 119f49a3a49ea325b6a62529623823e0f99a2bc8 Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Fri, 17 Apr 2026 16:36:12 +0200
Subject: [PATCH 02/12] fix freq list generation and dvb
---
.../loader/source/oc/customize.cpp | 9 +-
.../loader/source/oc/customize.hpp | 8 ++
.../loader/source/oc/pcv/pcv_common.hpp | 5 -
.../loader/source/oc/pcv/pcv_mariko.cpp | 107 +++++++++++-------
.../loader/source/oc/pcv/pcv_mariko.hpp | 7 +-
5 files changed, 86 insertions(+), 50 deletions(-)
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
index 9f85de34..a22c0c70 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
@@ -31,6 +31,7 @@
namespace ams::ldr::hoc {
volatile CustomizeTable C = {
+
/* Disables RAM powerdown */
.hpMode = DISABLED,
@@ -39,8 +40,12 @@ volatile CustomizeTable C = {
.eristaEmcMaxClock1 = 1600000,
.eristaEmcMaxClock2 = 1600000,
-.marikoEmcMaxClock = 1633000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
-.marikoEmcVddqVolt = 600000, /* Micron: 600mV, other manafacturers: 640mV */
+/* Available: 66MHz step rate, 100MHz step rate and jedec. */
+/* 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 */
+.marikoEmcVddqVolt = 600000,
.emcDvbShift = 0,
// Primary
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
index fbbd51d4..d500db15 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
@@ -36,6 +36,12 @@ enum TableConfig: u32 {
EXTREME_TABLE = 4,
};
+enum StepMode:u32 {
+ StepMode_66MHz = 0,
+ StepMode_100MHz = 1,
+ StepMode_Jedec = 2,
+};
+
/*
* Read:
* 2133RL = 40
@@ -82,6 +88,8 @@ typedef struct CustomizeTable {
u32 eristaEmcMaxClock;
u32 eristaEmcMaxClock1;
u32 eristaEmcMaxClock2;
+
+ StepMode stepMode;
u32 marikoEmcMaxClock;
u32 marikoEmcVddqVolt;
u32 emcDvbShift;
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp
index 2d75687a..cbd6a12f 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp
@@ -46,11 +46,6 @@ namespace ams::ldr::hoc::pcv {
unsigned long dvco_calibration_max;
} cvb_cpu_dfll_data;
- typedef struct emc_dvb_dvfs_table_t {
- u64 freq;
- s32 volt[4] = {0};
- } emc_dvb_dvfs_table_t;
-
typedef struct __attribute__((packed)) div_nmp {
u8 divn_shift;
u8 divn_width;
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 c55b545a..a6ee1268 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
@@ -500,11 +500,11 @@ namespace ams::ldr::hoc::pcv::mariko {
constexpr u32 Mask2 = 0xFFFFFF00;
constexpr u32 Mask3 = 0xFF00FF00;
- const u32 allowance1 = static_cast(0x32000 / (table->rate_khz / 0x3E8)) & 0xFF;
- const u32 allowance2 = static_cast(0x9C40 / (table->rate_khz / 0x3E8)) & 0xFF;
- const u32 allowance3 = static_cast(0xB540 / (table->rate_khz / 0x3E8)) & 0xFF;
- const u32 allowance4 = static_cast(0x9600 / (table->rate_khz / 0x3E8)) & 0xFF;
- const u32 allowance5 = static_cast(0x8980 / (table->rate_khz / 0x3E8)) & 0xFF;
+ const u32 allowance1 = static_cast(0x32000 / (table->rate_khz / 1000)) & 0xFF;
+ const u32 allowance2 = static_cast(0x9C40 / (table->rate_khz / 1000)) & 0xFF;
+ const u32 allowance3 = static_cast(0xB540 / (table->rate_khz / 1000)) & 0xFF;
+ const u32 allowance4 = static_cast(0x9600 / (table->rate_khz / 1000)) & 0xFF;
+ const u32 allowance5 = static_cast(0x8980 / (table->rate_khz / 1000)) & 0xFF;
table->la_scale_regs.mc_latency_allowance_xusb_0 = (table->la_scale_regs.mc_latency_allowance_xusb_0 & MaskHigh) | (allowance1 << 16);
table->la_scale_regs.mc_latency_allowance_xusb_1 = (table->la_scale_regs.mc_latency_allowance_xusb_1 & MaskHigh) | (allowance1 << 16);
@@ -645,19 +645,31 @@ namespace ams::ldr::hoc::pcv::mariko {
/* Clear all other tables */
/* 1 erista table is excluded because it's always before firstTable. */
/* We also exclude the used table obviously. */
- constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * 16 + (erista::MtcFullTableSize * 2);
+ constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * (mariko::MtcFullTableCount - 1) + (erista::MtcFullTableSize * (erista::MtcFullTableCount - 1));
memset(firstTable + mariko::MtcFullTableSize, 0, RemainingRegionSize);
}
std::vector newEmcList;
void MtcGenerateFreqTables() {
- constexpr u32 OverwrittenEristaTables = erista::MtcFullTableCount - 1; /* We don't overwrite the first table */
- constexpr size_t MaxFreqSize = (OverwrittenEristaTables * erista::MtcTableCountDefault) + (mariko::MtcFullTableCount * mariko::MtcTableCountDefault);
- constexpr u32 StepHz = 100000;
- constexpr u32 RoundHz = 1000;
+ constexpr u32 RoundHz = 1000;
+
+ u32 stepRate = 0;
+ switch (C.stepMode) {
+ case StepMode_66MHz:
+ stepRate = 66667;
+ break;
+ case StepMode_100MHz:
+ stepRate = 100000;
+ break;
+ case StepMode_Jedec:
+ break;
+ default:
+ stepRate = 66667;
+ break;
+ }
newEmcList.clear();
- newEmcList.reserve(MaxFreqSize);
+ newEmcList.reserve(DvfsTableEntryCount);
newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault));
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
@@ -665,17 +677,27 @@ namespace ams::ldr::hoc::pcv::mariko {
}
for (u32 stepIndex = 1;; ++stepIndex) {
- u32 newFreq = EmcClkOSLimit + (stepIndex * StepHz + 1) / 3;
+ u32 newFreq = EmcClkOSLimit + stepIndex * stepRate;
newFreq = (newFreq / RoundHz) * RoundHz;
-
if (newFreq > C.marikoEmcMaxClock) {
+ if (newEmcList.back() != C.marikoEmcMaxClock) {
+ newEmcList.push_back(static_cast(C.marikoEmcMaxClock));
+ }
break;
}
-
newEmcList.push_back(newFreq);
}
- newEmcList.resize(std::min(newEmcList.size(), MaxFreqSize));
+ 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));
}
void MtcExtendTables(MarikoMtcTable *table) {
@@ -700,7 +722,7 @@ namespace ams::ldr::hoc::pcv::mariko {
static const MtcTableIndex mtcIndex = [] {
MtcTableIndex idx = GetMtcDramIndex(dramId);
- /* If for some reason this happens, there is chance of recovering this. */
+ /* If for some reason this happens, there is no chance of recovering this. */
if (idx == MtcTableIndex_Invalid) {
AbortInvalidDramId();
}
@@ -742,8 +764,8 @@ namespace ams::ldr::hoc::pcv::mariko {
}
Result MemFreqDvbTable(u32 *ptr) {
- emc_dvb_dvfs_table_t *default_end = reinterpret_cast(ptr);
- emc_dvb_dvfs_table_t *new_start = default_end + 1;
+ DvbEntry *default_end = reinterpret_cast(ptr);
+ DvbEntry *new_start = default_end + 1;
// Validate existing table
void *mem_dvb_table_head = reinterpret_cast(new_start) - sizeof(EmcDvbTableDefault);
@@ -754,33 +776,34 @@ namespace ams::ldr::hoc::pcv::mariko {
R_SKIP();
}
- int32_t voltAdd = 25 * C.emcDvbShift;
-
+ s32 voltAdd = 25 * C.emcDvbShift;
#define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000),
+ DvbEntry emcDvbTableNew[] = {
+ { 204000, { 637, 637, 637, } },
+ { 1331200, { 650, 637, 637, } },
+ { 1600000, { 675, 650, 637, } },
+ { 1866000, { DVB_VOLT(700, 675, 650) } },
+ { 2133000, { DVB_VOLT(725, 700, 675) } },
+ { 2400000, { DVB_VOLT(750, 725, 700) } },
+ { 2666000, { DVB_VOLT(775, 750, 725) } },
+ { 2933000, { DVB_VOLT(800, 775, 750) } },
+ { 3200000, { DVB_VOLT(800, 800, 775) } },
+ { 0xFFFFFFFF, { } },
+ };
- /* TODO: More fine tuned values? */
- if (C.marikoEmcMaxClock < 1862400) {
- std::memcpy(new_start, default_end, sizeof(emc_dvb_dvfs_table_t));
- } else if (C.marikoEmcMaxClock < 2131200) {
- emc_dvb_dvfs_table_t oc_table = {1862400, {700, 675, 650, }};
- std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
- } else if (C.marikoEmcMaxClock < 2400000) {
- emc_dvb_dvfs_table_t oc_table = {2131200, { 725, 700, 675} };
- std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
- } else if (C.marikoEmcMaxClock < 2665600) {
- emc_dvb_dvfs_table_t oc_table = {2400000, {DVB_VOLT(750, 725, 700)}};
- std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
- } else if (C.marikoEmcMaxClock < 2931200) {
- emc_dvb_dvfs_table_t oc_table = {2665600, {DVB_VOLT(775, 750, 725)}};
- std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
- } else if (C.marikoEmcMaxClock < 3200000) {
- emc_dvb_dvfs_table_t oc_table = {2931200, {DVB_VOLT(800, 775, 750)}};
- std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
- } else {
- emc_dvb_dvfs_table_t oc_table = {3200000, {DVB_VOLT(800, 800, 775)}};
- std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
+ u32 j = MtcTableCountDefault;
+ for (u32 i = MtcTableCountDefault; i < newEmcList.size(); ++i) {
+ if (newEmcList[i] >= emcDvbTableNew[j].freq && newEmcList[i] < emcDvbTableNew[j + 1].freq) {
+ emcDvbTableNew[j].freq = newEmcList[i];
+ ++j;
+ } else {
+ break;
+ }
}
- new_start->freq = C.marikoEmcMaxClock;
+
+ std::memset(mem_dvb_table_head, 0, sizeof(EmcDvbTableDefault));
+ std::memcpy(mem_dvb_table_head, &emcDvbTableNew, sizeof(emcDvbTableNew));
+
/* Max dvfs entry is 32, but HOS doesn't seem to boot if exact freq doesn't exist in dvb table,
reason why it's like this
*/
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
index 2d61861d..8e4058fe 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
@@ -110,7 +110,12 @@ namespace ams::ldr::hoc::pcv::mariko {
return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]);
}
- constexpr emc_dvb_dvfs_table_t EmcDvbTableDefault[] = {
+ struct DvbEntry {
+ u64 freq;
+ s32 volt[4] = {};
+ };
+
+ constexpr DvbEntry EmcDvbTableDefault[] = {
{ 204000, { 637, 637, 637, } },
{ 408000, { 637, 637, 637, } },
{ 800000, { 637, 637, 637, } },
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 03/12] 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();
}
}
From c95b6fde88b088f9819caa6f40b579cf413f41b9 Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Fri, 17 Apr 2026 23:40:17 +0200
Subject: [PATCH 04/12] assign all mtc tables
---
.../stratosphere/loader/source/oc/pcv/pcv.cpp | 2 +-
.../loader/source/oc/pcv/pcv_mariko.cpp | 8 --
.../loader/source/oc/pcv/pcv_mariko.hpp | 90 +++++++++----------
3 files changed, 46 insertions(+), 54 deletions(-)
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
index c635b435..0b9ff6c8 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, 3700'000, false, panic::Emc },
+ { C.marikoEmcMaxClock, 1600'000, 3500'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 51217fe2..3bb05150 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
@@ -592,7 +592,6 @@ namespace ams::ldr::hoc::pcv::mariko {
}
Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) {
- // 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 +623,6 @@ namespace ams::ldr::hoc::pcv::mariko {
}
NORETURN void AbortInvalidDramId() {
- Log("Invalid dram id\n");
- ViewLog();
panic::SmcError(panic::Emc);
CRASH("Invalid dram id\n");
}
@@ -706,8 +703,6 @@ 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);
return id;
}();
@@ -737,9 +732,6 @@ namespace ams::ldr::hoc::pcv::mariko {
}
MtcExtendTables(table);
- for (u32 i = 0; i < newEmcList.size(); ++i) {
- // Log("freqList[%u] = %u\n", i, newEmcList[i]);
- }
if (R_FAILED(MtcValidateAllTables(table, newEmcList.data(), newEmcList.size()))) {
panic::SmcError(panic::Emc);
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
index 8e4058fe..033d5f42 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp
@@ -184,23 +184,23 @@ namespace ams::ldr::hoc::pcv::mariko {
};
enum MtcTableIndex {
- T210b0SdevEmcDvfsTableS4gb01 = 0, /* Samsung */
- T210b0SdevEmcDvfsTableS4gb03 = 1, /* Samsung AM-MGCJ 4Gb */ // guessed
- T210b0SdevEmcDvfsTableS8gb03 = 2, /* Samsung AM-MGCJ 8Gb */ // Guesed
- T210b0SdevEmcDvfsTableH4gb03 = 3, /* Hynix */
- T210b0SdevEmcDvfsTableM4gb03 = 4, /* Micron */
- T210b0SdevEmcDvfsTableS4gbY01 = 5, /* Samsung */
- T210b0SdevEmcDvfsTableS1y4gbY01 = 6, /* Samsung */
- T210b0SdevEmcDvfsTableS1y8gbY01 = 7, /* Samsung */
- T210b0SdevEmcDvfsTableS1y4gbX03 = 8, /* Samsung AA-MGCL 4GB */
- T210b0SdevEmcDvfsTableS1y8gbX03 = 9, /* Samsung AA-MGCL 8GB */
- T210b0SdevEmcDvfsTableS1y4gb01 = 10, /* Samsung */
- T210b0SdevEmcDvfsTableM1y4gb01 = 11, /* Micron */
- T210b0SdevEmcDvfsTableH1y4gb01 = 12, /* Hynix */
- T210b0SdevEmcDvfsTableS1y8gb04 = 13, /* Samsung */
- T210b0SdevEmcDvfsTableS1z4gb01 = 14, /* Samsung */
- T210b0SdevEmcDvfsTableH1a4gb01 = 15, /* Hynix */
- T210b0SdevEmcDvfsTableM1a4gb01 = 16, /* Micron */
+ T210b0SdevEmcDvfsTableS4gb01 = 0, /* (Unused) Samsung 4Gb */
+ T210b0SdevEmcDvfsTableS4gb03 = 1, /* Samsung AM-MGCJ 4Gb */
+ T210b0SdevEmcDvfsTableS8gb03 = 2, /* (Unused) Samsung 4Gb */
+ T210b0SdevEmcDvfsTableH4gb03 = 3, /* Hynix NME 4Gb */
+ T210b0SdevEmcDvfsTableM4gb03 = 4, /* Micron WT:F 4Gb */
+ T210b0SdevEmcDvfsTableS4gbY01 = 5, /* (Unused) Samsung 4Gb */
+ T210b0SdevEmcDvfsTableS1y4gbY01 = 6, /* (Unused) Samsung 4Gb */
+ T210b0SdevEmcDvfsTableS1y8gbY01 = 7, /* (Unused) Samsung 4Gb */
+ T210b0SdevEmcDvfsTableS1y4gbX03 = 8, /* Samsung AA-MGCL 4Gb */
+ T210b0SdevEmcDvfsTableS1y8gbX03 = 9, /* Samsung AA-MGCL 8Gb */
+ T210b0SdevEmcDvfsTableS1y4gb01 = 10, /* (Unused) Samsung 4Gb */
+ T210b0SdevEmcDvfsTableM1y4gb01 = 11, /* Micron WT:E 4Gb */
+ T210b0SdevEmcDvfsTableH1y4gb01 = 12, /* Hynix NEE 4Gb */
+ T210b0SdevEmcDvfsTableS1y8gb04 = 13, /* Samsung AM-MGCJ 8Gb */
+ T210b0SdevEmcDvfsTableS1z4gb01 = 14, /* Samsung AB-MGCL 4Gb */
+ T210b0SdevEmcDvfsTableH1a4gb01 = 15, /* Hynix x267 4Gb */
+ T210b0SdevEmcDvfsTableM1a4gb01 = 16, /* Micron WT:B 8Gb */
MtcTableIndex_Invalid = 17,
};
@@ -210,35 +210,35 @@ namespace ams::ldr::hoc::pcv::mariko {
};
constexpr MtcDramIndex mtcIndexTable[] = {
- { HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), },
- { AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), },
- { IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), },
- { IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, static_cast(0), },
- { IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, static_cast(0), },
- { IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME, static_cast(0), },
- { IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE, static_cast(0), },
- { HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, static_cast(0), },
- { HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, static_cast(0), },
- { HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME, static_cast(0), },
- { HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE, static_cast(0), },
+ { HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
+ { AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
+ { IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
+ { IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, T210b0SdevEmcDvfsTableS4gb03, },
+ { IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, T210b0SdevEmcDvfsTableS1y8gb04, },
+ { IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME, T210b0SdevEmcDvfsTableH4gb03, },
+ { IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE, T210b0SdevEmcDvfsTableM1y4gb01, },
+ { HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, T210b0SdevEmcDvfsTableS4gb03, },
+ { HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, T210b0SdevEmcDvfsTableS1y8gb04, },
+ { HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME, T210b0SdevEmcDvfsTableH4gb03, },
+ { HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE, T210b0SdevEmcDvfsTableM1y4gb01, },
{ IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
- { IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), },
- { HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL, static_cast(0), },
- { IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), },
- { HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), },
- { AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), },
- { HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), },
- { AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, static_cast(0), },
- { IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), },
- { HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), },
- { AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), },
- { AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), },
- { IOWA_4GB_HYNIX_H54G46CYRBX267, static_cast(0), },
- { HOAG_4GB_HYNIX_H54G46CYRBX267, static_cast(0), },
- { AULA_4GB_HYNIX_H54G46CYRBX267, static_cast(0), },
- { IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), },
- { HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), },
- { AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), },
+ { IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
+ { HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
+ { IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1z4gb01, },
+ { HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1y8gb04, },
+ { AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1y8gb04, },
+ { HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
+ { AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
+ { IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
+ { HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
+ { AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
+ { AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
+ { IOWA_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
+ { HOAG_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
+ { AULA_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
+ { IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
+ { HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
+ { AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
};
/*
From 638ddb499c09aa4a898537afbf2278f2d16b26a2 Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sat, 18 Apr 2026 11:52:06 +0200
Subject: [PATCH 05/12] add jedec mode
---
.../loader/source/oc/customize.cpp | 2 +-
.../loader/source/oc/pcv/pcv_mariko.cpp | 101 ++++++++++--------
2 files changed, 59 insertions(+), 44 deletions(-)
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
index 06298463..2ba074e2 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
@@ -42,7 +42,7 @@ volatile CustomizeTable C = {
/* Available: 66MHz step rate, 100MHz step rate and jedec. */
/* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */
-.stepMode = StepMode_66MHz,
+.stepMode = StepMode_Jedec,
.marikoEmcMaxClock = 2933000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
.marikoEmcVddqVolt = 600000,
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 3bb05150..fbb60197 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
@@ -591,6 +591,63 @@ namespace ams::ldr::hoc::pcv::mariko {
}
}
+ std::vector newEmcList;
+ void MtcGenerateJedecTable() {
+ const u32 jedecFreqs[] = { 1866000, 1996000, 2133000, 2400000, 2666000, 2933000, 3200000 };
+ constexpr u32 JedecFreqCount = std::size(jedecFreqs);
+
+ for (u32 i = 0; i < JedecFreqCount; ++i) {
+ if (jedecFreqs[i] <= C.marikoEmcMaxClock) {
+ newEmcList.push_back(jedecFreqs[i]);
+ } else {
+ break;
+ }
+ }
+
+ newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
+ }
+
+ void MtcGenerateFreqTables() {
+ if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
+ return;
+ }
+
+ newEmcList.clear();
+ newEmcList.reserve(DvfsTableEntryCount);
+ newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault));
+
+ u32 stepRate = 0;
+ switch (C.stepMode) {
+ case StepMode_66MHz:
+ stepRate = 66667;
+ break;
+ case StepMode_100MHz:
+ stepRate = 100000;
+ break;
+ case StepMode_Jedec:
+ MtcGenerateJedecTable();
+ return;
+ default:
+ stepRate = 66667;
+ break;
+ }
+
+ constexpr u32 RoundHz = 1000;
+ for (u32 stepIndex = 1;; ++stepIndex) {
+ u32 newFreq = EmcClkOSLimit + stepIndex * stepRate;
+ newFreq = (newFreq / RoundHz) * RoundHz;
+ if (newFreq > C.marikoEmcMaxClock) {
+ if (newEmcList.back() != C.marikoEmcMaxClock) {
+ newEmcList.push_back(static_cast(C.marikoEmcMaxClock));
+ }
+ break;
+ }
+ newEmcList.push_back(newFreq);
+ }
+
+ newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
+ }
+
Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) {
R_UNLESS(tableStart->rate_khz == expectedFreq, ldr::ResultInvalidMtcTable());
R_UNLESS(tableStart->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
@@ -639,55 +696,13 @@ namespace ams::ldr::hoc::pcv::mariko {
void PrepareMtcMemoryRegion(u8 *firstTable, MarikoMtcTable *usedTable) {
memmove(firstTable, usedTable, mariko::MtcFullTableSize);
- /* Clear all other tables */
+ /* Clear all other tables. */
/* 1 erista table is excluded because it's always before firstTable. */
/* We also exclude the used table obviously. */
constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * (mariko::MtcFullTableCount - 1) + (erista::MtcFullTableSize * (erista::MtcFullTableCount - 1));
memset(firstTable + mariko::MtcFullTableSize, 0, RemainingRegionSize);
}
- std::vector newEmcList;
- void MtcGenerateFreqTables() {
- constexpr u32 RoundHz = 1000;
-
- u32 stepRate = 0;
- switch (C.stepMode) {
- case StepMode_66MHz:
- stepRate = 66667;
- break;
- case StepMode_100MHz:
- stepRate = 100000;
- break;
- case StepMode_Jedec:
- break;
- default:
- stepRate = 66667;
- break;
- }
-
- newEmcList.clear();
- newEmcList.reserve(DvfsTableEntryCount);
- newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault));
-
- if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
- return;
- }
-
- for (u32 stepIndex = 1;; ++stepIndex) {
- u32 newFreq = EmcClkOSLimit + stepIndex * stepRate;
- newFreq = (newFreq / RoundHz) * RoundHz;
- if (newFreq > C.marikoEmcMaxClock) {
- if (newEmcList.back() != C.marikoEmcMaxClock) {
- newEmcList.push_back(static_cast(C.marikoEmcMaxClock));
- }
- break;
- }
- newEmcList.push_back(newFreq);
- }
-
- newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
- }
-
void MtcExtendTables(MarikoMtcTable *table) {
for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) {
std::memcpy(&table[i], &table[i - 1], sizeof(MarikoMtcTable));
From aa95f526b877a1477943d2f842aa0c40965ed556 Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sat, 18 Apr 2026 12:19:55 +0200
Subject: [PATCH 06/12] add mrf latency stuff to configs
---
Source/hoc-clk/common/include/hocclk/config.h | 35 +++++++++++++++++++
Source/hoc-clk/sysmodule/src/kip.cpp | 28 +++++++++++++--
Source/hoc-clk/sysmodule/src/kip.hpp | 31 +++++++++++++++-
3 files changed, 91 insertions(+), 3 deletions(-)
diff --git a/Source/hoc-clk/common/include/hocclk/config.h b/Source/hoc-clk/common/include/hocclk/config.h
index 8b3700d6..5b8020f1 100644
--- a/Source/hoc-clk/common/include/hocclk/config.h
+++ b/Source/hoc-clk/common/include/hocclk/config.h
@@ -79,6 +79,8 @@ typedef enum {
KipConfigValue_eristaEmcMaxClock,
KipConfigValue_eristaEmcMaxClock1,
KipConfigValue_eristaEmcMaxClock2,
+
+ KipConfigValue_stepMode,
KipConfigValue_marikoEmcMaxClock,
KipConfigValue_marikoEmcVddqVolt,
KipConfigValue_emcDvbShift,
@@ -91,6 +93,17 @@ typedef enum {
KipConfigValue_t6_tRTW,
KipConfigValue_t7_tWTR,
KipConfigValue_t8_tREFI,
+
+ KipConfigValue_read_latency_1333,
+ KipConfigValue_read_latency_1600,
+ KipConfigValue_read_latency_1866,
+ KipConfigValue_read_latency_2133,
+
+ KipConfigValue_write_latency_1333,
+ KipConfigValue_write_latency_1600,
+ KipConfigValue_write_latency_1866,
+ KipConfigValue_write_latency_2133,
+
KipConfigValue_mem_burst_read_latency,
KipConfigValue_mem_burst_write_latency,
@@ -283,6 +296,8 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
return pretty ? "Erista EMC Max Clock 2" : "erista_emc_max_clock1";
case KipConfigValue_eristaEmcMaxClock2:
return pretty ? "Erista EMC Max Clock 3" : "erista_emc_max_clock2";
+ case KipConfigValue_stepMode:
+ return pretty ? "Step Mode:" : "step_mode";
case KipConfigValue_marikoEmcMaxClock:
return pretty ? "Mariko EMC Max Clock" : "mariko_emc_max_clock";
case KipConfigValue_marikoEmcVddqVolt:
@@ -307,6 +322,25 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
return pretty ? "t7 - tWTR" : "t7_twtr";
case KipConfigValue_t8_tREFI:
return pretty ? "t8 - tREFI" : "t8_trefi";
+
+ case KipConfigValue_read_latency_1333:
+ return pretty ? "1333 Read Latency" : "read_latency_1333";
+ case KipConfigValue_read_latency_1600:
+ return pretty ? "1600 Read Latency" : "read_latency_1600";
+ case KipConfigValue_read_latency_1866:
+ return pretty ? "1866 Read Latency" : "read_latency_1866";
+ case KipConfigValue_read_latency_2133:
+ return pretty ? "2133 Read Latency" : "read_latency_2133";
+
+ case KipConfigValue_write_latency_1333:
+ return pretty ? "1333 Write Latency" : "write_latency_1333";
+ case KipConfigValue_write_latency_1600:
+ return pretty ? "1600 Write Latency" : "write_latency_1600";
+ case KipConfigValue_write_latency_1866:
+ return pretty ? "1866 Write Latency" : "write_latency_1866";
+ case KipConfigValue_write_latency_2133:
+ return pretty ? "2133 Write Latency" : "write_latency_2133";
+
case KipConfigValue_mem_burst_read_latency:
return pretty ? "Memory Burst Read Latency" : "mem_burst_read_latency";
case KipConfigValue_mem_burst_write_latency:
@@ -512,6 +546,7 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case KipConfigValue_eristaEmcMaxClock:
case KipConfigValue_eristaEmcMaxClock1:
case KipConfigValue_eristaEmcMaxClock2:
+ case KipConfigValue_stepMode:
case KipConfigValue_marikoEmcMaxClock:
case KipConfigValue_marikoEmcVddqVolt:
case KipConfigValue_emcDvbShift:
diff --git a/Source/hoc-clk/sysmodule/src/kip.cpp b/Source/hoc-clk/sysmodule/src/kip.cpp
index b1faabc2..c11c06c6 100644
--- a/Source/hoc-clk/sysmodule/src/kip.cpp
+++ b/Source/hoc-clk/sysmodule/src/kip.cpp
@@ -21,7 +21,7 @@
namespace kip {
bool kipAvailable = false;
-
+
void SetKipData()
{
// TODO: figure out if this REALLY causes issues (i doubt it)
@@ -70,6 +70,18 @@ namespace kip {
CUST_WRITE_FIELD_BATCH(&table, t6_tRTW, config::GetConfigValue(KipConfigValue_t6_tRTW));
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR, config::GetConfigValue(KipConfigValue_t7_tWTR));
CUST_WRITE_FIELD_BATCH(&table, t8_tREFI, config::GetConfigValue(KipConfigValue_t8_tREFI));
+ CUST_WRITE_FIELD_BATCH(&table, stepMode, config::GetConfigValue(KipConfigValue_stepMode));
+
+ CUST_WRITE_FIELD_BATCH(&table, readLatency1333, config::GetConfigValue(KipConfigValue_read_latency_1333));
+ CUST_WRITE_FIELD_BATCH(&table, readLatency1600, config::GetConfigValue(KipConfigValue_read_latency_1600));
+ CUST_WRITE_FIELD_BATCH(&table, readLatency1866, config::GetConfigValue(KipConfigValue_read_latency_1866));
+ CUST_WRITE_FIELD_BATCH(&table, readLatency2133, config::GetConfigValue(KipConfigValue_read_latency_2133));
+
+ CUST_WRITE_FIELD_BATCH(&table, writeLatency1333, config::GetConfigValue(KipConfigValue_write_latency_1333));
+ CUST_WRITE_FIELD_BATCH(&table, writeLatency1600, config::GetConfigValue(KipConfigValue_write_latency_1600));
+ CUST_WRITE_FIELD_BATCH(&table, writeLatency1866, config::GetConfigValue(KipConfigValue_write_latency_1866));
+ CUST_WRITE_FIELD_BATCH(&table, writeLatency2133, config::GetConfigValue(KipConfigValue_write_latency_2133));
+
CUST_WRITE_FIELD_BATCH(&table, mem_burst_read_latency, config::GetConfigValue(KipConfigValue_mem_burst_read_latency));
CUST_WRITE_FIELD_BATCH(&table, mem_burst_write_latency, config::GetConfigValue(KipConfigValue_mem_burst_write_latency));
CUST_WRITE_FIELD_BATCH(&table, eristaCpuUV, config::GetConfigValue(KipConfigValue_eristaCpuUV));
@@ -166,7 +178,7 @@ namespace kip {
configValues.values[HocClkConfigValue_IsFirstLoad] = (u64)false;
notification::writeNotification("Horizon OC has been installed");
}
-
+
configValues.values[KipCrc32] = (u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum
// configValues.values[KipConfigValue_mtcConf] = cust_get_mtc_conf(&table);
configValues.values[KipConfigValue_hpMode] = cust_get_hp_mode(&table);
@@ -187,6 +199,18 @@ namespace kip {
configValues.values[KipConfigValue_t6_tRTW] = cust_get_tRTW(&table);
configValues.values[KipConfigValue_t7_tWTR] = cust_get_tWTR(&table);
configValues.values[KipConfigValue_t8_tREFI] = cust_get_tREFI(&table);
+ configValues.values[KipConfigValue_stepMode] = cust_get_step_mode(&table);
+
+ configValues.values[KipConfigValue_read_latency_1333] = cust_get_read_latency_1333(&table);
+ configValues.values[KipConfigValue_read_latency_1600] = cust_get_read_latency_1600(&table);
+ configValues.values[KipConfigValue_read_latency_1866] = cust_get_read_latency_1866(&table);
+ configValues.values[KipConfigValue_read_latency_2133] = cust_get_read_latency_2133(&table);
+
+ configValues.values[KipConfigValue_write_latency_1333] = cust_get_write_latency_1333(&table);
+ configValues.values[KipConfigValue_write_latency_1600] = cust_get_write_latency_1600(&table);
+ configValues.values[KipConfigValue_write_latency_1866] = cust_get_write_latency_1866(&table);
+ configValues.values[KipConfigValue_write_latency_2133] = cust_get_write_latency_2133(&table);
+
configValues.values[KipConfigValue_mem_burst_read_latency] = cust_get_burst_read_lat(&table);
configValues.values[KipConfigValue_mem_burst_write_latency] = cust_get_burst_write_lat(&table);
diff --git a/Source/hoc-clk/sysmodule/src/kip.hpp b/Source/hoc-clk/sysmodule/src/kip.hpp
index 26729fdc..8cb27f17 100644
--- a/Source/hoc-clk/sysmodule/src/kip.hpp
+++ b/Source/hoc-clk/sysmodule/src/kip.hpp
@@ -30,7 +30,7 @@
namespace kip {
extern bool kipAvailable;
-
+
typedef struct {
u8 cust[4];
u32 custRev;
@@ -40,6 +40,7 @@ namespace kip {
u32 eristaEmcMaxClock;
u32 eristaEmcMaxClock1;
u32 eristaEmcMaxClock2;
+ u32 stepMode;
u32 marikoEmcMaxClock;
u32 marikoEmcVddqVolt;
u32 emcDvbShift;
@@ -53,6 +54,10 @@ namespace kip {
u32 t7_tWTR;
u32 t8_tREFI;
+ /* These latencies are arrays in loader, but it's easier to handle it this way in the configurator. */
+ u32 readLatency1333, readLatency1600, readLatency1866, readLatency2133;
+ u32 writeLatency1333, writeLatency1600, writeLatency1866, writeLatency2133;
+
u32 mem_burst_read_latency;
u32 mem_burst_write_latency;
@@ -201,6 +206,7 @@ namespace kip {
static inline bool cust_set_erista_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock, v); }
static inline bool cust_set_erista_emc_max1(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock1, v); }
static inline bool cust_set_erista_emc_max2(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock2, v); }
+ static inline bool cust_set_step_mode(const char* p, u32 v) { CUST_WRITE_FIELD(p, stepMode, v); }
static inline bool cust_set_mariko_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcMaxClock, v); }
static inline bool cust_set_mariko_emc_vddq(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcVddqVolt, v); }
static inline bool cust_set_emc_dvb_shift(const char* p, u32 v) { CUST_WRITE_FIELD(p, emcDvbShift, v); }
@@ -215,6 +221,17 @@ namespace kip {
static inline bool cust_set_tREFI(const char* p, u32 v) { CUST_WRITE_FIELD(p, t8_tREFI, v); }
static inline bool cust_set_tRTW_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW_fine_tune, v); }
static inline bool cust_set_tWTR_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR_fine_tune, v); }
+
+ static inline bool cust_set_read_latency_1333(const char* p, u32 v) { CUST_WRITE_FIELD(p, readLatency1333, v); }
+ static inline bool cust_set_read_latency_1600(const char* p, u32 v) { CUST_WRITE_FIELD(p, readLatency1600, v); }
+ static inline bool cust_set_read_latency_1866(const char* p, u32 v) { CUST_WRITE_FIELD(p, readLatency1866, v); }
+ static inline bool cust_set_read_latency_2133(const char* p, u32 v) { CUST_WRITE_FIELD(p, readLatency2133, v); }
+
+ static inline bool cust_set_write_latency_1333(const char* p, u32 v) { CUST_WRITE_FIELD(p, writeLatency1333, v); }
+ static inline bool cust_set_write_latency_1600(const char* p, u32 v) { CUST_WRITE_FIELD(p, writeLatency1600, v); }
+ static inline bool cust_set_write_latency_1866(const char* p, u32 v) { CUST_WRITE_FIELD(p, writeLatency1866, v); }
+ static inline bool cust_set_write_latency_2133(const char* p, u32 v) { CUST_WRITE_FIELD(p, writeLatency2133, v); }
+
static inline bool cust_set_burst_read_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_read_latency, v); }
static inline bool cust_set_burst_write_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_write_latency, v); }
@@ -272,6 +289,7 @@ namespace kip {
static inline u32 cust_get_erista_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock); }
static inline u32 cust_get_erista_emc_max1(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock1); }
static inline u32 cust_get_erista_emc_max2(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock2); }
+ static inline u32 cust_get_step_mode(const CustomizeTable* t) { return CUST_GET_FIELD(t, stepMode); }
static inline u32 cust_get_mariko_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcMaxClock); }
static inline u32 cust_get_mariko_emc_vddq(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcVddqVolt); }
static inline u32 cust_get_emc_dvb_shift(const CustomizeTable* t) { return CUST_GET_FIELD(t, emcDvbShift); }
@@ -286,6 +304,17 @@ namespace kip {
static inline u32 cust_get_tREFI(const CustomizeTable* t) { return CUST_GET_FIELD(t, t8_tREFI); }
static inline u32 cust_get_tRTW_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW_fine_tune); }
static inline u32 cust_get_tWTR_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR_fine_tune); }
+
+ static inline u32 cust_get_read_latency_1333(const CustomizeTable* t) { return CUST_GET_FIELD(t, readLatency1333); }
+ static inline u32 cust_get_read_latency_1600(const CustomizeTable* t) { return CUST_GET_FIELD(t, readLatency1600); }
+ static inline u32 cust_get_read_latency_1866(const CustomizeTable* t) { return CUST_GET_FIELD(t, readLatency1866); }
+ static inline u32 cust_get_read_latency_2133(const CustomizeTable* t) { return CUST_GET_FIELD(t, readLatency2133); }
+
+ static inline u32 cust_get_write_latency_1333(const CustomizeTable* t) { return CUST_GET_FIELD(t, writeLatency1333); }
+ static inline u32 cust_get_write_latency_1600(const CustomizeTable* t) { return CUST_GET_FIELD(t, writeLatency1600); }
+ static inline u32 cust_get_write_latency_1866(const CustomizeTable* t) { return CUST_GET_FIELD(t, writeLatency1866); }
+ static inline u32 cust_get_write_latency_2133(const CustomizeTable* t) { return CUST_GET_FIELD(t, writeLatency2133); }
+
static inline u32 cust_get_burst_read_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_read_latency); }
static inline u32 cust_get_burst_write_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_write_latency); }
From 6c8d429c643826cb98e6f70328fda87e5bae98aa Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sat, 18 Apr 2026 12:21:42 +0200
Subject: [PATCH 07/12] add missing configs
---
Source/hoc-clk/common/include/hocclk/config.h | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Source/hoc-clk/common/include/hocclk/config.h b/Source/hoc-clk/common/include/hocclk/config.h
index 5b8020f1..a188a9ed 100644
--- a/Source/hoc-clk/common/include/hocclk/config.h
+++ b/Source/hoc-clk/common/include/hocclk/config.h
@@ -558,6 +558,14 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case KipConfigValue_t6_tRTW:
case KipConfigValue_t7_tWTR:
case KipConfigValue_t8_tREFI:
+ case KipConfigValue_read_latency_1333:
+ case KipConfigValue_read_latency_1600:
+ case KipConfigValue_read_latency_1866:
+ case KipConfigValue_read_latency_2133:
+ case KipConfigValue_write_latency_1333:
+ case KipConfigValue_write_latency_1600:
+ case KipConfigValue_write_latency_1866:
+ case KipConfigValue_write_latency_2133:
case KipConfigValue_mem_burst_read_latency:
case KipConfigValue_mem_burst_write_latency:
case KipConfigValue_eristaCpuUV:
From be61b9df0e24ac1446458636e927a4ed3021e47c Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sat, 18 Apr 2026 16:30:24 +0200
Subject: [PATCH 08/12] add sloppy configurator: todo fix crap
---
Source/hoc-clk/overlay/Makefile | 2 +-
.../hoc-clk/overlay/src/ui/gui/misc_gui.cpp | 293 +++++++++++++++---
2 files changed, 256 insertions(+), 39 deletions(-)
diff --git a/Source/hoc-clk/overlay/Makefile b/Source/hoc-clk/overlay/Makefile
index 2477a223..634df530 100644
--- a/Source/hoc-clk/overlay/Makefile
+++ b/Source/hoc-clk/overlay/Makefile
@@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk
# version control constants
#---------------------------------------------------------------------------------
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
-APP_VERSION := 1.3.0
+APP_VERSION := 2.0.0
TARGET_VERSION := $(APP_VERSION)
#---------------------------------------------------------------------------------
diff --git a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
index 7c9f1229..60182215 100644
--- a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
+++ b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
@@ -423,7 +423,7 @@ void MiscGui::listUI()
std::vector noNamedValues = {};
this->listElement->addItem(new tsl::elm::CategoryHeader("Settings"));
-
+
tsl::elm::CustomDrawer* rebootSetWarning = new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString("\uE150 Settings marked in orange", false, x + 20, y + 30, 18, tsl::style::color::ColorText);
renderer->drawString("require a reboot to apply!", false, x + 20, y + 50, 18, tsl::style::color::ColorText);
@@ -554,7 +554,7 @@ protected:
{},
RamDisplayUnitValues,
false
-
+
);
addConfigButton(
HocClkConfigValue_PollingIntervalMs,
@@ -604,7 +604,7 @@ protected:
false
);
-
+
std::vector ramRFMeasurementMethods = {
NamedValue("Actmon", MemoryFrequencyMeasurementMode_Actmon),
NamedValue("PLL", MemoryFrequencyMeasurementMode_PLL),
@@ -724,7 +724,7 @@ public:
protected:
void listUI() override {
ValueThresholds thresholdsDisabled(0, 0);
-
+
BaseMenuGui::refresh(); // get latest context
if(!this->context)
return;
@@ -876,6 +876,14 @@ protected:
true
);
+ std::vector stepMode = {
+ NamedValue("66MHz", 0),
+ NamedValue("100MHz", 1),
+ NamedValue("Jedec", 2),
+ };
+
+ addConfigButton(KipConfigValue_stepMode, "Step Mode", ValueRange(0, 0, 2, "", 0), "Step Mode", &thresholdsDisabled, {}, stepMode, false, true);
+
if (IsErista()) {
tsl::elm::ListItem* freqSubmenu = new tsl::elm::ListItem("RAM Frequency Editor");
freqSubmenu->setTextColor(tsl::Color(255, 165, 0, 255));
@@ -958,6 +966,7 @@ protected:
addConfigButton(KipConfigValue_marikoEmcMaxClock, "Ram Max Clock", ValueRange(0, 1, 1, "", 1), "Ram Max Clock", &thresholdsDisabled, {}, marikoMaxEmcClock, false, true);
}
+
tsl::elm::ListItem* latenciesSubmenu = new tsl::elm::ListItem("RAM Latency Editor");
latenciesSubmenu->setTextColor(tsl::Color(255, 165, 0, 255));
latenciesSubmenu->setClickListener([](u64 keys) {
@@ -1033,48 +1042,256 @@ public:
RamLatenciesSubmenuGui() { }
protected:
+
+ void normalizeLatencies(const HocClkConfigValue keysArr[4]) {
+ uint32_t maxClock = (uint32_t)this->configList->values[KipConfigValue_marikoEmcMaxClock];
+ uint32_t vals[4];
+
+ // 1. Fetch and resolve 0xFFFFFFFF
+ for (int i = 0; i < 4; i++) {
+ vals[i] = (uint32_t)this->configList->values[keysArr[i]];
+ if (vals[i] == 0xFFFFFFFFu) vals[i] = maxClock;
+ }
+
+ // 2. Backward Pass: If a higher tier is set, lower tiers must be <= that tier
+ uint32_t currentLimit = 0;
+ for (int i = 3; i >= 0; i--) {
+ if (vals[i] != 0) {
+ if (currentLimit != 0 && vals[i] > currentLimit) {
+ vals[i] = currentLimit;
+ }
+ currentLimit = vals[i];
+ }
+ }
+
+ // 3. Forward Pass: Ensure monotonic increase for active tiers
+ uint32_t last = 0;
+ for (int i = 0; i < 4; i++) {
+ if (vals[i] == 0) continue;
+
+ if (vals[i] < last) vals[i] = last;
+ if (vals[i] > maxClock) vals[i] = maxClock;
+
+ last = vals[i];
+ }
+
+ // 4. Save back to config
+ for (int i = 0; i < 4; i++) {
+ this->configList->values[keysArr[i]] = vals[i];
+ }
+ }
+
void listUI() override {
ValueThresholds thresholdsDisabled(0, 0);
- this->listElement->addItem(new tsl::elm::CategoryHeader("Memory Latencies"));
+ if (IsErista()) {
+ std::vector rlLabels = { NamedValue("1333 RL", 28), NamedValue("1600 RL", 32), NamedValue("1866 RL", 36), NamedValue("2133 RL", 40) };
+ std::vector wlLabels = { NamedValue("1333 WL", 12), NamedValue("1600 WL", 14), NamedValue("1866 WL", 16), NamedValue("2133 WL", 18) };
- std::vector rlLabels = {
- NamedValue("1333 RL", 28),
- NamedValue("1600 RL", 32),
- NamedValue("1866 RL", 36),
- NamedValue("2133 RL", 40)
+ addConfigButton(KipConfigValue_mem_burst_read_latency, "Read Latency", ValueRange(0, 6, 1, "", 0), "Read Latency", &thresholdsDisabled, {}, rlLabels, false, true);
+ addConfigButton(KipConfigValue_mem_burst_write_latency, "Write Latency", ValueRange(0, 6, 1, "", 0), "Write Latency", &thresholdsDisabled, {}, wlLabels, false, true);
+ return;
+ }
+
+ Result rc = hocclkIpcGetConfigValues(this->configList);
+ if (R_FAILED(rc)) [[unlikely]] {
+ FatalGui::openWithResultCode("hocclkIpcGetConfigValues", rc);
+ return;
+ }
+
+ uint32_t maxClock = (uint32_t)this->configList->values[KipConfigValue_marikoEmcMaxClock];
+ RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];
+
+ static const std::vector kFreqOptions = {
+ 1633000, 1666000, 1700000, 1733000, 1766000, 1800000,
+ 1833000, 1866000, 1900000, 1933000, 1966000, 1996800, 2000000,
+ 2033000, 2066000, 2100000, 2133000, 2166000, 2200000, 2233000,
+ 2266000, 2300000, 2333000, 2366000, 2400000, 2433000, 2466000,
+ 2500000, 2533000, 2566000, 2600000, 2633000, 2666000, 2700000,
+ 2733000, 2766000, 2800000, 2833000, 2866000, 2900000, 2933000,
+ 2966000, 3000000, 3033000, 3066000, 3100000, 3133000, 3166000,
+ 3200000, 3233000, 3266000, 3300000,
};
- std::vector wlLabels = {
- NamedValue("1333 WL", 12),
- NamedValue("1600 WL", 14),
- NamedValue("1866 WL", 16),
- NamedValue("2133 WL", 18)
+ static const HocClkConfigValue kLatencyRKeys[4] = {
+ KipConfigValue_read_latency_1333,
+ KipConfigValue_read_latency_1600,
+ KipConfigValue_read_latency_1866,
+ KipConfigValue_read_latency_2133,
+ };
+ static const HocClkConfigValue kLatencyWKeys[4] = {
+ KipConfigValue_write_latency_1333,
+ KipConfigValue_write_latency_1600,
+ KipConfigValue_write_latency_1866,
+ KipConfigValue_write_latency_2133,
};
- addConfigButton(
- KipConfigValue_mem_burst_read_latency,
- "Read Latency",
- ValueRange(0, 6, 1, "", 0),
- "Read Latency",
- &thresholdsDisabled,
- {},
- rlLabels,
- false,
- true
- );
+ static const char* kTierLabels[4] = { "1333 Latency Max", "1600 Latency Max", "1866 Latency Max", "2133 Latency Max" };
- addConfigButton(
- KipConfigValue_mem_burst_write_latency,
- "Write Latency",
- ValueRange(0, 6, 1, "", 0),
- "Write Latency",
- &thresholdsDisabled,
- {},
- wlLabels,
- false,
- true
- );
+ auto buildNamedValues = [&](int tierIdx) -> std::vector {
+ std::vector nv;
+ nv.push_back(NamedValue("-", 0u));
+ if (tierIdx == 3) {
+ nv.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), maxClock));
+ nv.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), 0xFFFFFFFFu));
+ } else {
+ for (uint32_t freq : kFreqOptions) {
+ if (freq > maxClock) continue;
+ nv.push_back(NamedValue(formatMemClockKhzLabel(freq, unit), freq));
+ }
+ nv.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), maxClock));
+ nv.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), 0xFFFFFFFFu));
+ }
+ return nv;
+ };
+
+ auto makeValueText = [&](uint32_t rawVal) -> std::string {
+ if (rawVal == 0) return "-";
+ if (rawVal == 0xFFFFFFFFu) return formatMemClockKhzLabel(maxClock, unit);
+ return formatMemClockKhzLabel(rawVal, unit);
+ };
+
+ auto addLatencyRow = [&](const char* label, int tierIdx, const HocClkConfigValue keysArr[4]) {
+ HocClkConfigValue thisKey = keysArr[tierIdx];
+ uint32_t currentVal = (uint32_t)this->configList->values[thisKey];
+
+ tsl::elm::ListItem* item = new tsl::elm::ListItem(label);
+ item->setTextColor(tsl::Color(255, 165, 0, 255));
+ item->setValue(makeValueText(currentVal));
+
+ item->setClickListener([this, tierIdx, thisKey, keysArr](u64 keys) -> bool {
+ if ((keys & HidNpadButton_A) == 0)
+ return false;
+
+ uint32_t vals[4];
+ for (int i = 0; i < 4; i++)
+ vals[i] = (uint32_t)this->configList->values[keysArr[i]];
+
+ uint32_t maxClock = (uint32_t)this->configList->values[KipConfigValue_marikoEmcMaxClock];
+ RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];
+
+ auto resolveVal = [maxClock](uint32_t v) -> uint32_t {
+ return (v == 0xFFFFFFFFu) ? maxClock : v;
+ };
+
+ if (tierIdx == 3) {
+ bool maxOccupied = false;
+ for (int i = 0; i < 3; i++) {
+ if (resolveVal(vals[i]) == maxClock) {
+ maxOccupied = true;
+ break;
+ }
+ }
+
+ std::vector opts;
+ opts.push_back(NamedValue("-", 0u));
+
+ if (!maxOccupied) {
+ opts.push_back(NamedValue(formatMemClockKhzLabel(maxClock, unit), maxClock));
+ }
+
+ uint32_t displayCurrent = resolveVal(vals[3]);
+ if (maxOccupied && displayCurrent == maxClock) {
+ displayCurrent = 0;
+ }
+
+ tsl::changeTo(
+ displayCurrent,
+ ValueRange(0, 0, 1, "", 1),
+ std::string("2133 Latency Max"),
+ [this, thisKey, keysArr](uint32_t chosen) -> bool {
+ this->configList->values[thisKey] = chosen;
+ Result rc = hocclkIpcSetConfigValues(this->configList);
+ if (R_FAILED(rc)) {
+ FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
+ return false;
+ }
+ sendKipData();
+ this->lastContextUpdate = armGetSystemTick();
+ return true;
+ },
+ ValueThresholds(), false,
+ std::map{},
+ opts,
+ false,
+ false
+ );
+ return true;
+ }
+
+ uint32_t lowerBound = 0;
+ for (int i = 0; i < tierIdx; i++) {
+ uint32_t v = resolveVal(vals[i]);
+ if (v != 0 && v > lowerBound)
+ lowerBound = v;
+ }
+
+ uint32_t upperBound = 0;
+ for (int i = tierIdx + 1; i < 4; i++) {
+ uint32_t v;
+ if (i == 3) {
+ uint32_t r = resolveVal(vals[i]);
+ v = (r != 0) ? maxClock : 0;
+ } else {
+ v = resolveVal(vals[i]);
+ }
+ if (v != 0 && (upperBound == 0 || v < upperBound))
+ upperBound = v;
+ }
+
+ std::vector opts;
+ opts.push_back(NamedValue("-", 0u));
+ for (uint32_t freq : kFreqOptions) {
+ if (freq <= lowerBound) continue;
+ if (freq > maxClock) continue;
+ if (upperBound != 0 && freq >= upperBound) continue;
+ opts.push_back(NamedValue(formatMemClockKhzLabel(freq, unit), freq));
+ }
+
+ uint32_t displayCurrent = resolveVal(vals[tierIdx]);
+ bool currentInList = false;
+ for (auto& nv : opts)
+ if (nv.value == displayCurrent) { currentInList = true; break; }
+ if (!currentInList) displayCurrent = 0;
+
+ tsl::changeTo(
+ displayCurrent,
+ ValueRange(0, 0, 1, "", 1),
+ std::string("Latency Max"),
+ [this, thisKey, keysArr](uint32_t chosen) -> bool {
+ this->configList->values[thisKey] = chosen;
+ normalizeLatencies(keysArr);
+ Result rc = hocclkIpcSetConfigValues(this->configList);
+ if (R_FAILED(rc)) {
+ FatalGui::openWithResultCode("hocclkIpcSetConfigValues", rc);
+ return false;
+ }
+ sendKipData();
+ this->lastContextUpdate = armGetSystemTick();
+ return true;
+ },
+ ValueThresholds(), false,
+ std::map{},
+ opts,
+ false,
+ false
+ );
+ return true;
+ });
+
+ this->listElement->addItem(item);
+ this->configButtons[thisKey] = item;
+ this->configRanges[thisKey] = ValueRange(0, 0, 1, "", 1);
+ this->configNamedValues[thisKey] = buildNamedValues(tierIdx);
+ };
+
+ this->listElement->addItem(new tsl::elm::CategoryHeader("Read Latency"));
+ for (int i = 0; i < 4; i++)
+ addLatencyRow(kTierLabels[i], i, kLatencyRKeys);
+
+ this->listElement->addItem(new tsl::elm::CategoryHeader("Write Latency"));
+ for (int i = 0; i < 4; i++)
+ addLatencyRow(kTierLabels[i], i, kLatencyWKeys);
}
};
@@ -1373,7 +1590,7 @@ protected:
for (auto& nv : eristaMaxEmcClock)
if (nv.name != "Disabled")
nv.name = formatMemClockKhzLabel(nv.value, unit);
-
+
addConfigButtonS(KipConfigValue_eristaEmcMaxClock, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, A_BTN, true);
addConfigButtonS(KipConfigValue_eristaEmcMaxClock1, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, A_BTN, true);
addConfigButtonS(KipConfigValue_eristaEmcMaxClock2, "", ValueRange(0, 1, 1, "", 1), "", &eristaRamThresholds, {}, eristaMaxEmcClock, false, A_BTN, true);
From 55b3a4230c04288ca5bb0cdfc60c0bcc9616966d Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sat, 18 Apr 2026 16:31:49 +0200
Subject: [PATCH 09/12] Remove useless comments, yes I cannot be bothered to
write ui code myself :D; fuck ui
---
Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp | 4 ----
1 file changed, 4 deletions(-)
diff --git a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
index 60182215..edec5b7b 100644
--- a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
+++ b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
@@ -1047,13 +1047,11 @@ protected:
uint32_t maxClock = (uint32_t)this->configList->values[KipConfigValue_marikoEmcMaxClock];
uint32_t vals[4];
- // 1. Fetch and resolve 0xFFFFFFFF
for (int i = 0; i < 4; i++) {
vals[i] = (uint32_t)this->configList->values[keysArr[i]];
if (vals[i] == 0xFFFFFFFFu) vals[i] = maxClock;
}
- // 2. Backward Pass: If a higher tier is set, lower tiers must be <= that tier
uint32_t currentLimit = 0;
for (int i = 3; i >= 0; i--) {
if (vals[i] != 0) {
@@ -1064,7 +1062,6 @@ protected:
}
}
- // 3. Forward Pass: Ensure monotonic increase for active tiers
uint32_t last = 0;
for (int i = 0; i < 4; i++) {
if (vals[i] == 0) continue;
@@ -1075,7 +1072,6 @@ protected:
last = vals[i];
}
- // 4. Save back to config
for (int i = 0; i < 4; i++) {
this->configList->values[keysArr[i]] = vals[i];
}
From 822e9f28175fcbd7d8c1c9ad121e6d1aeb2ffe47 Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sat, 18 Apr 2026 19:02:07 +0200
Subject: [PATCH 10/12] add t2 trp cap
---
.../loader/source/oc/customize.cpp | 6 ++--
.../loader/source/oc/customize.hpp | 2 ++
.../source/oc/mariko/calculate_timings.cpp | 30 +++++++++++++++++--
.../loader/source/oc/mtc_timing_value.hpp | 23 +++++++-------
Source/hoc-clk/common/include/hocclk/config.h | 6 ++++
.../hoc-clk/overlay/src/ui/gui/misc_gui.cpp | 1 +
Source/hoc-clk/sysmodule/src/kip.cpp | 3 ++
Source/hoc-clk/sysmodule/src/kip.hpp | 4 +++
8 files changed, 58 insertions(+), 17 deletions(-)
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
index 2ba074e2..24f39eac 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
@@ -42,7 +42,7 @@ volatile CustomizeTable C = {
/* Available: 66MHz step rate, 100MHz step rate and jedec. */
/* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */
-.stepMode = StepMode_Jedec,
+.stepMode = StepMode_66MHz,
.marikoEmcMaxClock = 2933000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
.marikoEmcVddqVolt = 600000,
@@ -50,7 +50,7 @@ volatile CustomizeTable C = {
// Primary
.t1_tRCD = 0,
-.t2_tRP = 0,
+.t2_tRP = 4,
.t3_tRAS = 0,
// Secondary
.t4_tRRD = 0,
@@ -59,6 +59,8 @@ volatile CustomizeTable C = {
.t7_tWTR = 0,
.t8_tREFI = 0,
+.t2_tRP_cap = 2,
+
.readLatency = {
2133000,
2400000,
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
index 93169bca..19d6673d 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
@@ -103,6 +103,8 @@ typedef struct CustomizeTable {
u32 t7_tWTR;
u32 t8_tREFI;
+ u32 t2_tRP_cap;
+
u32 readLatency[4];
u32 writeLatency[4];
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 7d58178c..2c6ee633 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp
@@ -127,13 +127,39 @@ namespace ams::ldr::hoc::pcv::mariko {
}
void CalculateTimings(double tCK_avg, u32 freq) {
- RL = 28;
- WL = 12;
+ RL = RL_1331;
+ WL = WL_1331;
HandleLatency(freq);
GetRext();
+ /* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
+ /* This is a lazy workaround until I find the issue... */
+ u32 tRPpbIndex = C.t2_tRP;
+ if (WL == WL_1331) {
+ tRPpbIndex = MIN(C.t2_tRP_cap, C.t2_tRP);
+ }
+
+ tRCD = tRCD_values[C.t1_tRCD];
+ tRPpb = tRP_values[tRPpbIndex];
+ tRAS = tRAS_values[C.t3_tRAS];
+ tRRD = tRRD_values[C.t4_tRRD];
+ tRFCpb = tRFC_values[C.t5_tRFC];
+ u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
+ s32 finetRTW = C.fineTune_t6_tRTW;
+ s32 finetWTR = C.fineTune_t7_tWTR;
+
+ Log("Freq: %u\n", freq);
+ Log("WL: %u\n", WL);
+ Log("tRP value: %u\n\n", tRPpb);
+
+ tRC = tRAS + tRPpb;
+ tRFCab = tRFCpb * 2;
+ tXSR = static_cast(tRFCab + 7.5);
+ tFAW = static_cast(tRRD * 4.0);
+ tRPab = tRPpb + 3;
+
tR2P = CEIL((RL * 0.426) - 2.0);
tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW;
tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
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 609901d3..1b592f0e 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp
@@ -111,20 +111,17 @@ namespace ams::ldr::hoc {
const std::array tWTR_values = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
const std::array tREFpb_values = { 3900, 5850, 7800, 11700, 15600, 99999 };
- const u32 tRCD = tRCD_values[C.t1_tRCD];
- const u32 tRPpb = tRP_values[C.t2_tRP];
- const u32 tRAS = tRAS_values[C.t3_tRAS];
- const double tRRD = tRRD_values[C.t4_tRRD];
- const u32 tRFCpb = tRFC_values[C.t5_tRFC];
- const u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
- const s32 finetRTW = C.fineTune_t6_tRTW;
- const s32 finetWTR = C.fineTune_t7_tWTR;
+ inline u32 tRCD;
+ inline u32 tRPpb;
+ inline u32 tRAS;
+ inline double tRRD;
+ inline u32 tRFCpb;
- const u32 tRC = tRAS + tRPpb;
- const u32 tRFCab = tRFCpb * 2;
- const double tXSR = static_cast(tRFCab + 7.5);
- const u32 tFAW = static_cast(tRRD * 4.0);
- const double tRPab = tRPpb + 3;
+ inline u32 tRC;
+ inline u32 tRFCab;
+ inline double tXSR;
+ inline u32 tFAW;
+ inline double tRPab;
inline u32 RL;
inline u32 WL;
diff --git a/Source/hoc-clk/common/include/hocclk/config.h b/Source/hoc-clk/common/include/hocclk/config.h
index a188a9ed..0ccbf01d 100644
--- a/Source/hoc-clk/common/include/hocclk/config.h
+++ b/Source/hoc-clk/common/include/hocclk/config.h
@@ -94,6 +94,8 @@ typedef enum {
KipConfigValue_t7_tWTR,
KipConfigValue_t8_tREFI,
+ KipConfigValue_t2_tRP_cap,
+
KipConfigValue_read_latency_1333,
KipConfigValue_read_latency_1600,
KipConfigValue_read_latency_1866,
@@ -323,6 +325,9 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case KipConfigValue_t8_tREFI:
return pretty ? "t8 - tREFI" : "t8_trefi";
+ case KipConfigValue_t2_tRP_cap:
+ return pretty ? "t2 - trp 1333WL Cap" : "t2_tRP_cap";
+
case KipConfigValue_read_latency_1333:
return pretty ? "1333 Read Latency" : "read_latency_1333";
case KipConfigValue_read_latency_1600:
@@ -558,6 +563,7 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case KipConfigValue_t6_tRTW:
case KipConfigValue_t7_tWTR:
case KipConfigValue_t8_tREFI:
+ case KipConfigValue_t2_tRP_cap:
case KipConfigValue_read_latency_1333:
case KipConfigValue_read_latency_1600:
case KipConfigValue_read_latency_1866:
diff --git a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
index edec5b7b..2d7cd270 100644
--- a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
+++ b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
@@ -1032,6 +1032,7 @@ protected:
};
this->listElement->addItem(new tsl::elm::CategoryHeader("Advanced"));
+ addConfigButton(KipConfigValue_t2_tRP_cap, "1333WL t2 RP Cap", ValueRange(0, 8, 1, "", 1), "tRP Cap", &thresholdsDisabled, {}, {}, false, true );
addConfigButton(KipConfigValue_t6_tRTW_fine_tune, "t6 tRTW Fine Tune", ValueRange(0, 4, 1, "", 0), "tRTW Fine Tune", &thresholdsDisabled, {}, t6_tRTW_fine_tune, false, true);
addConfigButton(KipConfigValue_t7_tWTR_fine_tune, "t7 tWTR Fine Tune", ValueRange(0, 6, 1, "", 0), "tWTR Fine Tune", &thresholdsDisabled, {}, t7_tWTR_fine_tune, false, true);
}
diff --git a/Source/hoc-clk/sysmodule/src/kip.cpp b/Source/hoc-clk/sysmodule/src/kip.cpp
index c11c06c6..6f670241 100644
--- a/Source/hoc-clk/sysmodule/src/kip.cpp
+++ b/Source/hoc-clk/sysmodule/src/kip.cpp
@@ -71,6 +71,7 @@ namespace kip {
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR, config::GetConfigValue(KipConfigValue_t7_tWTR));
CUST_WRITE_FIELD_BATCH(&table, t8_tREFI, config::GetConfigValue(KipConfigValue_t8_tREFI));
CUST_WRITE_FIELD_BATCH(&table, stepMode, config::GetConfigValue(KipConfigValue_stepMode));
+ CUST_WRITE_FIELD_BATCH(&table, t2_tRP_cap, config::GetConfigValue(KipConfigValue_t2_tRP_cap));
CUST_WRITE_FIELD_BATCH(&table, readLatency1333, config::GetConfigValue(KipConfigValue_read_latency_1333));
CUST_WRITE_FIELD_BATCH(&table, readLatency1600, config::GetConfigValue(KipConfigValue_read_latency_1600));
@@ -201,6 +202,8 @@ namespace kip {
configValues.values[KipConfigValue_t8_tREFI] = cust_get_tREFI(&table);
configValues.values[KipConfigValue_stepMode] = cust_get_step_mode(&table);
+ configValues.values[KipConfigValue_t2_tRP_cap] = cust_get_tRP_cap(&table);
+
configValues.values[KipConfigValue_read_latency_1333] = cust_get_read_latency_1333(&table);
configValues.values[KipConfigValue_read_latency_1600] = cust_get_read_latency_1600(&table);
configValues.values[KipConfigValue_read_latency_1866] = cust_get_read_latency_1866(&table);
diff --git a/Source/hoc-clk/sysmodule/src/kip.hpp b/Source/hoc-clk/sysmodule/src/kip.hpp
index 8cb27f17..460775a1 100644
--- a/Source/hoc-clk/sysmodule/src/kip.hpp
+++ b/Source/hoc-clk/sysmodule/src/kip.hpp
@@ -54,6 +54,8 @@ namespace kip {
u32 t7_tWTR;
u32 t8_tREFI;
+ u32 t2_tRP_cap;
+
/* These latencies are arrays in loader, but it's easier to handle it this way in the configurator. */
u32 readLatency1333, readLatency1600, readLatency1866, readLatency2133;
u32 writeLatency1333, writeLatency1600, writeLatency1866, writeLatency2133;
@@ -219,6 +221,7 @@ namespace kip {
static inline bool cust_set_tRTW(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW, v); }
static inline bool cust_set_tWTR(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR, v); }
static inline bool cust_set_tREFI(const char* p, u32 v) { CUST_WRITE_FIELD(p, t8_tREFI, v); }
+ static inline bool cust_set_tRP_cap(const char* p, u32 v) { CUST_WRITE_FIELD(p, t2_tRP_cap, v); }
static inline bool cust_set_tRTW_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW_fine_tune, v); }
static inline bool cust_set_tWTR_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR_fine_tune, v); }
@@ -302,6 +305,7 @@ namespace kip {
static inline u32 cust_get_tRTW(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW); }
static inline u32 cust_get_tWTR(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR); }
static inline u32 cust_get_tREFI(const CustomizeTable* t) { return CUST_GET_FIELD(t, t8_tREFI); }
+ static inline u32 cust_get_tRP_cap(const CustomizeTable* t) { return CUST_GET_FIELD(t, t2_tRP_cap); }
static inline u32 cust_get_tRTW_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW_fine_tune); }
static inline u32 cust_get_tWTR_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR_fine_tune); }
From 6a851d40958e7a8535d040dc480cb592a91a421e Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sat, 18 Apr 2026 23:00:21 +0200
Subject: [PATCH 11/12] nso start check, timing tbreak and some horrendous ui
code
---
.../loader/source/oc/customize.cpp | 7 ++
.../loader/source/oc/customize.hpp | 4 +
.../source/oc/mariko/calculate_timings.cpp | 15 ++--
.../loader/source/oc/pcv/pcv_mariko.cpp | 25 +++++--
Source/hoc-clk/common/include/hocclk/config.h | 14 ++++
.../hoc-clk/overlay/src/ui/gui/misc_gui.cpp | 75 ++++++++++++++++++-
Source/hoc-clk/sysmodule/src/kip.cpp | 7 ++
Source/hoc-clk/sysmodule/src/kip.hpp | 10 +++
8 files changed, 143 insertions(+), 14 deletions(-)
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
index 24f39eac..121c5d8b 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
@@ -59,8 +59,15 @@ volatile CustomizeTable C = {
.t7_tWTR = 0,
.t8_tREFI = 0,
+/* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
+/* This is a lazy workaround until I find the issue... */
.t2_tRP_cap = 2,
+/* Frequency where non low timings gets used. */
+.timingEmcTbreak = DISABLED,
+.low_t6_tRTW = DISABLED,
+.low_t7_tWTR = DISABLED,
+
.readLatency = {
2133000,
2400000,
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
index 19d6673d..e3e23bd7 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp
@@ -105,6 +105,10 @@ typedef struct CustomizeTable {
u32 t2_tRP_cap;
+ u32 timingEmcTbreak;
+ u32 low_t6_tRTW;
+ u32 low_t7_tWTR;
+
u32 readLatency[4];
u32 writeLatency[4];
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 2c6ee633..c88412d7 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp
@@ -146,14 +146,17 @@ namespace ams::ldr::hoc::pcv::mariko {
tRAS = tRAS_values[C.t3_tRAS];
tRRD = tRRD_values[C.t4_tRRD];
tRFCpb = tRFC_values[C.t5_tRFC];
- u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
+ u32 tRTW = C.t6_tRTW;
+ u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
+
+ if (freq < C.timingEmcTbreak) {
+ tRTW = C.low_t6_tRTW;
+ tWTR = 10 - tWTR_values[C.low_t7_tWTR];
+ }
+
s32 finetRTW = C.fineTune_t6_tRTW;
s32 finetWTR = C.fineTune_t7_tWTR;
- Log("Freq: %u\n", freq);
- Log("WL: %u\n", WL);
- Log("tRP value: %u\n\n", tRPpb);
-
tRC = tRAS + tRPpb;
tRFCab = tRFCpb * 2;
tXSR = static_cast(tRFCab + 7.5);
@@ -161,7 +164,7 @@ namespace ams::ldr::hoc::pcv::mariko {
tRPab = tRPpb + 3;
tR2P = CEIL((RL * 0.426) - 2.0);
- tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW;
+ tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (tRTW * 3) + finetRTW;
tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
tRATM = CEIL((tRTM - 10.0) + (RL * 0.426));
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 fbb60197..3e0b04b1 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp
@@ -591,7 +591,11 @@ namespace ams::ldr::hoc::pcv::mariko {
}
}
- std::vector newEmcList;
+ namespace {
+ std::vector newEmcList;
+ u32 *nsoStart;
+ }
+
void MtcGenerateJedecTable() {
const u32 jedecFreqs[] = { 1866000, 1996000, 2133000, 2400000, 2666000, 2933000, 3200000 };
constexpr u32 JedecFreqCount = std::size(jedecFreqs);
@@ -718,6 +722,7 @@ namespace ams::ldr::hoc::pcv::mariko {
static const DramId dramId = [] {
DramId id = GetDramId();
+ id = IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL;
return id;
}();
@@ -877,29 +882,37 @@ namespace ams::ldr::hoc::pcv::mariko {
}
Result MemMtcTableAsm(u32 *ptr) {
- u32 adrp = *(ptr - 1);
+ constexpr u32 AddpOffset = 1;
+ constexpr u32 BrOffset = 12;
+ constexpr u32 MovOffset = 10;
+
+ /* Ensure we don't dereference memory before nso start. */
+ R_UNLESS(ptr - BrOffset >= nsoStart, ldr::ResultInvalidMtcTable());
+
+ u32 adrp = *(ptr - AddpOffset);
R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTable());
/* We don't check for matching register because both registers must be x0 in order to pass the previous checks. */
/* The correct instructions will always be x0 since the mtcTable pointer is returned. */
/* Pray this does not break. */
- u32 br = *(ptr - 12);
+ u32 br = *(ptr - BrOffset);
R_UNLESS(AsmCompareBrNoRd(br, MtcBrAsm), ldr::ResultInvalidMtcTable());
/* Pray this does not break either. */
- u32 mov = *(ptr - 10);
+ u32 mov = *(ptr - MovOffset);
R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTable());
u8 movRd = asm_get_rd(mov);
u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), movRd);
- PATCH_OFFSET(ptr - 12, NopIns);
- PATCH_OFFSET(ptr - 10, movCountPatch);
+ PATCH_OFFSET(ptr - BrOffset, NopIns);
+ PATCH_OFFSET(ptr - MovOffset, movCountPatch);
R_SUCCEED();
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
+ nsoStart = reinterpret_cast(mapped_nso);
MtcGenerateFreqTables();
u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
diff --git a/Source/hoc-clk/common/include/hocclk/config.h b/Source/hoc-clk/common/include/hocclk/config.h
index 0ccbf01d..825caf5d 100644
--- a/Source/hoc-clk/common/include/hocclk/config.h
+++ b/Source/hoc-clk/common/include/hocclk/config.h
@@ -94,6 +94,10 @@ typedef enum {
KipConfigValue_t7_tWTR,
KipConfigValue_t8_tREFI,
+ KipConfigValue_timingEmcTbreak,
+ KipConfigValue_low_t6_tRTW,
+ KipConfigValue_low_t7_tWTR,
+
KipConfigValue_t2_tRP_cap,
KipConfigValue_read_latency_1333,
@@ -325,6 +329,13 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case KipConfigValue_t8_tREFI:
return pretty ? "t8 - tREFI" : "t8_trefi";
+ case KipConfigValue_timingEmcTbreak:
+ return pretty ? "Timing Emc Tbreak" : "timingEmcTbreak";
+ case KipConfigValue_low_t6_tRTW:
+ return pretty ? "Low T6 - tRTW" : "low_t6_tRTW";
+ case KipConfigValue_low_t7_tWTR:
+ return pretty ? "Low T7 - tWTR" : "low_t7_tWTR";
+
case KipConfigValue_t2_tRP_cap:
return pretty ? "t2 - trp 1333WL Cap" : "t2_tRP_cap";
@@ -563,6 +574,9 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case KipConfigValue_t6_tRTW:
case KipConfigValue_t7_tWTR:
case KipConfigValue_t8_tREFI:
+ case KipConfigValue_timingEmcTbreak:
+ case KipConfigValue_low_t6_tRTW:
+ case KipConfigValue_low_t7_tWTR:
case KipConfigValue_t2_tRP_cap:
case KipConfigValue_read_latency_1333:
case KipConfigValue_read_latency_1600:
diff --git a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
index 2d7cd270..086ababb 100644
--- a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
+++ b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp
@@ -897,6 +897,7 @@ protected:
freqSubmenu->setValue(R_ARROW);
this->listElement->addItem(freqSubmenu);
} else {
+ RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];
std::vector marikoMaxEmcClock = {
NamedValue("1600 MHz", 1600000),
NamedValue("1633 MHz", 1633000),
@@ -958,8 +959,6 @@ protected:
// NamedValue("3466MHz (Needs ridiculous Speedo/PLL)", 3466000),
// NamedValue("3500MHz (Needs ridiculous Speedo/PLL)", 3500000),
};
-
- RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];
for (auto& nv : marikoMaxEmcClock)
nv.name = formatMemClockKhzLabel(nv.value, unit);
@@ -1031,7 +1030,79 @@ protected:
NamedValue("+3", 3),
};
+ /* Yes this is duplicated code, yes I don't care. */
+ std::vector timingTbreakFreqs = {
+ NamedValue("Disabled", 0),
+ NamedValue("1633 MHz", 1633000),
+ NamedValue("1666 MHz", 1666000),
+ NamedValue("1700 MHz", 1700000),
+ NamedValue("1733 MHz", 1733000),
+ NamedValue("1766 MHz", 1766000),
+ NamedValue("1800 MHz", 1800000),
+ NamedValue("1833 MHz", 1833000),
+ NamedValue("1866 MHz", 1866000, "JEDEC."),
+ NamedValue("1900 MHz", 1900000),
+ NamedValue("1933 MHz", 1933000),
+ NamedValue("1966 MHz", 1966000),
+ NamedValue("1996 MHz", 1996800, "JEDEC."),
+ NamedValue("2000 MHz", 2000000),
+ NamedValue("2033 MHz", 2033000),
+ NamedValue("2066 MHz", 2066000),
+ NamedValue("2100 MHz", 2100000),
+ NamedValue("2133 MHz", 2133000, "JEDEC."),
+ NamedValue("2166 MHz", 2166000),
+ NamedValue("2200 MHz", 2200000),
+ NamedValue("2233 MHz", 2233000),
+ NamedValue("2266 MHz", 2266000),
+ NamedValue("2300 MHz", 2300000),
+ NamedValue("2333 MHz", 2333000),
+ NamedValue("2366 MHz", 2366000),
+ NamedValue("2400 MHz", 2400000, "JEDEC."),
+ NamedValue("2433 MHz", 2433000),
+ NamedValue("2466 MHz", 2466000),
+ NamedValue("2500 MHz", 2500000),
+ NamedValue("2533 MHz", 2533000),
+ NamedValue("2566 MHz", 2566000),
+ NamedValue("2600 MHz", 2600000),
+ NamedValue("2633 MHz", 2633000),
+ NamedValue("2666 MHz", 2666000, "JEDEC."),
+ NamedValue("2700 MHz", 2700000),
+ NamedValue("2733 MHz", 2733000),
+ NamedValue("2766 MHz", 2766000),
+ NamedValue("2800 MHz", 2800000),
+ NamedValue("2833 MHz", 2833000),
+ NamedValue("2866 MHz", 2866000),
+ NamedValue("2900 MHz", 2900000),
+ NamedValue("2933 MHz", 2933000, "JEDEC."),
+ NamedValue("2966 MHz", 2966000),
+ NamedValue("3000 MHz", 3000000),
+ NamedValue("3033 MHz", 3033000),
+ NamedValue("3066 MHz", 3066000),
+ NamedValue("3100 MHz", 3100000),
+ NamedValue("3133 MHz", 3133000),
+ NamedValue("3166 MHz", 3166000),
+ NamedValue("3200 MHz", 3200000, "JEDEC."),
+ NamedValue("3233 MHz", 3233000, "High speedo needed"),
+ NamedValue("3266 MHz", 3266000, "High speedo needed!"),
+ NamedValue("3300 MHz", 3300000, "High speedo needed!"),
+ // NamedValue("3333MHz (Needs extreme Speedo/PLL)", 3333000),
+ // NamedValue("3366MHz (Needs extreme Speedo/PLL)", 3366000),
+ // NamedValue("3400MHz (Needs extreme Speedo/PLL)", 3400000),
+ // NamedValue("3433MHz (Needs ridiculous Speedo/PLL)", 3433000),
+ // NamedValue("3466MHz (Needs ridiculous Speedo/PLL)", 3466000),
+ // NamedValue("3500MHz (Needs ridiculous Speedo/PLL)", 3500000),
+ };
+ RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit];
+
+ for (size_t i = 1; i < timingTbreakFreqs.size(); ++i) {
+ auto &nv = timingTbreakFreqs[i];
+ nv.name = formatMemClockKhzLabel(nv.value, unit);
+ }
+
this->listElement->addItem(new tsl::elm::CategoryHeader("Advanced"));
+ addConfigButton(KipConfigValue_timingEmcTbreak, "RAM-Timing tBreak", ValueRange(0, 1, 1, "", 1), "tBreak", &thresholdsDisabled, {}, timingTbreakFreqs, false, true);
+ addConfigButton(KipConfigValue_low_t6_tRTW, "Low t6 tRTW", ValueRange(0, 10, 1, "", 1), "low tRTW", &thresholdsDisabled, {}, {}, false, true );
+ addConfigButton(KipConfigValue_low_t7_tWTR, "Low t7 tWTR", ValueRange(0, 10, 1, "", 1), "low tWTR", &thresholdsDisabled, {}, {}, false, true );
addConfigButton(KipConfigValue_t2_tRP_cap, "1333WL t2 RP Cap", ValueRange(0, 8, 1, "", 1), "tRP Cap", &thresholdsDisabled, {}, {}, false, true );
addConfigButton(KipConfigValue_t6_tRTW_fine_tune, "t6 tRTW Fine Tune", ValueRange(0, 4, 1, "", 0), "tRTW Fine Tune", &thresholdsDisabled, {}, t6_tRTW_fine_tune, false, true);
addConfigButton(KipConfigValue_t7_tWTR_fine_tune, "t7 tWTR Fine Tune", ValueRange(0, 6, 1, "", 0), "tWTR Fine Tune", &thresholdsDisabled, {}, t7_tWTR_fine_tune, false, true);
diff --git a/Source/hoc-clk/sysmodule/src/kip.cpp b/Source/hoc-clk/sysmodule/src/kip.cpp
index 6f670241..5b5b3213 100644
--- a/Source/hoc-clk/sysmodule/src/kip.cpp
+++ b/Source/hoc-clk/sysmodule/src/kip.cpp
@@ -71,6 +71,10 @@ namespace kip {
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR, config::GetConfigValue(KipConfigValue_t7_tWTR));
CUST_WRITE_FIELD_BATCH(&table, t8_tREFI, config::GetConfigValue(KipConfigValue_t8_tREFI));
CUST_WRITE_FIELD_BATCH(&table, stepMode, config::GetConfigValue(KipConfigValue_stepMode));
+
+ CUST_WRITE_FIELD_BATCH(&table, timingEmcTbreak, config::GetConfigValue(KipConfigValue_timingEmcTbreak));
+ CUST_WRITE_FIELD_BATCH(&table, low_t6_tRTW, config::GetConfigValue(KipConfigValue_low_t6_tRTW));
+ CUST_WRITE_FIELD_BATCH(&table, low_t7_tWTR, config::GetConfigValue(KipConfigValue_low_t7_tWTR));
CUST_WRITE_FIELD_BATCH(&table, t2_tRP_cap, config::GetConfigValue(KipConfigValue_t2_tRP_cap));
CUST_WRITE_FIELD_BATCH(&table, readLatency1333, config::GetConfigValue(KipConfigValue_read_latency_1333));
@@ -202,6 +206,9 @@ namespace kip {
configValues.values[KipConfigValue_t8_tREFI] = cust_get_tREFI(&table);
configValues.values[KipConfigValue_stepMode] = cust_get_step_mode(&table);
+ configValues.values[KipConfigValue_timingEmcTbreak] = cust_get_timing_emc_tbreak(&table);
+ configValues.values[KipConfigValue_low_t6_tRTW] = cust_get_low_t6_tRTW(&table);
+ configValues.values[KipConfigValue_low_t7_tWTR] = cust_get_low_t7_tWTR(&table);
configValues.values[KipConfigValue_t2_tRP_cap] = cust_get_tRP_cap(&table);
configValues.values[KipConfigValue_read_latency_1333] = cust_get_read_latency_1333(&table);
diff --git a/Source/hoc-clk/sysmodule/src/kip.hpp b/Source/hoc-clk/sysmodule/src/kip.hpp
index 460775a1..8a57f2a2 100644
--- a/Source/hoc-clk/sysmodule/src/kip.hpp
+++ b/Source/hoc-clk/sysmodule/src/kip.hpp
@@ -56,6 +56,10 @@ namespace kip {
u32 t2_tRP_cap;
+ u32 timingEmcTbreak;
+ u32 low_t6_tRTW;
+ u32 low_t7_tWTR;
+
/* These latencies are arrays in loader, but it's easier to handle it this way in the configurator. */
u32 readLatency1333, readLatency1600, readLatency1866, readLatency2133;
u32 writeLatency1333, writeLatency1600, writeLatency1866, writeLatency2133;
@@ -222,6 +226,9 @@ namespace kip {
static inline bool cust_set_tWTR(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR, v); }
static inline bool cust_set_tREFI(const char* p, u32 v) { CUST_WRITE_FIELD(p, t8_tREFI, v); }
static inline bool cust_set_tRP_cap(const char* p, u32 v) { CUST_WRITE_FIELD(p, t2_tRP_cap, v); }
+ static inline bool cust_set_timing_emc_tbreak(const char* p, u32 v) { CUST_WRITE_FIELD(p, timingEmcTbreak, v); }
+ static inline bool cust_set_low_tRTW(const char* p, u32 v) { CUST_WRITE_FIELD(p, low_t6_tRTW, v); }
+ static inline bool cust_set_low_tWTR(const char* p, u32 v) { CUST_WRITE_FIELD(p, low_t7_tWTR, v); }
static inline bool cust_set_tRTW_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW_fine_tune, v); }
static inline bool cust_set_tWTR_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR_fine_tune, v); }
@@ -306,6 +313,9 @@ namespace kip {
static inline u32 cust_get_tWTR(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR); }
static inline u32 cust_get_tREFI(const CustomizeTable* t) { return CUST_GET_FIELD(t, t8_tREFI); }
static inline u32 cust_get_tRP_cap(const CustomizeTable* t) { return CUST_GET_FIELD(t, t2_tRP_cap); }
+ static inline u32 cust_get_timing_emc_tbreak(const CustomizeTable* t) { return CUST_GET_FIELD(t, timingEmcTbreak); }
+ static inline u32 cust_get_low_t6_tRTW(const CustomizeTable* t) { return CUST_GET_FIELD(t, low_t6_tRTW); }
+ static inline u32 cust_get_low_t7_tWTR(const CustomizeTable* t) { return CUST_GET_FIELD(t, low_t7_tWTR); }
static inline u32 cust_get_tRTW_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW_fine_tune); }
static inline u32 cust_get_tWTR_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR_fine_tune); }
From f1eab00ce1378020bac9ae0f4ef659587036c480 Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sun, 19 Apr 2026 14:51:15 +0200
Subject: [PATCH 12/12] revert custom and pcv to default settings
---
.../loader/source/oc/customize.cpp | 22 +++++++++----------
.../stratosphere/loader/source/oc/pcv/pcv.cpp | 2 +-
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
index 121c5d8b..c46201a0 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp
@@ -44,13 +44,13 @@ volatile CustomizeTable C = {
/* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */
.stepMode = StepMode_66MHz,
-.marikoEmcMaxClock = 2933000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
+.marikoEmcMaxClock = 1866000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
.marikoEmcVddqVolt = 600000,
-.emcDvbShift = 10,
+.emcDvbShift = 0,
// Primary
.t1_tRCD = 0,
-.t2_tRP = 4,
+.t2_tRP = 0,
.t3_tRAS = 0,
// Secondary
.t4_tRRD = 0,
@@ -69,17 +69,17 @@ volatile CustomizeTable C = {
.low_t7_tWTR = DISABLED,
.readLatency = {
- 2133000,
- 2400000,
- 2600000,
- C.marikoEmcMaxClock,
+ DISABLED,
+ DISABLED,
+ DISABLED,
+ DISABLED,
},
.writeLatency = {
- 2133000,
- 2400000,
- 2600000,
- C.marikoEmcMaxClock,
+ DISABLED,
+ DISABLED,
+ DISABLED,
+ DISABLED,
},
/* You can mix and match different latencies if needed */
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
index 0b9ff6c8..3b29b705 100644
--- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
+++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp
@@ -178,7 +178,7 @@ namespace ams::ldr::hoc::pcv {
if (isMariko) {
mariko::Patch(mapped_nso, nso_size);
} else {
- mariko::Patch(mapped_nso, nso_size);
+ erista::Patch(mapped_nso, nso_size);
}
#endif