From d28a01d2c244ff79042401d19212f066402bff3c Mon Sep 17 00:00:00 2001 From: Lightos1 <124387232+Lightos1@users.noreply.github.com> Date: Sat, 22 Nov 2025 20:44:27 +0100 Subject: [PATCH] 2133BL for mariko (AA); TODO: proper scaling and adjustments --- .../loader/source/oc/customize.cpp | 36 +- .../loader/source/oc/customize.hpp | 2 +- .../loader/source/oc/mtc_timing_value.hpp | 69 ++- .../stratosphere/loader/source/oc/pcv/pcv.hpp | 112 ++-- .../loader/source/oc/pcv/pcv_mariko.cpp | 522 ++++++++---------- 5 files changed, 341 insertions(+), 400 deletions(-) diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 684740e1..e8bd2c29 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -36,19 +36,19 @@ volatile CustomizeTable C = { .commonCpuBoostClock = 1785000, // Default boost clock -.commonEmcMemVolt = 1212500, // LPDDR4X JEDEC Specification +.commonEmcMemVolt = 1175000, // LPDDR4X JEDEC Specification .eristaCpuMaxVolt = 1235, -.eristaEmcMaxClock = 2300000, // Maximum HB-MGCH ram rating +.eristaEmcMaxClock = 1600001, // Maximum HB-MGCH ram rating .marikoCpuMaxVolt = 1120, -.marikoEmcMaxClock = 1866000, // Hynix NME and Samsung AM-MGCJ Rating (others are 4766MT, 2133MHz) +.marikoEmcMaxClock = 3000000, // Hynix NME and Samsung AM-MGCJ Rating (others are 4766MT, 2133MHz) .marikoEmcVddqVolt = 640000, -.marikoCpuUV = 5, // No undervolt +.marikoCpuUV = 0, // No undervolt .marikoGpuUV = 3, @@ -71,15 +71,15 @@ volatile CustomizeTable C = { // Defaults - (3-3-2) 0-1-4-3-6 // Primary -.t1_tRCD = 5, -.t2_tRP = 7, -.t3_tRAS = 8, +.t1_tRCD = 0, +.t2_tRP = 0, +.t3_tRAS = 0, // Secondary -.t4_tRRD = 1, -.t5_tRFC = 2, -.t6_tRTW = 5, -.t7_tWTR = 4, -.t8_tREFI= 6, +.t4_tRRD = 0, +.t5_tRFC = 0, +.t6_tRTW = 0, +.t7_tWTR = 0, +.t8_tREFI= 0, // .mem_burst_latency = 0, // 0 - 1600l, 1 = 1866bl, 2 = 2133bl /* TODO: Remove/fix. */ @@ -110,12 +110,12 @@ volatile CustomizeTable C = { 710 /* 1075 */, 735 /* 1152 */, 785 /* 1228 */, - 780 /* 1267 (Disabled by default) */, - 805 /* 1305 (Disabled by default) */, - 870 /* 1344 (Disabled by default) */, - 870 /* 1382 (Disabled by default) */, - 870 /* 1420 (Disabled by default) */, - 0 /* 1459 (Disabled by default) */, + 785 /* 1267 (Disabled by default) */, + 960 /* 1305 (Disabled by default) */, + 960 /* 1344 (Disabled by default) */, + 960 /* 1382 (Disabled by default) */, + 960 /* 1420 (Disabled by default) */, + 960 /* 1459 (Disabled by default) */, 0 /* 1497 (Disabled by default) */, 0 /* 1536 (Disabled by default) */, diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index bbf367a0..690fab87 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -46,7 +46,7 @@ u8 cust[4] = {'C', 'U', 'S', 'T'}; u32 custRev = CUST_REV; u32 mtcConf = AUTO_ADJ; - bool hpMode; + u32 hpMode; u32 commonCpuBoostClock; u32 commonEmcMemVolt; u32 eristaCpuMaxVolt; 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 4436111f..08c987b7 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp @@ -28,37 +28,44 @@ namespace ams::ldr::oc { const std::array tRCD_values = {18, 17, 16, 15, 14, 13, 12, 11}; const std::array tRP_values = {18, 17, 16, 15, 14, 13, 12, 11}; const std::array tRAS_values = {42, 36, 34, 32, 30, 28, 26, 24, 22, 20}; - - /* Secondary timings. */ const std::array tRRD_values = {10.0, 7.5, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0}; - const std::array tRFC_values = {90, 80, 70, 60, 50, 40}; + const std::array tRFC_values = {140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40}; const std::array tWTR_values = {10, 9, 8, 7, 6, 5, 4, 3, 3, 1}; const std::array tREFpb_values = {3900, 5850, 7800, 11700, 15600, 99999}; + const u32 BL = 16; + /* Set to 4 read and 2 write for 1866bl. */ /* For 2131bl: 8 read and 4 write. */ const u32 latency_offset_read = 0; const u32 latency_offset_write = 0; - const u32 BL = 16; - const u32 RL = 28 + latency_offset_read; - const u32 WL = 14 + latency_offset_write; - /* Precharge to Precharge Delay. (Cycles) */ - /* Don't touch! */ const u32 tPPD = 4; /* DQS output access time from CK_t/CK_c. */ const double tDQSCK_max = 3.5; - const double tWPRE = 2.0; + const double tWPRE = 1.8; /* tCK Read postamble. */ const double tRPST = 0.5; + /* Minimum Self-Refresh Time. (Entry to Exit) */ + const double tSR = 15.0; + + /* Exit power down to next valid command delay. */ + const double tXP = 7.5; + + const double tDQSS_max = 1.25; + const double tDQS2DQ_max = 0.8; + + /* TOOD: Fix erista */ namespace pcv::erista { - /* tCK_avg may have to be improved... */ const double tCK_avg = 1000'000.0 / C.eristaEmcMaxClock; + const u32 RL = 28 + latency_offset_read; + const u32 WL = 14 + latency_offset_write; + /* Primary timings. */ const u32 tRCD = tRCD_values[C.t1_tRCD]; const u32 tRPpb = tRP_values[C.t2_tRP]; @@ -76,33 +83,57 @@ namespace ams::ldr::oc { /* Latency stuff. */ const int tR2W = (int)((3.5 / tCK_avg) + 32 + (BL / 2) - 14 - 6 + tWPRE + 12 - (C.t6_tRTW * 3)); const int tW2R = (int)((tWTR / tCK_avg) + 18 - (BL / 2)); - const int tRW2PDEN = (int)((1.25 / tCK_avg) + 46 + (0.8 / tCK_avg) + 6); + const int tRW2PDEN = (int)((tDQSS_max / tCK_avg) + 46 + (tDQS2DQ_max / tCK_avg) + 6); /* Refresh Cycle time. (All Banks) */ const u32 tRFCab = tRFCpb * 2; - /* Do not touch stuff. */ /* ACTIVATE-to-ACTIVATE command period. (same bank) */ const u32 tRC = tRAS + tRPpb; - /* Minimum Self-Refresh Time. (Entry to Exit) */ - const double tSR = 15.0; /* SELF REFRESH exit to next valid command delay. */ const double tXSR = (double) (tRFCab + 7.5); - /* Exit power down to next valid command delay. */ - const double tXP = 7.5; // I assume this is correct. - /* u32ernal READ to PRECHARGE command delay. */ const int pdex2mrr = (tCK_avg * 3.0) + tRCD_values[C.t1_tRCD] + 1; /* Row Precharge Time. (all banks) */ - const double tRPab = tRPpb + 3; + const u32 tRPab = tRPpb + 3; } - /* TODO. */ namespace pcv::mariko { + 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]; + const double tRRD = tRRD_values[C.t4_tRRD]; + const u32 tRFCpb = tRFC_values[C.t5_tRFC]; + const u32 tWTR = tWTR_values[C.t7_tWTR]; + + const u32 RL = 36; + const u32 WL = 18; + + const u32 tRC = tRAS + tRPpb; + const u32 tRFCab = tRFCpb * 2; + const double tXSR = (double) (tRFCab + 7.5); + const u32 tFAW = (u32) (tRRD * 4.0); + const double tRPab = tRPpb + 3; + + const double tR2P = 7.5; /* Tighten up? */ + const u32 tR2W = CEIL(RL + (tDQSCK_max / tCK_avg) + (BL / 2) - WL + tWPRE + FLOOR(tRPST)); /* TODO */ + const u32 tRTM = RL + 9 + (tDQSCK_max / tCK_avg) + FLOOR(tRPST) + CEIL(7.5 / tCK_avg); + const u32 tRATM = tRTM + CEIL(tR2P / tCK_avg) - (BL / 2); + + /* Note: Dividing WL is probably incorect but it works out by pure chance :) */ + const u32 tW2P = WL + (BL / 2) + 1 + CEIL(WL / tCK_avg); /* Tighten? */ + const u32 tW2R = WL + (BL / 2) + 1 + CEIL(tWTR / tCK_avg); + const u32 tWTM = WL + (BL / 2) + 1 + CEIL(7.5 / tCK_avg); + const u32 tWATM = tWTM + CEIL(WL / tCK_avg); + + const double tMMRI = tRCD + (tCK_avg * 3); + const double tPDEX2MRR = tMMRI + 10; + const u32 tWTPDEN = tW2P + 1 + CEIL(tDQSS_max / tCK_avg) + CEIL(tDQS2DQ_max / tCK_avg) + 6.0; } } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index 7760fccc..511d8b58 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -26,8 +26,7 @@ namespace ams::ldr::oc::pcv { - namespace mariko - { + namespace mariko { constexpr cvb_entry_t CpuCvbTableDefault[] = { // CPUB01_CVB_TABLE {204000, {721589, -12695, 27}, {}}, @@ -119,8 +118,7 @@ namespace ams::ldr::oc::pcv inline auto asm_set_imm16 = [](u32 ins, u16 imm) { return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); }; - inline bool GpuMaxClockPatternFn(u32 *ptr32) - { + inline bool GpuMaxClockPatternFn(u32 *ptr32) { return asm_compare_no_rd(*ptr32, asm_pattern[0]); } @@ -168,8 +166,7 @@ namespace ams::ldr::oc::pcv } - namespace erista - { + namespace erista { constexpr cvb_entry_t CpuCvbTableDefault[] = { // CPU_PLL_CVB_TABLE_ODN {204000, {721094}, {}}, @@ -199,8 +196,7 @@ namespace ams::ldr::oc::pcv constexpr u16 CpuMinVolts[] = {950, 850, 825, 810}; - inline bool CpuMaxVoltPatternFn(u32 *ptr32) - { + inline bool CpuMaxVoltPatternFn(u32 *ptr32) { u32 val = *ptr32; return (val == 1132 || val == 1170 || val == 1227); } @@ -229,8 +225,7 @@ namespace ams::ldr::oc::pcv inline auto asm_set_imm16 = [](u32 ins, u16 imm) { return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); }; - inline bool GpuMaxClockPatternFn(u32 *ptr32) - { + inline bool GpuMaxClockPatternFn(u32 *ptr32) { return asm_compare_no_rd(*ptr32, asm_pattern[0]); } @@ -260,8 +255,7 @@ namespace ams::ldr::oc::pcv } template - Result CpuFreqCvbTable(u32 *ptr) - { + Result CpuFreqCvbTable(u32 *ptr) { cvb_entry_t *default_table = isMariko ? (cvb_entry_t *)(&mariko::CpuCvbTableDefault) : (cvb_entry_t *)(&erista::CpuCvbTableDefault); cvb_entry_t *customize_table = nullptr; // impossible to reach, there will always be a way to set a pointer @@ -284,12 +278,9 @@ namespace ams::ldr::oc::pcv } u32 cpu_max_volt = isMariko ? C.marikoCpuMaxVolt : C.eristaCpuMaxVolt; u32 cpu_freq_threshold = 1020'000; - if (isMariko) - { + if (isMariko) { cpu_freq_threshold = C.marikoCpuUV ? 2193'000 : 2091'000; - } - else - { + } else { cpu_freq_threshold = cpu_max_volt >= 1300 ? 1887'000 : 1428'000; } @@ -307,19 +298,13 @@ namespace ams::ldr::oc::pcv std::memcpy(cpu_cvb_table_head, static_cast(customize_table), customize_table_size); // Patch CPU max volt - if (cpu_max_volt) - { + if (cpu_max_volt) { cvb_entry_t *entry = static_cast(cpu_cvb_table_head); - for (size_t i = 0; i < customize_entry_count; i++) - { - if (entry->freq >= cpu_freq_threshold) - { - if (isMariko) - { + for (size_t i = 0; i < customize_entry_count; i++) { + if (entry->freq >= cpu_freq_threshold) { + if (isMariko) { PATCH_OFFSET(&(entry->cvb_pll_param.c0), cpu_max_volt * 1000); - } - else - { + } else { PATCH_OFFSET(&(entry->cvb_dfll_param.c0), cpu_max_volt * 1000); } } @@ -331,14 +316,11 @@ namespace ams::ldr::oc::pcv } template - Result GpuFreqCvbTable(u32 *ptr) - { + Result GpuFreqCvbTable(u32 *ptr) { cvb_entry_t *default_table = isMariko ? (cvb_entry_t *)(&mariko::GpuCvbTableDefault) : (cvb_entry_t *)(&erista::GpuCvbTableDefault); cvb_entry_t *customize_table; - if (isMariko) - { - switch (C.marikoGpuUV) - { + if (isMariko) { + switch (C.marikoGpuUV) { case 0: customize_table = const_cast(C.marikoGpuDvfsTable); break; @@ -349,24 +331,18 @@ namespace ams::ldr::oc::pcv customize_table = const_cast(C.marikoGpuDvfsTableHiOPT); break; case 3: - if(C.enableMarikoGpuUnsafeFreqs) - { + if(C.enableMarikoGpuUnsafeFreqs) { customize_table = const_cast(C.marikoGpuDvfsTableUv3UnsafeFreqs); - } - else - { - customize_table = const_cast(C.marikoGpuDvfsTable); + } else { + customize_table = const_cast(C.marikoGpuDvfsTable); } break; default: customize_table = const_cast(C.marikoGpuDvfsTable); break; } - } - else - { - switch (C.eristaGpuUV) - { + } else { + switch (C.eristaGpuUV) { case 0: customize_table = const_cast(C.eristaGpuDvfsTable); break; @@ -377,13 +353,10 @@ namespace ams::ldr::oc::pcv customize_table = const_cast(C.eristaGpuDvfsTableHigh); break; case 3: - if(C.enableEristaGpuUnsafeFreqs) - { - customize_table = const_cast(C.eristaGpuDvfsTableUv3UnsafeFreqs); - } - else - { - customize_table = const_cast(C.eristaGpuDvfsTable); + if(C.enableEristaGpuUnsafeFreqs) { + customize_table = const_cast(C.eristaGpuDvfsTableUv3UnsafeFreqs); + } else { + customize_table = const_cast(C.eristaGpuDvfsTable); } break; default: @@ -406,23 +379,16 @@ namespace ams::ldr::oc::pcv std::memcpy(gpu_cvb_table_head, (void *)customize_table, customize_table_size); // Patch GPU volt - if (C.marikoGpuUV == 3 || C.eristaGpuUV == 3) - { + if (C.marikoGpuUV == 3 || C.eristaGpuUV == 3) { cvb_entry_t *entry = static_cast(gpu_cvb_table_head); - for (size_t i = 0; i < customize_entry_count; i++) - { - if (isMariko) - { - if (C.marikoGpuVoltArray[i] == 0) - { + for (size_t i = 0; i < customize_entry_count; i++) { + if (isMariko) { + if (C.marikoGpuVoltArray[i] == 0) { continue; } PATCH_OFFSET(&(entry->cvb_pll_param.c0), C.marikoGpuVoltArray[i] * 1000); - } - else - { - if (C.eristaGpuVoltArray[i] == 0) - { + } else { + if (C.eristaGpuVoltArray[i] == 0) { continue; } PATCH_OFFSET(&(entry->cvb_pll_param.c0), C.eristaGpuVoltArray[i] * 1000); @@ -434,12 +400,9 @@ namespace ams::ldr::oc::pcv PATCH_OFFSET(&(entry->cvb_pll_param.c5), 0); entry++; } - } - else if (C.commonGpuVoltOffset) - { + } else if (C.commonGpuVoltOffset) { cvb_entry_t *entry = static_cast(gpu_cvb_table_head); - for (size_t i = 0; i < customize_entry_count; i++) - { + for (size_t i = 0; i < customize_entry_count; i++) { PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000)); entry++; } @@ -452,21 +415,18 @@ namespace ams::ldr::oc::pcv Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2 template - Result MemMtcCustomizeTable(T *dst, T *src) - { + Result MemMtcCustomizeTable(T *dst, T *src) { constexpr u32 mtc_magic = std::is_same_v ? MARIKO_MTC_MAGIC : ERISTA_MTC_MAGIC; R_UNLESS(src->rev == mtc_magic, ldr::ResultInvalidMtcMagic()); constexpr u32 ZERO_VAL = UINT32_MAX; // Skip params from dvfs_ver to clock_src; - for (size_t offset = offsetof(T, clk_src_emc); offset < sizeof(T); offset += sizeof(u32)) - { + for (size_t offset = offsetof(T, clk_src_emc); offset < sizeof(T); offset += sizeof(u32)) { u32 *src_ent = reinterpret_cast(reinterpret_cast(src) + offset); u32 *dst_ent = reinterpret_cast(reinterpret_cast(dst) + offset); u32 src_val = *src_ent; - if (src_val) - { + if (src_val){ PATCH_OFFSET(dst_ent, src_val == ZERO_VAL ? 0 : src_val); } } 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 9977f916..500dad41 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -21,47 +21,38 @@ #include "pcv.hpp" #include "../mtc_timing_value.hpp" -namespace ams::ldr::oc::pcv::mariko -{ +namespace ams::ldr::oc::pcv::mariko { - Result GpuVmin(u32 *ptr) - { + Result GpuVmin(u32 *ptr) { if (!C.marikoGpuVmin) R_SKIP(); PATCH_OFFSET(ptr, (int)C.marikoGpuVmin); R_SUCCEED(); } - Result GpuVmax(u32 *ptr) - { + Result GpuVmax(u32 *ptr) { if (!C.marikoGpuVmax) R_SKIP(); PATCH_OFFSET(ptr, (int)C.marikoGpuVmax); R_SUCCEED(); } - Result CpuFreqVdd(u32 *ptr) - { + Result CpuFreqVdd(u32 *ptr) { dvfs_rail *entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(dvfs_rail, freq)); R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry()); R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry()); R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry()); R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry()); - if (C.enableMarikoCpuUnsafeFreqs) - { + if (C.enableMarikoCpuUnsafeFreqs) { PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); } - else - { - if (C.marikoCpuUV) - { - if (!C.enableMarikoCpuUnsafeFreqs) - { + else { + if (C.marikoCpuUV) { + if (!C.enableMarikoCpuUnsafeFreqs) { PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq); } - else - { + else { PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTableUnsafeFreqs)->freq); } } @@ -69,11 +60,9 @@ namespace ams::ldr::oc::pcv::mariko R_SUCCEED(); } - Result CpuVoltRange(u32 *ptr) - { + Result CpuVoltRange(u32 *ptr) { u32 min_volt_got = *(ptr - 1); - for (const auto &mv : CpuMinVolts) - { + for (const auto &mv : CpuMinVolts) { if (min_volt_got != mv) continue; @@ -82,14 +71,11 @@ namespace ams::ldr::oc::pcv::mariko PATCH_OFFSET(ptr, C.marikoCpuMaxVolt); // Patch vmin for slt - if (C.marikoCpuUV) - { - if (*(ptr - 5) == 620) - { + if (C.marikoCpuUV) { + if (*(ptr - 5) == 620) { PATCH_OFFSET((ptr - 5), C.marikoCpuVmin); } - if (*(ptr - 1) == 620) - { + if (*(ptr - 1) == 620) { PATCH_OFFSET((ptr - 1), 600); } } @@ -98,16 +84,14 @@ namespace ams::ldr::oc::pcv::mariko R_THROW(ldr::ResultInvalidCpuMinVolt()); } - Result CpuVoltDfll(u32 *ptr) - { + Result CpuVoltDfll(u32 *ptr) { cvb_cpu_dfll_data *entry = reinterpret_cast(ptr); R_UNLESS(entry->tune0_low == 0x0000FFCF, ldr::ResultInvalidCpuVoltDfllEntry()); R_UNLESS(entry->tune0_high == 0x00000000, ldr::ResultInvalidCpuVoltDfllEntry()); R_UNLESS(entry->tune1_low == 0x012207FF, ldr::ResultInvalidCpuVoltDfllEntry()); R_UNLESS(entry->tune1_high == 0x03FFF7FF, ldr::ResultInvalidCpuVoltDfllEntry()); - switch (C.marikoCpuUV) - { + switch (C.marikoCpuUV) { case 0: break; case 1: @@ -164,8 +148,7 @@ namespace ams::ldr::oc::pcv::mariko R_SUCCEED(); } - Result GpuFreqMaxAsm(u32 *ptr32) - { + Result GpuFreqMaxAsm(u32 *ptr32) { // Check if both two instructions match the pattern u32 ins1 = *ptr32, ins2 = *(ptr32 + 1); if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1]))) @@ -177,8 +160,7 @@ namespace ams::ldr::oc::pcv::mariko R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); u32 max_clock; - switch (C.marikoGpuUV) - { + switch (C.marikoGpuUV) { case 0: max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; break; @@ -189,12 +171,10 @@ namespace ams::ldr::oc::pcv::mariko max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq; break; case 3: - if (C.enableMarikoGpuUnsafeFreqs) - { + if (C.enableMarikoGpuUnsafeFreqs) { max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableUv3UnsafeFreqs)->freq; } - else - { + else { max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; } break; @@ -204,20 +184,20 @@ namespace ams::ldr::oc::pcv::mariko } u32 asm_patch[2] = { asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd), - asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)}; + asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd) + }; + PATCH_OFFSET(ptr32, asm_patch[0]); PATCH_OFFSET(ptr32 + 1, asm_patch[1]); R_SUCCEED(); } - Result GpuFreqPllLimit(u32 *ptr) - { + Result GpuFreqPllLimit(u32 *ptr) { clk_pll_param *entry = reinterpret_cast(ptr); // All zero except for freq - for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) - { + for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) { R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry()); } @@ -227,183 +207,193 @@ namespace ams::ldr::oc::pcv::mariko R_SUCCEED(); } - /* Get RAM vendor data, ty b0rd2death! */ - /* Note: I know this is horrible but I don't care atm. */ - bool IsMicron() - { - u64 packed_version; - splGetConfig((SplConfigItem)2, &packed_version); - - switch (packed_version) - { - case 11: - case 15: - case 25: - case 26: - case 27: - case 32: - case 33: - case 34: - /* RAM is Micron. */ - return true; - default: - /* Not Micron. */ - return false; - } - } - - void MemMtcTableAutoAdjustBaseLatency(MarikoMtcTable *table) { #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \ TABLE->burst_regs.PARAM = VALUE; \ TABLE->shadow_regs_ca_train.PARAM = VALUE; \ TABLE->shadow_regs_rdwr_train.PARAM = VALUE; - WRITE_PARAM_ALL_REG(table, emc_cfg, 0xf3200000); - WRITE_PARAM_ALL_REG(table, emc_rc, 0x00000070); - WRITE_PARAM_ALL_REG(table, emc_rfc, 0x0000020b); - WRITE_PARAM_ALL_REG(table, emc_ras, 0x0000004f); - WRITE_PARAM_ALL_REG(table, emc_rp, 0x00000022); - WRITE_PARAM_ALL_REG(table, emc_r2w, 0x0000002e); - WRITE_PARAM_ALL_REG(table, emc_w2r, 0x00000025); - WRITE_PARAM_ALL_REG(table, emc_r2p, 0x0000000e); - WRITE_PARAM_ALL_REG(table, emc_w2p, 0x00000033); - WRITE_PARAM_ALL_REG(table, emc_rd_rcd, 0x00000022); - WRITE_PARAM_ALL_REG(table, emc_wr_rcd, 0x00000022); - WRITE_PARAM_ALL_REG(table, emc_rrd, 0x00000013); - WRITE_PARAM_ALL_REG(table, emc_rext, 0x0000001a); + #define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) - WRITE_PARAM_ALL_REG(table, emc_qsafe, 0x00000038); - WRITE_PARAM_ALL_REG(table, emc_refresh, 0x00001c2d); - WRITE_PARAM_ALL_REG(table, emc_burst_refresh_num, 0x00000000); - WRITE_PARAM_ALL_REG(table, emc_pdex2wr, 0x00000013); - WRITE_PARAM_ALL_REG(table, emc_pdex2rd, 0x00000013); - WRITE_PARAM_ALL_REG(table, emc_pchg2pden, 0x00000004); - WRITE_PARAM_ALL_REG(table, emc_act2pden, 0x0000001b); - WRITE_PARAM_ALL_REG(table, emc_ar2pden, 0x00000004); - WRITE_PARAM_ALL_REG(table, emc_rw2pden, 0x0000003f); - WRITE_PARAM_ALL_REG(table, emc_txsr, 0x00000219); - WRITE_PARAM_ALL_REG(table, emc_tcke, 0x00000010); - WRITE_PARAM_ALL_REG(table, emc_tfaw, 0x0000004b); - WRITE_PARAM_ALL_REG(table, emc_trpab, 0x00000028); - WRITE_PARAM_ALL_REG(table, emc_tclkstable, 0x00000004); - WRITE_PARAM_ALL_REG(table, emc_tclkstop, 0x00000017); - WRITE_PARAM_ALL_REG(table, emc_trefbw, 0x00001c6d); - WRITE_PARAM_ALL_REG(table, emc_tppd, 0x00000004); - WRITE_PARAM_ALL_REG(table, emc_odt_write, 0x00000000); - WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, 0x00000037); - WRITE_PARAM_ALL_REG(table, emc_wext, 0x00000016); - WRITE_PARAM_ALL_REG(table, emc_rfc_slr, 0x00000000); - WRITE_PARAM_ALL_REG(table, emc_mrs_wait_cnt2, 0x01d3001b); - WRITE_PARAM_ALL_REG(table, emc_mrs_wait_cnt, 0x074a0034); - table->emc_mrs = 0x00000000; - table->emc_emrs = 0x00000000; - table->emc_mrw = 0x00170040; - WRITE_PARAM_ALL_REG(table, emc_fbio_spare, 0x00000012); - WRITE_PARAM_ALL_REG(table, emc_fbio_cfg5, 0x9160a00d); - WRITE_PARAM_ALL_REG(table, emc_pdex2cke, 0x00000002); - WRITE_PARAM_ALL_REG(table, emc_cke2pden, 0x00000010); - WRITE_PARAM_ALL_REG(table, emc_r2r, 0x00000000); - WRITE_PARAM_ALL_REG(table, emc_einput, 0x00000015); - WRITE_PARAM_ALL_REG(table, emc_einput_duration, 0x00000020); - WRITE_PARAM_ALL_REG(table, emc_puterm_extra, 0x00000001); - WRITE_PARAM_ALL_REG(table, emc_tckesr, 0x0000001c); - WRITE_PARAM_ALL_REG(table, emc_tpd, 0x0000000e); - table->emc_cfg_2 = 0x0011083d; - WRITE_PARAM_ALL_REG(table, emc_cfg_dig_dll, 0x002c03a9); - WRITE_PARAM_ALL_REG(table, emc_cfg_dig_dll_period, 0x00008000); - WRITE_PARAM_ALL_REG(table, emc_rdv_mask, 0x00000040); - WRITE_PARAM_ALL_REG(table, emc_wdv_mask, 0x00000010); - WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, 0x0000003e); - WRITE_PARAM_ALL_REG(table, emc_rdv_early, 0x0000003c); - WRITE_PARAM_ALL_REG(table, emc_fdpd_ctrl_dq, 0x8020221f); - WRITE_PARAM_ALL_REG(table, emc_fdpd_ctrl_cmd, 0x0220f40f); - table->emc_sel_dpd_ctrl = 0x0004000c; - WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, 0x0000070b); - WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, 0x80003873); - WRITE_PARAM_ALL_REG(table, emc_txsrdll, 0x00000219); - WRITE_PARAM_ALL_REG(table, emc_ibdly, 0x1000001f); - WRITE_PARAM_ALL_REG(table, emc_obdly, 0x10000004); - WRITE_PARAM_ALL_REG(table, emc_txdsrvttgen, 0x00000000); - WRITE_PARAM_ALL_REG(table, emc_we_duration, 0x0000000e); - WRITE_PARAM_ALL_REG(table, emc_ws_duration, 0x00000008); - WRITE_PARAM_ALL_REG(table, emc_wev, 0x0000000c); - WRITE_PARAM_ALL_REG(table, emc_cfg_3, 0x00000040); - WRITE_PARAM_ALL_REG(table, emc_wdv_chk, 0x00000006); - WRITE_PARAM_ALL_REG(table, emc_cfg_pipe_2, 0x00000000); - WRITE_PARAM_ALL_REG(table, emc_cfg_pipe_1, 0x0fff0000); - WRITE_PARAM_ALL_REG(table, emc_cfg_pipe, 0x0fff0000); - WRITE_PARAM_ALL_REG(table, emc_quse_width, 0x00000009); - WRITE_PARAM_ALL_REG(table, emc_puterm_width, 0x80000000); - WRITE_PARAM_ALL_REG(table, emc_fbio_cfg7, 0x00003bff); - WRITE_PARAM_ALL_REG(table, emc_rfcpb, 0x00000106); - WRITE_PARAM_ALL_REG(table, emc_ccdmw, 0x00000020); - WRITE_PARAM_ALL_REG(table, emc_config_sample_delay, 0x00000020); - table->dram_timings.t_rp = 0x00000106; - table->dram_timings.t_rfc = 0x0000020b; + if (C.hpMode) { + WRITE_PARAM_ALL_REG(table, emc_cfg, 0x13200000); + } else { + WRITE_PARAM_ALL_REG(table, emc_cfg, 0xF3200000); + } - table->dram_timings.rl = 32; /* */ - WRITE_PARAM_ALL_REG(table, emc_wdv, 0x00000010); /* */ - WRITE_PARAM_ALL_REG(table, emc_quse, 0x00000028); /* */ - WRITE_PARAM_ALL_REG(table, emc_qrst, 0x0007000c); /* These timings cause issues and I have no idea why. */ - WRITE_PARAM_ALL_REG(table, emc_rdv, 0x0000003e); /* */ - WRITE_PARAM_ALL_REG(table, emc_wsv, 0x0000000e); /* */ - WRITE_PARAM_ALL_REG(table, emc_qpop, 0x00000030); /* */ + u32 refresh_raw = 0xFFFF; + if (C.t8_tREFI != 6) { + refresh_raw = std::floor(tREFpb_values[C.t8_tREFI] / tCK_avg) - 0x40; + refresh_raw = MIN(refresh_raw, static_cast(0xFFFF)); + } - table->burst_mc_regs.mc_emem_arb_cfg = 0x0000000E; - table->burst_mc_regs.mc_emem_arb_outstanding_req = 0x80000080; - table->burst_mc_regs.mc_emem_arb_timing_rcd = 0x00000007; - table->burst_mc_regs.mc_emem_arb_timing_rp = 0x00000008; - table->burst_mc_regs.mc_emem_arb_timing_rc = 0x0000001C; - table->burst_mc_regs.mc_emem_arb_timing_ras = 0x00000012; - table->burst_mc_regs.mc_emem_arb_timing_faw = 0x00000012; - table->burst_mc_regs.mc_emem_arb_timing_rrd = 0x00000004; - table->burst_mc_regs.mc_emem_arb_timing_rap2pre = 0x00000004; - table->burst_mc_regs.mc_emem_arb_timing_wap2pre = 0x0000000F; - table->burst_mc_regs.mc_emem_arb_timing_r2r = 0x00000001; - table->burst_mc_regs.mc_emem_arb_timing_w2w = 0x00000001; - table->burst_mc_regs.mc_emem_arb_timing_r2w = 0x0000000D; - table->burst_mc_regs.mc_emem_arb_timing_w2r = 0x0000000B; - table->burst_mc_regs.mc_emem_arb_da_turns = 0x05060000; - table->burst_mc_regs.mc_emem_arb_da_covers = 0x000F0A0E; - table->burst_mc_regs.mc_emem_arb_misc0 = 0x726E2A1D; - table->burst_mc_regs.mc_emem_arb_misc1 = 0x70000F0F; - table->burst_mc_regs.mc_emem_arb_misc2 = 0x00000000; - table->burst_mc_regs.mc_emem_arb_ring1_throttle = 0x001F0000; - table->burst_mc_regs.mc_emem_arb_timing_rfcpb = 0x00000041; - table->burst_mc_regs.mc_emem_arb_timing_ccdmw = 0x00000008; - table->burst_mc_regs.mc_emem_arb_dhyst_ctrl = 0x00000002; - table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_0 = 0x0000001A; - table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_1 = 0x0000001A; - table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_2 = 0x0000001A; - table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_3 = 0x0000001A; - table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_4 = 0x0000001A; - table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_5 = 0x0000001A; - table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_6 = 0x0000001A; - table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_7 = 0x0000001A; - table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = 0x000000F2; - table->la_scale_regs.mc_ftop_ptsa_rate = 0x0000001B; - table->la_scale_regs.mc_ptsa_grant_decrement = 0x00001501; - table->la_scale_regs.mc_latency_allowance_avpc_0 = 0x006D0004; - table->la_scale_regs.mc_latency_allowance_sdmmcaa_0 = 0x006D0005; - table->la_scale_regs.mc_latency_allowance_sdmmca_0 = 0x006D0014; - table->la_scale_regs.mc_latency_allowance_isp2_0 = 0x0000002C; - table->la_scale_regs.mc_latency_allowance_isp2_1 = 0x006D006D; - table->la_scale_regs.mc_latency_allowance_vic_0 = 0x006D0019; - table->la_scale_regs.mc_latency_allowance_nvdec_0 = 0x006D0095; - table->la_scale_regs.mc_latency_allowance_tsec_0 = 0x006D0041; - table->la_scale_regs.mc_latency_allowance_ppcs_1 = 0x006D0080; - table->la_scale_regs.mc_latency_allowance_xusb_0 = 0x006D003D; - table->la_scale_regs.mc_latency_allowance_ppcs_0 = 0x00340049; - table->la_scale_regs.mc_latency_allowance_gpu2_0 = 0x006D0016; - table->la_scale_regs.mc_latency_allowance_hc_1 = 0x0000006D; - table->la_scale_regs.mc_latency_allowance_sdmmc_0 = 0x006D0090; - table->la_scale_regs.mc_latency_allowance_mpcore_0 = 0x006D0004; - table->la_scale_regs.mc_latency_allowance_vi2_0 = 0x0000006D; - table->la_scale_regs.mc_latency_allowance_hc_0 = 0x00080013; - table->la_scale_regs.mc_latency_allowance_gpu_0 = 0x006D0016; - table->la_scale_regs.mc_latency_allowance_sdmmcab_0 = 0x006D0005; - table->la_scale_regs.mc_latency_allowance_nvenc_0 = 0x006D0018; + u32 trefbw = refresh_raw + 0x40; + trefbw = MIN(trefbw, static_cast(0x3FFF)); + + WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD)); + WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD)); + WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC)); + WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS)); + WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD)); + WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb)); + WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab)); + WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb)); + WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), static_cast(0x3fe))); + WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), static_cast(0x3fe))); + WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW)); + WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab)); + WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tSR)); + WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tXP) + 1); + WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tXP)); + WRITE_PARAM_ALL_REG(table, emc_tclkstop, GET_CYCLE_CEIL(tXP) + 8); + WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tR2P)); + WRITE_PARAM_ALL_REG(table, emc_r2w, tR2W); + WRITE_PARAM_ALL_REG(table, emc_trtm, tRTM); + WRITE_PARAM_ALL_REG(table, emc_tratm, tRATM); + WRITE_PARAM_ALL_REG(table, emc_w2p, tW2P); + WRITE_PARAM_ALL_REG(table, emc_w2r, tW2R); + WRITE_PARAM_ALL_REG(table, emc_twtm, tWTM); + WRITE_PARAM_ALL_REG(table, emc_twatm, tWATM); + WRITE_PARAM_ALL_REG(table, emc_rext, 26); + 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); + const u32 dyn_self_ref_control = (((u32)(7605.0 / tCK_avg)) + 260U) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000U); + WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, dyn_self_ref_control); + WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE_CEIL(10.0)); + WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE_CEIL(10.0)); + WRITE_PARAM_ALL_REG(table, emc_pchg2pden, GET_CYCLE_CEIL(1.75)); + WRITE_PARAM_ALL_REG(table, emc_ar2pden, GET_CYCLE_CEIL(1.75)); + WRITE_PARAM_ALL_REG(table, emc_act2pden, GET_CYCLE_CEIL(14.0)); + WRITE_PARAM_ALL_REG(table, emc_cke2pden, GET_CYCLE_CEIL(5.0)); + WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR)); + WRITE_PARAM_ALL_REG(table, emc_rw2pden, tWTPDEN); + WRITE_PARAM_ALL_REG(table, emc_einput, 0xF); + WRITE_PARAM_ALL_REG(table, emc_einput_duration, 0x31); + WRITE_PARAM_ALL_REG(table, emc_obdly, 0x10000002); + WRITE_PARAM_ALL_REG(table, emc_ibdly, 0x1000001C); + WRITE_PARAM_ALL_REG(table, emc_wdv_mask, 0x12); + WRITE_PARAM_ALL_REG(table, emc_quse_width, 0xD); + WRITE_PARAM_ALL_REG(table, emc_quse, 0x2F); + WRITE_PARAM_ALL_REG(table, emc_wdv, 0x12); + WRITE_PARAM_ALL_REG(table, emc_wsv, 0x10); + WRITE_PARAM_ALL_REG(table, emc_wev, 0xE); + WRITE_PARAM_ALL_REG(table, emc_qrst, 0x00080005); + WRITE_PARAM_ALL_REG(table, emc_qsafe, 0x44); + WRITE_PARAM_ALL_REG(table, emc_tr_qpop, 0x3B); + WRITE_PARAM_ALL_REG(table, emc_rdv, 0x49); + WRITE_PARAM_ALL_REG(table, emc_qpop, 0x3B); + WRITE_PARAM_ALL_REG(table, emc_tr_rdv_mask, 0x4B); + WRITE_PARAM_ALL_REG(table, emc_rdv_early, 0x47); + WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, 0x49); + WRITE_PARAM_ALL_REG(table, emc_rdv_mask, 0x4B); + WRITE_PARAM_ALL_REG(table, emc_tr_rdv, 0x49); + + constexpr u32 MC_ARB_DIV = 4; + 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_timing_rcd = (u32) (GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2; + table->burst_mc_regs.mc_emem_arb_timing_rp = (u32) (GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA; + table->burst_mc_regs.mc_emem_arb_timing_rc = (u32) (GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1; + table->burst_mc_regs.mc_emem_arb_timing_ras = (u32) (GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2; + table->burst_mc_regs.mc_emem_arb_timing_faw = (u32) (GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1; + table->burst_mc_regs.mc_emem_arb_timing_rrd = (u32) (GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1; + table->burst_mc_regs.mc_emem_arb_timing_rfcpb = (u32) (GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV); + table->burst_mc_regs.mc_emem_arb_timing_rap2pre = (u32) (GET_CYCLE_CEIL(tR2P) / MC_ARB_DIV); + table->burst_mc_regs.mc_emem_arb_timing_wap2pre = (u32) (tW2P / MC_ARB_DIV); + table->burst_mc_regs.mc_emem_arb_timing_r2r = (u32) (table->burst_regs.emc_rext / 4) - 1 + MC_ARB_SFA; + table->burst_mc_regs.mc_emem_arb_timing_r2w = (u32) (tR2W / MC_ARB_DIV) - 1 + MC_ARB_SFA; + table->burst_mc_regs.mc_emem_arb_timing_w2r = (u32) (tW2R / MC_ARB_DIV) - 1 + MC_ARB_SFA; + + u32 da_turns = 0; + da_turns |= u8(table->burst_mc_regs.mc_emem_arb_timing_r2w / 2) << 16; + da_turns |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24; + table->burst_mc_regs.mc_emem_arb_da_turns = da_turns; + + u32 da_covers = 0; + u8 r_cover = (table->burst_mc_regs.mc_emem_arb_timing_rap2pre + table->burst_mc_regs.mc_emem_arb_timing_rp + table->burst_mc_regs.mc_emem_arb_timing_rcd) / 2; + u8 w_cover = (table->burst_mc_regs.mc_emem_arb_timing_wap2pre + table->burst_mc_regs.mc_emem_arb_timing_rp + table->burst_mc_regs.mc_emem_arb_timing_rcd) / 2; + da_covers |= (u8) (table->burst_mc_regs.mc_emem_arb_timing_rc / 2); + da_covers |= (r_cover << 8); + da_covers |= (w_cover << 16); + table->burst_mc_regs.mc_emem_arb_da_covers = da_covers; + + table->burst_mc_regs.mc_emem_arb_misc0 &= 0xFFE08000U; + table->burst_mc_regs.mc_emem_arb_misc0 |= ((table->burst_mc_regs.mc_emem_arb_timing_rc + 1) & 0xFF); /* TODO, check this */ + + table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0xd0U), (u32)0x115); + table->la_scale_regs.mc_ftop_ptsa_rate = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0x18U), (u32)0x1f); + table->la_scale_regs.mc_ptsa_grant_decrement = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0x1203U), (u32)0x17ff); + + u32 mc_latency_allowance = 0; + if (C.marikoEmcMaxClock / 1000 != 0) { + mc_latency_allowance = 204800 / (C.marikoEmcMaxClock / 1000); + } + + const u32 mc_latency_allowance2 = mc_latency_allowance & 0xFF; + const u32 mc_latency_allowance3 = (mc_latency_allowance & 0xFF) << 0x10; + table->la_scale_regs.mc_latency_allowance_xusb_0 = (table->la_scale_regs.mc_latency_allowance_xusb_0 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_sdmmc_0 = (table->la_scale_regs.mc_latency_allowance_sdmmc_0 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_xusb_1 = (table->la_scale_regs.mc_latency_allowance_xusb_1 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_tsec_0 = (table->la_scale_regs.mc_latency_allowance_tsec_0 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_sdmmca_0 = (table->la_scale_regs.mc_latency_allowance_sdmmca_0 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_sdmmcaa_0 = (table->la_scale_regs.mc_latency_allowance_sdmmcaa_0 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_sdmmcab_0 = (table->la_scale_regs.mc_latency_allowance_sdmmcab_0 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_ppcs_1 = (table->la_scale_regs.mc_latency_allowance_ppcs_1 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_mpcore_0 = (table->la_scale_regs.mc_latency_allowance_mpcore_0 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_avpc_0 = (table->la_scale_regs.mc_latency_allowance_avpc_0 & 0xff00ffffU) | mc_latency_allowance3; + + u32 mc_latency_allowance_hc_0 = 0; + if (C.marikoEmcMaxClock / 1000 != 0) { + mc_latency_allowance_hc_0 = 35200 / (C.marikoEmcMaxClock / 1000); + } + + table->la_scale_regs.mc_latency_allowance_nvdec_0 = (table->la_scale_regs.mc_latency_allowance_nvdec_0 & 0xff00ffffU) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_hc_0 = (table->la_scale_regs.mc_latency_allowance_hc_0 & 0xffffff00U) | mc_latency_allowance_hc_0; + + table->la_scale_regs.mc_latency_allowance_isp2_1 = (table->la_scale_regs.mc_latency_allowance_isp2_1 & 0xff00ff00U) | mc_latency_allowance3 | mc_latency_allowance2; + table->la_scale_regs.mc_latency_allowance_hc_1 = (table->la_scale_regs.mc_latency_allowance_hc_1 & 0xffffff00U) | mc_latency_allowance2; + + u32 mc_latency_allowance_gpu_0 = 0; + if (C.marikoEmcMaxClock / 1000 != 0) { + mc_latency_allowance_gpu_0 = 40000 / (C.marikoEmcMaxClock / 1000); + } + + table->la_scale_regs.mc_latency_allowance_gpu_0 = ((mc_latency_allowance_gpu_0 | table->la_scale_regs.mc_latency_allowance_gpu_0) & 0xff00ff00U) | mc_latency_allowance3; + + u32 mc_latency_allowance_gpu2_0 = 0; + if (C.marikoEmcMaxClock / 1000 != 0) { + mc_latency_allowance_gpu2_0 = 40000 / (C.marikoEmcMaxClock / 1000); + } + + table->la_scale_regs.mc_latency_allowance_gpu2_0 = ((mc_latency_allowance_gpu2_0 | table->la_scale_regs.mc_latency_allowance_gpu2_0) & 0xff00ff00U) | mc_latency_allowance3; + + u32 mc_latency_allowance_nvenc_0 = 0; + if (C.marikoEmcMaxClock / 1000 != 0) { + mc_latency_allowance_nvenc_0 = 38400 / (C.marikoEmcMaxClock / 1000); + } + + table->la_scale_regs.mc_latency_allowance_nvenc_0 = ((mc_latency_allowance_nvenc_0 | table->la_scale_regs.mc_latency_allowance_nvenc_0) & 0xff00ff00U) | mc_latency_allowance3; + + u32 mc_latency_allowance_vic_0 = 0; + if (C.marikoEmcMaxClock / 1000 != 0) { + mc_latency_allowance_vic_0 = 0xb540 / (C.marikoEmcMaxClock / 1000); + } + + table->la_scale_regs.mc_latency_allowance_vic_0 = ((mc_latency_allowance_vic_0 | table->la_scale_regs.mc_latency_allowance_vic_0) & 0xff00ff00U) | mc_latency_allowance3; + table->la_scale_regs.mc_latency_allowance_vi2_0 = (table->la_scale_regs.mc_latency_allowance_vi2_0 & 0xffffff00U) | mc_latency_allowance2; + + table->pllm_ss_ctrl1 = 0xb55fe01; + table->pllm_ss_ctrl2 = 0x10170b55; + table->pllmb_ss_ctrl1 = 0xb55fe01; + table->pllmb_ss_ctrl2 = 0x10170b55; + + table->dram_timings.t_rp = tRFCpb; + table->dram_timings.t_rfc = tRFCab; + table->dram_timings.rl = RL - 10; + table->emc_mrw2 = 0x8802003F; + table->emc_cfg_2 = 0x11083D; } void MemMtcTableAutoAdjust(MarikoMtcTable *table) { @@ -429,7 +419,7 @@ namespace ams::ldr::oc::pcv::mariko TABLE->shadow_regs_ca_train.PARAM = VALUE; \ TABLE->shadow_regs_rdwr_train.PARAM = VALUE; - #define GET_CYCLE(PARAM) ((u32)((double)(PARAM) / tCK_avg)) + #define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) WRITE_PARAM_ALL_REG(table, emc_rc, 0x60); // WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab)); @@ -483,28 +473,25 @@ namespace ams::ldr::oc::pcv::mariko // WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rfcpb, CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV)) } - void MemMtcPllmbDivisor(MarikoMtcTable *table) - { + void MemMtcPllmbDivisor(MarikoMtcTable *table) { // Calculate DIVM and DIVN (clock divisors) // Common PLL oscillator is 38.4 MHz // PLLMB_OUT = 38.4 MHz / PLLLMB_DIVM * PLLMB_DIVN - typedef struct - { + typedef struct { u8 numerator : 4; u8 denominator : 4; } pllmb_div; constexpr pllmb_div div[] = { - {3, 4}, {2, 3}, {1, 2}, {1, 3}, {1, 4}, {0, 2}}; + {3, 4}, {2, 3}, {1, 2}, {1, 3}, {1, 4}, {0, 2} + }; constexpr u32 pll_osc_in = 38'400; u32 divm{}, divn{}; const u32 remainder = C.marikoEmcMaxClock % pll_osc_in; - for (const auto &index : div) - { + for (const auto &index : div) { // Round down - if (remainder >= pll_osc_in * index.numerator / index.denominator) - { + if (remainder >= pll_osc_in * index.numerator / index.denominator) { divm = index.denominator; divn = C.marikoEmcMaxClock / pll_osc_in * divm + index.numerator; break; @@ -515,15 +502,13 @@ namespace ams::ldr::oc::pcv::mariko table->pllmb_divn = divn; } - Result MemFreqMtcTable(u32 *ptr) - { + Result MemFreqMtcTable(u32 *ptr) { u32 khz_list[] = {1600000, 1331200, 204000}; u32 khz_list_size = sizeof(khz_list) / sizeof(u32); // Generate list for mtc table pointers MarikoMtcTable *table_list[khz_list_size]; - for (u32 i = 0; i < khz_list_size; i++) - { + 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()); @@ -546,6 +531,7 @@ namespace ams::ldr::oc::pcv::mariko } else { MemMtcTableAutoAdjustBaseLatency(table_max); } + MemMtcPllmbDivisor(table_max); // Overwrite 13312000 table with unmodified 1600000 table copied back std::memcpy(reinterpret_cast(table_alt), reinterpret_cast(tmp), sizeof(MarikoMtcTable)); @@ -553,17 +539,10 @@ namespace ams::ldr::oc::pcv::mariko delete tmp; PATCH_OFFSET(ptr, C.marikoEmcMaxClock); - - // Handle customize table replacement - // if (C.mtcConf == CUSTOMIZED_ALL) { - // MemMtcCustomizeTable(table_list[0], reinterpret_cast(reinterpret_cast(C.marikoMtcTable))); - // } - R_SUCCEED(); } - Result MemFreqDvbTable(u32 *ptr) - { + Result MemFreqDvbTable(u32 *ptr) { emc_dvb_dvfs_table_t *default_end = reinterpret_cast(ptr); emc_dvb_dvfs_table_t *new_start = default_end + 1; @@ -579,45 +558,25 @@ namespace ams::ldr::oc::pcv::mariko #define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000), - if (C.marikoEmcMaxClock < 1862400) - { + 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, - }}; + } 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, - }}; + } 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) - { + } 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) - { + } 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) - { + 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 - { + } 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)); } @@ -629,8 +588,7 @@ namespace ams::ldr::oc::pcv::mariko R_SUCCEED(); } - Result MemFreqMax(u32 *ptr) - { + Result MemFreqMax(u32 *ptr) { if (C.marikoEmcMaxClock <= EmcClkOSLimit) R_SKIP(); @@ -638,10 +596,8 @@ namespace ams::ldr::oc::pcv::mariko R_SUCCEED(); } - Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) - { - struct - { + Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) { + struct { u8 reg; u8 val; } __attribute__((packed)) cmd; @@ -658,15 +614,13 @@ namespace ams::ldr::oc::pcv::mariko return res; } - Result EmcVddqVolt(u32 *ptr) - { + Result EmcVddqVolt(u32 *ptr) { regulator *entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(regulator, type_2_3.default_uv)); constexpr u32 uv_step = 5'000; constexpr u32 uv_min = 250'000; - auto validator = [entry]() - { + auto validator = [entry]() { 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()); @@ -692,8 +646,7 @@ namespace ams::ldr::oc::pcv::mariko R_SUCCEED(); } - void Patch(uintptr_t mapped_nso, size_t nso_size) - { + void Patch(uintptr_t mapped_nso, size_t nso_size) { u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq); @@ -715,23 +668,20 @@ namespace ams::ldr::oc::pcv::mariko {"GPU Vmax", &GpuVmax, 0, nullptr, gpuVmax}, }; - for (uintptr_t ptr = mapped_nso; - ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); - ptr += sizeof(u32)) - { + for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) { u32 *ptr32 = reinterpret_cast(ptr); - for (auto &entry : patches) - { - if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) + for (auto &entry : patches) { + if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) { break; + } } } - for (auto &entry : patches) - { + for (auto &entry : patches) { LOGGING("%s Count: %zu", entry.description, entry.patched_count); - if (R_FAILED(entry.CheckResult())) + if (R_FAILED(entry.CheckResult())) { CRASH(entry.description); + } } }