From a6422e0b1f05a9384bac33c3b47cde4840eab896 Mon Sep 17 00:00:00 2001 From: Lightos1 Date: Wed, 1 Oct 2025 15:31:08 +0200 Subject: [PATCH] Adding auto_adj for mariko, timing fix for erista, added mariko dvb to customize.cpp --- .../loader/source/oc/customize.cpp | 8 +- .../loader/source/oc/customize.hpp | 10 +- .../loader/source/oc/mtc_timing_value.hpp | 132 +- .../loader/source/oc/pcv/pcv_erista.cpp | 13 +- .../loader/source/oc/pcv/pcv_mariko.cpp | 1090 +++++++---------- 5 files changed, 492 insertions(+), 761 deletions(-) diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 55e43b7e..11c7bcb4 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -85,7 +85,7 @@ volatile CustomizeTable C = { * Not enabled by default. * This will not work without sys-clk-OC. */ -.marikoEmcVddqVolt = 0, +.marikoEmcVddqVolt = 600000, .marikoCpuUV = 0, @@ -105,6 +105,8 @@ volatile CustomizeTable C = { .commonGpuVoltOffset = 0, +.marikoEmcDvbShift = 0, + .t1_tRCD = 0, .t2_tRP = 0, .t3_tRAS = 0, @@ -114,7 +116,7 @@ volatile CustomizeTable C = { .t7_tWTR = 0, .t8_tREFI = 0, -.mem_burst_latency = 2, +.mem_burst_latency = 0, // NOTE: These tables should NOT BE USED and are only here as placeholders. Always try and find your own optimal tables. // Ensure the voltages actually increase or stay the sameot @@ -386,7 +388,7 @@ volatile CustomizeTable C = { { 768000, { }, { 1191317, 8144, -940, 808, -21583, 226 } }, { 844800, { }, { 1233208, 8144, -940, 808, -21583, 226 } }, { 921600, { }, { 1275100, 8144, -940, 808, -21583, 226 } }, - { 998400, { }, { 1316991, 8144, -940, 808, -21583, 226 } }, // UNSAFE + { 998400, { }, { 1316991, 8144, -940, 808, -21583, 226 } }, // POTENTIALLY UNSAFE { 1075200, { }, { 1358882, 8144, -940, 808, -21583, 226 } }, // UNSAFE // { 1152000, { }, { 1400773, 8144, -940, 808, -21583, 226 } }, // DANGEROUS // { 1228800, { }, { 1440773, 8144, -940, 808, -21583, 226 } }, // DANGEROUS diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index a68ed15b..5aae100e 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -30,12 +30,7 @@ #include "mtc_timing_table.hpp" enum MtcConfig: u32 { - AUTO_ADJ_ALL = 0, - CUSTOM_ADJ_ALL = 1, - NO_ADJ_ALL = 2, - - CUSTOMIZED_ALL = 4, - AUTO_ADJ = 5, + AUTO_ADJ = 0, }; using CustomizeCpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit]; @@ -49,8 +44,7 @@ typedef struct CustomizeTable { u8 cust[4] = {'C', 'U', 'S', 'T'}; u32 custRev = CUST_REV; - u32 mtcConfErista = AUTO_ADJ; - u32 mtcConfMariko = AUTO_ADJ_ALL; // TODO: Fix mariko and merge into mtcConf + u32 mtcConf = AUTO_ADJ; 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 8f0ca68d..a14010c3 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp @@ -58,6 +58,12 @@ // const u32 TIMING_PRESET_SIX = C.ramTimingPresetSix; // const u32 TIMING_PRESET_SEVEN = C.ramTimingPresetSeven; + + // Write Latency + const u32 WL = 14 + C.mem_burst_latency; + // Read Latency + const u32 RL = 32 - C.mem_burst_latency; + // Burst Length const u32 BL = 16; @@ -79,21 +85,9 @@ // tRC (ACTIVATE-ACTIVATE command period same bank) in ns const u32 tRC = tRPpb + tRAS; + const u32 tPPD = 4; + const u32 tRTW = !C.t6_tRTW ? 10 : tWTR_values[C.t6_tRTW-1]; - // DQS output access time from CK_t/CK_c - const double tDQSCK_min = 1.5; // TODO: Fix/remove for mariko if needed - // DQS output access time from CK_t/CK_c - const double tDQSCK_max = 3.5; // TODO: Fix/remove for mariko if needed - // Write preamble (tCK) - const double tWPRE = 1.8; // TODO: Fix/remove for mariko if needed - // Read postamble (tCK) - const double tRPST = 0.4; // TODO: Fix/remove for mariko if needed - // WRITE command to first DQS transition(max) (tCK) - const double tDQSS_max = 1.25; // TODO: Fix/remove for mariko if needed - // DQ-to-DQS offset(max) (ns) - const double tDQS2DQ_max = 0.8; // TODO: Fix/remove for mariko if needed - // DQS_t, DQS_c to DQ skew total, per group, per access (DBI Disabled) - const double tDQSQ = 0.18; // TODO: Fix/remove for mariko if needed // Write-to-Read delay const u32 tWTR = !C.t7_tWTR ? 10 : tWTR_values[C.t7_tWTR-1]; @@ -104,9 +98,6 @@ // write recovery time const u32 tWR = !TIMING_PRESET_THREE ? 18 : tWR_values[TIMING_PRESET_THREE-1]; - // Read to refresh delay - const u32 tR2REF = tRTP + tRPpb; - // tRCD (RAS-CAS delay) in ns const u32 tRCD = !C.t1_tRCD ? 18 : tRCD_values[C.t1_tRCD-1]; @@ -115,124 +106,35 @@ // tREFpb (average refresh interval per bank) in ns for 8Gb density const u32 tREFpb = !C.t8_tREFI ? 488 : tREFpb_values[C.t8_tREFI-1]; - // tREFab (average refresh interval all 8 banks) in ns for 8Gb density - // const u32 tREFab = tREFpb * 8; - // tPDEX2WR, tPDEX2RD (timing delay from exiting powerdown mode to a write/read command) in ns - // const u32 tPDEX2 = 10; // Exit power-down to next valid command delay - const double tXP = 10; - - // Delay from valid command to CKE input LOW in ns - const double tCMDCKE = 1.75; // TODO: Fix/remove for mariko if needed - - // tACT2PDEN (timing delay from an activate, MRS or EMRS command to power-down entry) in ns - // Valid clock and CS requirement after CKE input LOW after MRW command - const u32 tMRWCKEL = 14; // TODO: Fix/remove for mariko if needed - - // Valid CS requirement after CKE input LOW - const double tCKELCS = 5; // TODO: Fix/remove for mariko if needed - - // Valid CS requirement before CKE input HIGH - const double tCSCKEH = 1.75; // TODO: Fix/remove for mariko if needed + const double tXP = 7.5; // tXSR (SELF REFRESH exit to next valid command delay) in ns const double tXSR = tRFCab + 7.5; - // tCKE (minimum pulse width(HIGH and LOW pulse width)) in ns - const double tCKE = 7.5; // TODO: Fix/remove for mariko if needed - // Minimum self refresh time (entry to exit) const u32 tSR = 15; // tFAW (Four-bank Activate Window) in ns const u32 tFAW = 40;// !TIMING_PRESET_TWO ? 40 : tFAW_values[TIMING_PRESET_TWO-1]; TOGO - // Valid Clock requirement before CKE Input HIGH in ns - const double tCKCKEH = 1.75; // TODO: Fix/remove for mariko if needed + // #_of_rows per die for 8Gb density + const u32 numOfRows = 131072; - // p78 The first valid data is available RL × t CK + t DQSCK + t DQSQ - //const u32 QUSE = RL + CEIL(tDQSCK_min/tCK_avg + tDQSQ); + // {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%] + // emc_clk_period = dram_clk / 2; + // 1600 MHz: 5894, but N' set to 6176 (~4.8% margin) + const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.marikoEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4); + const u32 REFBW = MIN((u32)65536, REFRESH+64); namespace pcv::erista { // tCK_avg (average clock period) in ns const double tCK_avg = 1000'000. / C.eristaEmcMaxClock; - - // Write Latency - const u32 WL = 14 + C.mem_burst_latency; - // Read Latency - const u32 RL = 32 - C.mem_burst_latency; - - // minimum number of cycles from any read command to any write command, irrespective of bank - // const u32 R2W = CEIL (RL + CEIL(tDQSCK_max/tCK_avg) + BL/2 - WL + tWPRE + FLOOR(tRPST)) + 6; - - // Delay Time From WRITE-to-READ - // const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg) - 6; - - // write-to-precharge time for commands to the same bank in cycles - // const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg) - 8; - - // #_of_rows per die for 16Gb density - const u32 numOfRows = 131072; - // {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%] - // emc_clk_period = dram_clk / 2; - // 1600 MHz: 5894, but N' set to 6176 (~4.8% margin) - const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.eristaEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4); - const u32 REFBW = MIN((u32)65536, REFRESH+64); - - // Write With Auto Precharge to to Power-Down Entry - // const u32 WTPDEN = WTP + 1 + CEIL(tDQSS_max/tCK_avg) + CEIL(tDQS2DQ_max/tCK_avg) + 6; - - // Additional time after t XP hasexpired until the MRR commandmay be issued - // const double tMRRI = tRCD + 3 * tCK_avg; - - // tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns - // const double tPDEX2MRR = tXP + tMRRI; } + namespace pcv::mariko { // tCK_avg (average clock period) in ns const double tCK_avg = 1000'000. / C.marikoEmcMaxClock; - // Write Latency - const u32 WL = 14 + C.mem_burst_latency; - // Read Latency - const u32 RL = 32 - C.mem_burst_latency; - - // minimum number of cycles from any read command to any write command, irrespective of bank - const u32 R2W = WL + BL/2 + 1 + CEIL(tRTW/tCK_avg); - - // Delay Time From WRITE-to-READ - const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg); - - // write-to-precharge time for commands to the same bank in cycles - const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg); - - // Read-To-MRW delay - const u32 RTM = RL + BL/2 + CEIL(tDQSCK_max/tCK_avg) + FLOOR(tRPST) + CEIL(7.5/tCK_avg); - - // Write-To-MRW/MRR delay - const u32 WTM = WL + 1 + BL/2 + CEIL(7.5/tCK_avg); - - // Read With AP-To-MRW/MRR delay - const u32 RATM = RTM + CEIL(tRTP/tCK_avg) - 8; - - // Write With AP-To-MRW/MRR delay - const u32 WATM = WTM + CEIL(tWR/tCK_avg); - - // #_of_rows per die for 8Gb density - const u32 numOfRows = 65536; - // {REFRESH, REFRESH_LO} = max[(tREF/#_of_rows) / (emc_clk_period) - 64, (tREF/#_of_rows) / (emc_clk_period) * 97%] - // emc_clk_period = dram_clk / 2; - // 1600 MHz: 5894, but N' set to 6176 (~4.8% margin) - const u32 REFRESH = MIN((u32)65472, u32(std::ceil((double(tREFpb) * C.marikoEmcMaxClock / numOfRows * 1.048 / 2 - 64))) / 4 * 4); - const u32 REFBW = MIN((u32)65536, REFRESH+64); - - // Write With Auto Precharge to to Power-Down Entry - const u32 WTPDEN = WTP + 1 + CEIL(tDQSS_max/tCK_avg) + CEIL(tDQS2DQ_max/tCK_avg) + 6; - - // Additional time after t XP hasexpired until the MRR commandmay be issued - const double tMRRI = tRCD + 3 * tCK_avg; - - // tPDEX2MRR (timing delay from exiting powerdown mode to MRR command) in ns - const double tPDEX2MRR = tXP + tMRRI; } } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp index 16bb85b8..5ecd108a 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp @@ -160,7 +160,7 @@ } void MemMtcTableAutoAdjust(EristaMtcTable *table) { - if (C.mtcConfErista != AUTO_ADJ) + if (C.mtcConf != AUTO_ADJ) return; #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \ @@ -190,6 +190,11 @@ 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_trefbw, REFBW); + WRITE_PARAM_ALL_REG(table, emc_tcke, 0x68); + + // WRITE_PARAM_ALL_REG(table, emc_r2w, R2W); + // WRITE_PARAM_ALL_REG(table, emc_w2r, W2R); + // WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); #define WRITE_PARAM_BURST_MC_REG(TABLE, PARAM, VALUE) TABLE->burst_mc_regs.PARAM = VALUE; @@ -203,10 +208,10 @@ table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1; //table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV); //table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV); - // table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / MC_ARB_DIV) - 1 + MC_ARB_SFA; - // table->burst_mc_regs.mc_emem_arb_timing_w2w = CEIL(table->burst_regs.emc_wext / MC_ARB_DIV) - 1 + MC_ARB_SFA; + // table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / MC_ARB_DIV) - 1 + MC_ARB_SFA; + // table->burst_mc_regs.mc_emem_arb_timing_w2w = CEIL(table->burst_regs.emc_wext / MC_ARB_DIV) - 1 + MC_ARB_SFA; // table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA; - // table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA; + // table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA; table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV); // table->burst_mc_regs.mc_emem_arb_timing_ccdmw = CEIL(tCCDMW / MC_ARB_DIV) -1 + MC_ARB_SFA; } 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 471577ce..458a64ee 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -39,653 +39,481 @@ Result GpuVmax(u32 *ptr) R_SUCCEED(); } - Result CpuFreqVdd(u32* ptr) { - dvfs_rail* entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(dvfs_rail, freq)); +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()); + 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.marikoCpuUV) { - if(!C.enableMarikoCpuUnsafeFreqs) { - PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq); - } else { - PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTableUnsafeFreqs)->freq); - } - } else { - PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); - } + if (C.marikoCpuUV) { + if(!C.enableMarikoCpuUnsafeFreqs) { + PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq); + } else { + PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTableUnsafeFreqs)->freq); + } + } else { + PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); + } - R_SUCCEED(); - } + R_SUCCEED(); +} - Result CpuVoltRange(u32* ptr) { - u32 min_volt_got = *(ptr - 1); - for (const auto& mv : CpuMinVolts) { - if (min_volt_got != mv) - continue; +Result CpuVoltRange(u32* ptr) { + u32 min_volt_got = *(ptr - 1); + for (const auto& mv : CpuMinVolts) { + if (min_volt_got != mv) + continue; - if (!C.marikoCpuMaxVolt) - R_SKIP(); + if (!C.marikoCpuMaxVolt) + R_SKIP(); - PATCH_OFFSET(ptr, C.marikoCpuMaxVolt); - // Patch vmin for slt - if (C.marikoCpuUV) { - if (*(ptr-5) == 620) { - PATCH_OFFSET((ptr-5), C.marikoCpuVmin); - } - if (*(ptr-1) == 620) { - PATCH_OFFSET((ptr-1), 600); - } + PATCH_OFFSET(ptr, C.marikoCpuMaxVolt); + // Patch vmin for slt + if (C.marikoCpuUV) { + if (*(ptr-5) == 620) { + PATCH_OFFSET((ptr-5), C.marikoCpuVmin); + } + if (*(ptr-1) == 620) { + PATCH_OFFSET((ptr-1), 600); + } - } - R_SUCCEED(); - } - R_THROW(ldr::ResultInvalidCpuMinVolt()); - } + } + R_SUCCEED(); + } + R_THROW(ldr::ResultInvalidCpuMinVolt()); +} - Result CpuVoltDfll(u32* ptr) { - cvb_cpu_dfll_data *entry = reinterpret_cast(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) { + 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) { + case 0: + break; + case 1: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FF88); //process_id 0 // EOS UV1 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x00000000); + break; + case 2: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FF90); /// EOS Uv2 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x00000000); + break; + case 3: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FF98); // EOS UV3 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x00000000); + break; + case 4: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFA0); // EOS Uv4 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x00000000); + break; + case 5: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); // EOS UV5 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x022217FF); + break; + case 6: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); // EOS UV6 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x024417FF); + break; + case 7: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); // EOS UV6 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x026617FF); + break; + case 8: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); // EOS UV6 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x028817FF); + break; + default: + break; + } + R_SUCCEED(); +} + +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]))) + R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + + // Both instructions should operate on the same register + u8 rd = asm_get_rd(ins1); + if (rd != asm_get_rd(ins2)) + R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + + u32 max_clock; + switch(C.marikoGpuUV) { case 0: + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; break; case 1: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FF88); //process_id 0 // EOS UV1 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x00000000); + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq; break; case 2: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FF90); /// EOS Uv2 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x00000000); + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq; break; case 3: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FF98); // EOS UV3 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x00000000); - break; - case 4: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFA0); // EOS Uv4 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x00000000); - break; - case 5: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); // EOS UV5 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x022217FF); - break; - case 6: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); // EOS UV6 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x024417FF); - break; - case 7: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); // EOS UV6 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x026617FF); - break; - case 8: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); // EOS UV6 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x028817FF); + if(C.enableMarikoGpuUnsafeFreqs) + { + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableUv3UnsafeFreqs)->freq; + } + else + { + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; + } break; default: + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; break; - } - R_SUCCEED(); - } - - 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]))) - R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); - - // Both instructions should operate on the same register - u8 rd = asm_get_rd(ins1); - if (rd != asm_get_rd(ins2)) - R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); - - u32 max_clock; - switch(C.marikoGpuUV) { - case 0: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; - break; - case 1: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq; - break; - case 2: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq; - break; - case 3: - if(C.enableMarikoGpuUnsafeFreqs) - { - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableUv3UnsafeFreqs)->freq; - } - else - { - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; - } - break; - default: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; - break; - } - 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) - }; - PATCH_OFFSET(ptr32, asm_patch[0]); - PATCH_OFFSET(ptr32 + 1, asm_patch[1]); - - R_SUCCEED(); - } - - 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++) { - R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry()); - } - - // Double the max clk simply - u32 max_clk = entry->freq * 2; - entry->freq = max_clk; - R_SUCCEED(); - } - - void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) { - /* Official Tegra X1 TRM, sign up for nvidia developer program (free) to download: - * https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual - * Section 18.11: MC Registers - * - * Retail Mariko: 200FBGA 16Gb DDP LPDDR4X SDRAM x 2 - * x16/Ch, 1Ch/die, Double-die, 2Ch, 1CS(rank), 8Gb density per die - * 64Mb x 16DQ x 8banks x 2channels = 2048MB (x32DQ) per package - * - * Devkit Mariko: 200FBGA 32Gb DDP LPDDR4X SDRAM x 2 - * x16/Ch, 1Ch/die, Quad-die, 2Ch, 2CS(rank), 8Gb density per die - * X1+ EMC can R/W to both ranks at the same time, resulting in doubled DQ - * 64Mb x 32DQ x 8banks x 2channels = 4096MB (x64DQ) per package - * - * If you have access to LPDDR4(X) specs or datasheets (from manufacturers or Google), - * you'd better calculate timings yourself rather than relying on following algorithm. - */ - - if (C.mtcConfMariko != AUTO_ADJ_ALL) - return; - - // scale with linear interpolation - #define ADJUST_PROP(TARGET, REF) \ - (u32)(CEIL((REF + ((C.marikoEmcMaxClock-EmcClkOSAlt)*(TARGET-REF))/(EmcClkOSLimit-EmcClkOSAlt)))) - - #define ADJUST_PARAM(TARGET, REF) \ - TARGET = ADJUST_PROP(TARGET, REF); - - #define ADJUST_PARAM_TABLE(TABLE, PARAM, REF) ADJUST_PARAM(TABLE->PARAM, REF->PARAM) - - // Burst Register - #define ADJUST_PARAM_ALL_REG(TABLE, PARAM, REF) \ - ADJUST_PARAM_TABLE(TABLE, burst_regs.PARAM, REF) \ - ADJUST_PARAM_TABLE(TABLE, shadow_regs_ca_train.PARAM, REF) \ - ADJUST_PARAM_TABLE(TABLE, shadow_regs_rdwr_train.PARAM, REF) - - #define WRITE_PARAM_BURST_REG(TABLE, PARAM, VALUE) TABLE->burst_regs.PARAM = VALUE; - #define WRITE_PARAM_CA_TRAIN_REG(TABLE, PARAM, VALUE) TABLE->shadow_regs_ca_train.PARAM = VALUE; - #define WRITE_PARAM_RDWR_TRAIN_REG(TABLE, PARAM, VALUE) TABLE->shadow_regs_rdwr_train.PARAM = VALUE; - - #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \ - WRITE_PARAM_BURST_REG(TABLE, PARAM, VALUE) \ - WRITE_PARAM_CA_TRAIN_REG(TABLE, PARAM, VALUE) \ - WRITE_PARAM_RDWR_TRAIN_REG(TABLE, PARAM, VALUE) - - #define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) - - WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC)); - WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab)); - WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb)); - WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS)); - WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb)); - WRITE_PARAM_ALL_REG(table, emc_r2w, R2W); - WRITE_PARAM_ALL_REG(table, emc_w2r, W2R); - WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP)); - WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); - WRITE_PARAM_ALL_REG(table, emc_trtm, RTM); - WRITE_PARAM_ALL_REG(table, emc_twtm, WTM); - WRITE_PARAM_ALL_REG(table, emc_tratm, RATM); - WRITE_PARAM_ALL_REG(table, emc_twatm, WATM); - //WRITE_PARAM_ALL_REG(table, emc_tr2ref, GET_CYCLE_CEIL(tR2REF)); - 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_rrd, GET_CYCLE_CEIL(tRRD)); - WRITE_PARAM_ALL_REG(table, emc_rext, 26); - WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH); - WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4); - WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE_CEIL(tXP)); - WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE_CEIL(tXP)); - WRITE_PARAM_ALL_REG(table, emc_pchg2pden, GET_CYCLE_CEIL(tCMDCKE)); - WRITE_PARAM_ALL_REG(table, emc_act2pden, GET_CYCLE_CEIL(tMRWCKEL)); - WRITE_PARAM_ALL_REG(table, emc_ar2pden, GET_CYCLE_CEIL(tCMDCKE)); - WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN); - WRITE_PARAM_ALL_REG(table, emc_cke2pden, GET_CYCLE_CEIL(tCKELCS)); - //WRITE_PARAM_ALL_REG(table, emc_pdex2cke, GET_CYCLE_CEIL(tCSCKEH)); - WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR)); - WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); - WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); - WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tCKE) + 1); - WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tSR)); - WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tCKE)); - 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_tclkstable, GET_CYCLE_CEIL(tCKCKEH)); - WRITE_PARAM_ALL_REG(table, emc_tclkstop, GET_CYCLE_CEIL(tCKE) + 8); - WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW); - - ADJUST_PARAM_ALL_REG(table, emc_dyn_self_ref_control, ref); - - - #define CLEAR_BIT(BITS, HIGH, LOW) \ - BITS = BITS & ~( ((1u << HIGH) << 1u) - (1u << LOW) ); - - #define ADJUST(TARGET) (u32)CEIL(TARGET * (C.marikoEmcMaxClock / EmcClkOSLimit)) - #define ADJUST_INVERSE(TARGET) (u32)(TARGET * (EmcClkOSLimit / 1000) / (C.marikoEmcMaxClock / 1000)) - - // Burst MC Regs - #define WRITE_PARAM_BURST_MC_REG(TABLE, PARAM, VALUE) TABLE->burst_mc_regs.PARAM = VALUE; - - constexpr u32 MC_ARB_DIV = 4; - constexpr u32 MC_ARB_SFA = 2; - - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_cfg, C.marikoEmcMaxClock / (33.3 * 1000) / MC_ARB_DIV); //CYCLES_PER_UPDATE: The number of mcclk cycles per deadline timer update - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rcd, CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rp, CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rc, CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_ras, CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_faw, CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rrd, CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rap2pre, CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV)) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_wap2pre, CEIL((WTP) / MC_ARB_DIV)) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_r2r, CEIL(table->burst_regs.emc_rext / MC_ARB_DIV) - 1 + MC_ARB_SFA) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_r2w, CEIL((R2W) / MC_ARB_DIV) - 1 + MC_ARB_SFA) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_w2r, CEIL((W2R) / MC_ARB_DIV) - 1 + MC_ARB_SFA) - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rfcpb, CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV)) - - u32 DA_TURNS = 0; - DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_r2w / 2) << 16; //R2W TURN - DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24; //W2R TURN - WRITE_PARAM_BURST_MC_REG(table, 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); //RC COVER - DA_COVERS |= (R_COVER << 8); //RCD_R COVER - DA_COVERS |= (W_COVER << 16); //RCD_W COVER - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_covers, DA_COVERS); - - CLEAR_BIT(table->burst_mc_regs.mc_emem_arb_misc0, 7, 0); - table->burst_mc_regs.mc_emem_arb_misc0 |= u8(table->burst_mc_regs.mc_emem_arb_timing_rc + 1); //BC2AA_HOLDOFF - CLEAR_BIT(table->burst_mc_regs.mc_emem_arb_misc0, 14, 8); - table->burst_mc_regs.mc_emem_arb_misc0 |= u8((ADJUST(0x24) << 8)); //PRIORITY_INVERSION_THRESHOLD - CLEAR_BIT(table->burst_mc_regs.mc_emem_arb_misc0, 20, 16); - table->burst_mc_regs.mc_emem_arb_misc0 |= u8((ADJUST(12) << 16)); //PRIORITY_INVERSION_ISO_THRESHOLD - - // updown registers - #define ADJUST_PARAM_LA_SCALE_REG(TABLE, PARAM) \ - TABLE->la_scale_regs.PARAM = ADJUST(TABLE->la_scale_regs.PARAM) - - #define ADJUST_PARAM_LA_SCALE_REG_HI(TABLE, PARAM, VALUE) \ - CLEAR_BIT(TABLE->la_scale_regs.PARAM, 23, 16) \ - TABLE->la_scale_regs.PARAM |= VALUE << 16 - - #define ADJUST_PARAM_LA_SCALE_REG_LO(TABLE, PARAM, VALUE) \ - CLEAR_BIT(TABLE->la_scale_regs.PARAM, 7, 0) \ - TABLE->la_scale_regs.PARAM |= VALUE - - u8 LA = ADJUST_INVERSE(128); //0x80 - ADJUST_PARAM_LA_SCALE_REG(table, mc_mll_mpcorer_ptsa_rate); //208 - ADJUST_PARAM_LA_SCALE_REG(table, mc_ptsa_grant_decrement); //4611 - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_xusb_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_xusb_1, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_tsec_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_sdmmca_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_sdmmcaa_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_sdmmc_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_sdmmcab_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_sdmmc_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_sdmmcab_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_ppcs_1, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_mpcore_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_avpc_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_gpu_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_gpu2_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_nvenc_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_nvdec_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_vic_0, LA); - ADJUST_PARAM_LA_SCALE_REG_HI(table, mc_latency_allowance_isp2_1, LA); - - ADJUST_PARAM_LA_SCALE_REG_LO(table, mc_latency_allowance_hc_0, ADJUST_INVERSE(0x16)); - ADJUST_PARAM_LA_SCALE_REG_LO(table, mc_latency_allowance_hc_1, LA); - ADJUST_PARAM_LA_SCALE_REG_LO(table, mc_latency_allowance_gpu_0, ADJUST_INVERSE(0x19)); - ADJUST_PARAM_LA_SCALE_REG_LO(table, mc_latency_allowance_gpu2_0, ADJUST_INVERSE(0x19)); - ADJUST_PARAM_LA_SCALE_REG_LO(table, mc_latency_allowance_vic_0, ADJUST_INVERSE(0x1d)); - ADJUST_PARAM_LA_SCALE_REG_LO(table, mc_latency_allowance_vi2_0, LA); - ADJUST_PARAM_LA_SCALE_REG_LO(table, mc_latency_allowance_isp2_1, LA); - - //Spread Spectrum Control - table->pllm_ss_ctrl1 = 0x0b55fe01; - table->pllm_ss_ctrl2 = 0x10170b55; - table->pllmb_ss_ctrl1 = 0x0b55fe01; - table->pllmb_ss_ctrl2 = 0x10170b55; - - table->dram_timings.t_rp = tRPpb; - table->dram_timings.t_rfc = tRFCab; - //table->dram_timings.rl = 32; - - table->emc_cfg_2 = 0x0011083d; - } - - void MemMtcTableCustomAdjust(MarikoMtcTable* table) { - if (C.mtcConfMariko != CUSTOM_ADJ_ALL) - return; - - constexpr u32 MC_ARB_DIV = 4; - constexpr u32 MC_ARB_SFA = 2; - - 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_rp, GET_CYCLE_CEIL(tRPpb)); - WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab)); - 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_pdex2mrr,GET_CYCLE_CEIL(tPDEX2MRR)); - - 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_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1; - table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA; - table->burst_mc_regs.mc_emem_arb_timing_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2; - - - WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW)); - WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD)); - - table->burst_mc_regs.mc_emem_arb_timing_faw = CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1; - table->burst_mc_regs.mc_emem_arb_timing_rrd = CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1; - - WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP)); - WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); - WRITE_PARAM_ALL_REG(table, emc_tratm, RATM); - WRITE_PARAM_ALL_REG(table, emc_twatm, WATM); - WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN); - - table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV); - table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV); - - WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab)); - WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb)); - WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); - WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); - - table->burst_mc_regs.mc_emem_arb_timing_rfcpb = CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV); - - WRITE_PARAM_ALL_REG(table, emc_w2r, W2R); - - table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / MC_ARB_DIV) - 1 + MC_ARB_SFA; - - WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH); - WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4); - WRITE_PARAM_ALL_REG(table, emc_trefbw, REFBW); - - WRITE_PARAM_ALL_REG(table, emc_r2w, R2W); - WRITE_PARAM_ALL_REG(table, emc_w2r, W2R); - WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); - WRITE_PARAM_ALL_REG(table, emc_trtm, RTM); - WRITE_PARAM_ALL_REG(table, emc_twtm, WTM); - WRITE_PARAM_ALL_REG(table, emc_tratm, RATM); - WRITE_PARAM_ALL_REG(table, emc_twatm, WATM); - WRITE_PARAM_ALL_REG(table, emc_rw2pden, WTPDEN); - - table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(WTP / MC_ARB_DIV); - table->burst_mc_regs.mc_emem_arb_timing_r2w = CEIL(R2W / MC_ARB_DIV) - 1 + MC_ARB_SFA; - table->burst_mc_regs.mc_emem_arb_timing_w2r = CEIL(W2R / 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; //R2W TURN - DA_TURNS |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24; //W2R TURN - WRITE_PARAM_BURST_MC_REG(table, 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); //RC COVER - DA_COVERS |= (R_COVER << 8); //RCD_R COVER - DA_COVERS |= (W_COVER << 16); //RCD_W COVER - WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_da_covers, DA_COVERS); - } - - 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 { - u8 numerator : 4; - u8 denominator : 4; - } pllmb_div; - - constexpr pllmb_div div[] = { - {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) { - // Round down - if (remainder >= pll_osc_in * index.numerator / index.denominator) { - divm = index.denominator; - divn = C.marikoEmcMaxClock / pll_osc_in * divm + index.numerator; - break; - } - } - - table->pllmb_divm = divm; - table->pllmb_divn = divn; - } - - 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++) { - 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()); - } - - if (C.marikoEmcMaxClock <= EmcClkOSLimit) - R_SKIP(); - - MarikoMtcTable *table_alt = table_list[1], *table_max = table_list[0]; - MarikoMtcTable *tmp = new MarikoMtcTable; - - // Copy unmodified 1600000 table to tmp - std::memcpy(reinterpret_cast(tmp), reinterpret_cast(table_max), sizeof(MarikoMtcTable)); - // Adjust max freq mtc timing parameters with reference to 1331200 table - MemMtcTableAutoAdjust(table_max, table_alt); - MemMtcTableCustomAdjust(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)); - - 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) { - emc_dvb_dvfs_table_t* default_end = reinterpret_cast(ptr); - 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; - R_UNLESS(validated, ldr::ResultInvalidDvbTable()); - - if (C.marikoEmcMaxClock <= EmcClkOSLimit) - R_SKIP(); - - int32_t voltAdd = 25*C.marikoEmcDvbShift; - - #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) { - 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)); - } - new_start->freq = C.marikoEmcMaxClock; - /* 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 - */ - - R_SUCCEED(); - } - - Result MemFreqMax(u32* ptr) { - if (C.marikoEmcMaxClock <= EmcClkOSLimit) - R_SKIP(); - - PATCH_OFFSET(ptr, C.marikoEmcMaxClock); - R_SUCCEED(); - } - - Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) { - struct { - u8 reg; - u8 val; - } __attribute__((packed)) cmd; - - I2cSession _session; - Result res = i2cOpenSession(&_session, dev); - if (R_FAILED(res)) - return res; - - cmd.reg = reg; - cmd.val = val; - res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); - i2csessionClose(&_session); - return res; - } - - 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]() { - 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_SUCCEED(); - }; - - R_TRY(validator()); - - u32 emc_uv = C.marikoEmcVddqVolt; - 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); - - i2cInitialize(); - I2cSet_U8(I2cDevice_Max77812_2, 0x25, (emc_uv - uv_min) / uv_step); - i2cExit(); - - R_SUCCEED(); - } - - void Patch(uintptr_t mapped_nso, size_t nso_size) { - 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 Limit", &CpuVoltRange, 13, nullptr, CpuVoltOfficial }, - { "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0x0000FFCF }, - { "GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq }, - { "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, - { "GPU Freq PLL", &GpuFreqPllLimit, 1, 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 }, - { "GPU Vmin", &GpuVmin, 0, nullptr, gpuVmin}, - { "GPU Vmax", &GpuVmax, 0, nullptr, gpuVmax}, - }; - - 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))) - break; - } - } - - for (auto& entry : patches) { - LOGGING("%s Count: %zu", entry.description, entry.patched_count); - if (R_FAILED(entry.CheckResult())) - CRASH(entry.description); - } - } - - } + } + 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) + }; + PATCH_OFFSET(ptr32, asm_patch[0]); + PATCH_OFFSET(ptr32 + 1, asm_patch[1]); + + R_SUCCEED(); +} + +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++) { + R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry()); + } + + // Double the max clk simply + u32 max_clk = entry->freq * 2; + entry->freq = max_clk; + 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 MemMtcTableAutoAdjust(MarikoMtcTable* table) { + /* Official Tegra X1 TRM, sign up for nvidia developer program (free) to download: + * https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual + * Section 18.11: MC Registers + * + * Retail Mariko: 200FBGA 16Gb DDP LPDDR4X SDRAM x 2 + * x16/Ch, 1Ch/die, Double-die, 2Ch, 1CS(rank), 8Gb density per die + * 64Mb x 16DQ x 8banks x 2channels = 2048MB (x32DQ) per package + * + * Devkit Mariko: 200FBGA 32Gb DDP LPDDR4X SDRAM x 2 + * x16/Ch, 1Ch/die, Quad-die, 2Ch, 2CS(rank), 8Gb density per die + * X1+ EMC can R/W to both ranks at the same time, resulting in doubled DQ + * 64Mb x 32DQ x 8banks x 2channels = 4096MB (x64DQ) per package + * + * If you have access to LPDDR4(X) specs or datasheets (from manufacturers or Google), + * you'd better calculate timings yourself rather than relying on following algorithm. + */ + + if (C.mtcConf != AUTO_ADJ) { + return; + } + + #define WRITE_PARAM_BURST_REG(TABLE, PARAM, VALUE) TABLE->burst_regs.PARAM = VALUE; + #define WRITE_PARAM_CA_TRAIN_REG(TABLE, PARAM, VALUE) TABLE->shadow_regs_ca_train.PARAM = VALUE; + #define WRITE_PARAM_RDWR_TRAIN_REG(TABLE, PARAM, VALUE) TABLE->shadow_regs_rdwr_train.PARAM = VALUE; + + #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \ + WRITE_PARAM_BURST_REG(TABLE, PARAM, VALUE) \ + WRITE_PARAM_CA_TRAIN_REG(TABLE, PARAM, VALUE) \ + WRITE_PARAM_RDWR_TRAIN_REG(TABLE, PARAM, VALUE) + + #define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) + + WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC)); + WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab)); + WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb)); + WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS)); + WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb)); + WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tRTP)); + 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_rrd, GET_CYCLE_CEIL(tRRD)); + WRITE_PARAM_ALL_REG(table, emc_refresh, REFRESH); + WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, REFRESH / 4); + + /* Do not patch on micron. */ + if (!IsMicron()) { + WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE_CEIL(tXP)); // NOT on micron + WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE_CEIL(tXP)); // NOT on micron + } + + WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); +// WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), (u32)0x3fe)); // TODO: Test this. + WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tSR)); + 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_trefbw, REFBW); + + // Burst MC Regs + #define WRITE_PARAM_BURST_MC_REG(TABLE, PARAM, VALUE) TABLE->burst_mc_regs.PARAM = VALUE; + + constexpr u32 MC_ARB_DIV = 4; + constexpr u32 MC_ARB_SFA = 2; + +// WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_cfg, C.marikoEmcMaxClock / (33.3 * 1000) / MC_ARB_DIV); //CYCLES_PER_UPDATE: The number of mcclk cycles per deadline timer update + WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rcd, CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2) + WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rp, CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA) + WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rc, CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1) + WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_ras, CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2) + WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_faw, CEIL(GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1) + WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rrd, CEIL(GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1) +// WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rap2pre, CEIL(GET_CYCLE_CEIL(tRTP) / MC_ARB_DIV)) +// WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_wap2pre, CEIL((WTP) / MC_ARB_DIV)) +// WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_r2r, CEIL(table->burst_regs.emc_rext / MC_ARB_DIV) - 1 + MC_ARB_SFA) +// WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_r2w, CEIL((R2W) / MC_ARB_DIV) - 1 + MC_ARB_SFA) +// WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_w2r, CEIL((W2R) / MC_ARB_DIV) - 1 + MC_ARB_SFA) + WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rfcpb, CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV)) +} + +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 { + u8 numerator : 4; + u8 denominator : 4; + } pllmb_div; + + constexpr pllmb_div div[] = { + {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) { + // Round down + if (remainder >= pll_osc_in * index.numerator / index.denominator) { + divm = index.denominator; + divn = C.marikoEmcMaxClock / pll_osc_in * divm + index.numerator; + break; + } + } + + table->pllmb_divm = divm; + table->pllmb_divn = divn; +} + +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++) { + 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()); + } + + if (C.marikoEmcMaxClock <= EmcClkOSLimit) + R_SKIP(); + + MarikoMtcTable *table_alt = table_list[1], *table_max = table_list[0]; + MarikoMtcTable *tmp = new MarikoMtcTable; + + // Copy unmodified 1600000 table to tmp + std::memcpy(reinterpret_cast(tmp), reinterpret_cast(table_max), sizeof(MarikoMtcTable)); + // Adjust max freq mtc timing parameters with reference to 1331200 table + MemMtcTableAutoAdjust(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)); + + 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) { + emc_dvb_dvfs_table_t* default_end = reinterpret_cast(ptr); + 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; + R_UNLESS(validated, ldr::ResultInvalidDvbTable()); + + if (C.marikoEmcMaxClock <= EmcClkOSLimit) + R_SKIP(); + + int32_t voltAdd = 25*C.marikoEmcDvbShift; + + #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) { + 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)); + } + new_start->freq = C.marikoEmcMaxClock; + /* 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 + */ + + R_SUCCEED(); +} + +Result MemFreqMax(u32* ptr) { + if (C.marikoEmcMaxClock <= EmcClkOSLimit) + R_SKIP(); + + PATCH_OFFSET(ptr, C.marikoEmcMaxClock); + R_SUCCEED(); +} + +Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) { + struct { + u8 reg; + u8 val; + } __attribute__((packed)) cmd; + + I2cSession _session; + Result res = i2cOpenSession(&_session, dev); + if (R_FAILED(res)) + return res; + + cmd.reg = reg; + cmd.val = val; + res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + i2csessionClose(&_session); + return res; +} + +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]() { + 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_SUCCEED(); + }; + + R_TRY(validator()); + + u32 emc_uv = C.marikoEmcVddqVolt; + 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); + + i2cInitialize(); + I2cSet_U8(I2cDevice_Max77812_2, 0x25, (emc_uv - uv_min) / uv_step); + i2cExit(); + + R_SUCCEED(); +} + +void Patch(uintptr_t mapped_nso, size_t nso_size) { + 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 Limit", &CpuVoltRange, 13, nullptr, CpuVoltOfficial }, + { "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0x0000FFCF }, + { "GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq }, + { "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, + { "GPU Freq PLL", &GpuFreqPllLimit, 1, 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 }, + { "GPU Vmin", &GpuVmin, 0, nullptr, gpuVmin}, + { "GPU Vmax", &GpuVmax, 0, nullptr, gpuVmax}, + }; + + 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))) + break; + } + } + + for (auto& entry : patches) { + LOGGING("%s Count: %zu", entry.description, entry.patched_count); + if (R_FAILED(entry.CheckResult())) + CRASH(entry.description); + } +} + +}