diff --git a/README.md b/README.md index eed6c8b5..6b3d0fa2 100644 --- a/README.md +++ b/README.md @@ -53,5 +53,5 @@ Run build.bat or cd into folder and run "python -m PyInstaller --onefile --add-d ## Credits meha for Switch-Oc-Suite
sys-clk team for sys-clk
-b0rd2auth for Ultrahand sys-clk fork
-Lightos and Sammybigio2010 for early testing
+b0rd2death for Ultrahand sys-clk fork
+Lightos and Sammybigio2011 for early testing
diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 55e43b7e..43e015d5 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -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 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..472e01ac 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp @@ -61,178 +61,104 @@ // Burst Length const u32 BL = 16; - // tRFCpb (refresh cycle time per bank) in ns for 8Gb density - const u32 tRFCpb = !C.t5_tRFC ? 140 : tRFC_values[C.t5_tRFC-1]; + // Write Latency + const u32 WL = 14 + C.mem_burst_latency; + // Read Latency + const u32 RL = 32 - C.mem_burst_latency; - // tRFCab (refresh cycle time all banks) in ns for 8Gb density - const u32 tRFCab = !C.t5_tRFC ? 280 : 2*tRFCpb; + // tRFCpb (refresh cycle time per bank) in ns for 8Gb density + const u32 tRFCpb = !C.t5_tRFC ? 140 : tRFC_values[C.t5_tRFC-1]; - // tRAS (row active time) in ns - const u32 tRAS = !C.t3_tRAS ? 42 : tRAS_values[C.t3_tRAS-1]; + // tRFCab (refresh cycle time all banks) in ns for 8Gb density + const u32 tRFCab = !C.t5_tRFC ? 280 : 2*tRFCpb; - // tRPpb (row precharge time per bank) in ns - const u32 tRPpb = !C.t2_tRP ? 18 : tRP_values[C.t2_tRP-1]; + // tRAS (row active time) in ns + const u32 tRAS = !C.t3_tRAS ? 42 : tRAS_values[C.t3_tRAS-1]; - // tRPab (row precharge time all banks) in ns - const u32 tRPab = !C.t2_tRP ? 21 : tRPpb + 3; + // tRPpb (row precharge time per bank) in ns + const u32 tRPpb = !C.t2_tRP ? 18 : tRP_values[C.t2_tRP-1]; - // tRC (ACTIVATE-ACTIVATE command period same bank) in ns - const u32 tRC = tRPpb + tRAS; + // tRPab (row precharge time all banks) in ns + const u32 tRPab = !C.t2_tRP ? 21 : tRPpb + 3; - 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 + // tRC (ACTIVATE-ACTIVATE command period same bank) in ns + const u32 tRC = tRPab + tRAS; - // Write-to-Read delay - const u32 tWTR = !C.t7_tWTR ? 10 : tWTR_values[C.t7_tWTR-1]; + const u32 tPPD = 4; - // Internal READ-to-PRE-CHARGE command delay in ns - const double tRTP = !TIMING_PRESET_THREE ? 7.5 : tRTP_values[TIMING_PRESET_THREE-1]; + const u32 tRTW = !C.t6_tRTW ? 10 : tWTR_values[C.t6_tRTW-1]; - // write recovery time - const u32 tWR = !TIMING_PRESET_THREE ? 18 : tWR_values[TIMING_PRESET_THREE-1]; + // Write-to-Read delay + const u32 tWTR = !C.t7_tWTR ? 10 : tWTR_values[C.t7_tWTR-1]; - // Read to refresh delay - const u32 tR2REF = tRTP + tRPpb; + // Internal READ-to-PRE-CHARGE command delay in ns + const double tRTP = !TIMING_PRESET_THREE ? 7.5 : tRTP_values[TIMING_PRESET_THREE-1]; - // tRCD (RAS-CAS delay) in ns - const u32 tRCD = !C.t1_tRCD ? 18 : tRCD_values[C.t1_tRCD-1]; + // write recovery time + const u32 tWR = !TIMING_PRESET_THREE ? 18 : tWR_values[TIMING_PRESET_THREE-1]; - // tRRD (Active bank-A to Active bank-B) in ns - const double tRRD = !C.t4_tRRD ? 10. : tRRD_values[C.t4_tRRD-1]; + // tRCD (RAS-CAS delay) in ns + const u32 tRCD = !C.t1_tRCD ? 18 : tRCD_values[C.t1_tRCD-1]; - // 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; + // tRRD (Active bank-A to Active bank-B) in ns + const double tRRD = !C.t4_tRRD ? 10. : tRRD_values[C.t4_tRRD-1]; - // 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; + // tREFpb (average refresh interval per bank) in ns for 8Gb density + const u32 tREFpb = !C.t8_tREFI ? 488 : tREFpb_values[C.t8_tREFI-1]; - // Delay from valid command to CKE input LOW in ns - const double tCMDCKE = 1.75; // TODO: Fix/remove for mariko if needed + // Exit power-down to next valid command delay + const double tXP = 7.5; - // 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 + // tXSR (SELF REFRESH exit to next valid command delay) in ns + const double tXSR = tRFCab + 7.5; - // Valid CS requirement after CKE input LOW - const double tCKELCS = 5; // TODO: Fix/remove for mariko if needed + // Minimum self refresh time (entry to exit) + const u32 tSR = 15; - // Valid CS requirement before CKE input HIGH - const double tCSCKEH = 1.75; // TODO: Fix/remove for mariko if needed + // tFAW (Four-bank Activate Window) in ns + const u32 tFAW = 40;// !TIMING_PRESET_TWO ? 40 : tFAW_values[TIMING_PRESET_TWO-1]; TOGO - // tXSR (SELF REFRESH exit to next valid command delay) in ns - const double tXSR = tRFCab + 7.5; + // #_of_rows per die for 8Gb density + const u32 numOfRows = 131072; - // tCKE (minimum pulse width(HIGH and LOW pulse width)) in ns - const double tCKE = 7.5; // TODO: Fix/remove for mariko if needed + // {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); - // Minimum self refresh time (entry to exit) - const u32 tSR = 15; + // DQS output access time from CK_t/CK_c + const double tDQSCK_min = 1.5; + const double tDQSCK_max = 3.5; + // Write preamble (tCK) + const double tWPRE = 1.8; + // Read postamble (tCK) + const double tRPST = 0.4; - // tFAW (Four-bank Activate Window) in ns - const u32 tFAW = 40;// !TIMING_PRESET_TWO ? 40 : tFAW_values[TIMING_PRESET_TWO-1]; TOGO + namespace pcv::erista { + // tCK_avg (average clock period) in ns + const double tCK_avg = 1000'000. / C.eristaEmcMaxClock; - // Valid Clock requirement before CKE Input HIGH in ns - const double tCKCKEH = 1.75; // TODO: Fix/remove for mariko if needed + // 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; - // p78 The first valid data is available RL × t CK + t DQSCK + t DQSQ - //const u32 QUSE = RL + CEIL(tDQSCK_min/tCK_avg + tDQSQ); + // Delay Time From WRITE-to-READ + const u32 W2R = WL + BL/2 + 1 + CEIL(tWTR/tCK_avg) - 6; - namespace pcv::erista { - // tCK_avg (average clock period) in ns - const double tCK_avg = 1000'000. / C.eristaEmcMaxClock; + // write-to-precharge time for commands to the same bank in cycles + const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg) - 8; + } - // Write Latency - const u32 WL = 14 + C.mem_burst_latency; - // Read Latency - const u32 RL = 32 - C.mem_burst_latency; + namespace pcv::mariko { + // tCK_avg (average clock period) in ns + const double tCK_avg = 1000'000. / C.marikoEmcMaxClock; - // 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; + 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; + // 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; - } - } + // write-to-precharge time for commands to the same bank in cycles + const u32 WTP = WL + BL/2 + 1 + CEIL(tWR/tCK_avg) - 8; + } +} 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..8db2e304 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp @@ -21,271 +21,274 @@ #include "pcv.hpp" #include "../mtc_timing_value.hpp" - namespace ams::ldr::oc::pcv::erista { - Result CpuFreqVdd(u32* ptr) { - dvfs_rail* entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(dvfs_rail, freq)); +namespace ams::ldr::oc::pcv::erista { +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.eristaCpuUV) { - if(!C.enableEristaCpuUnsafeFreqs) { - PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq); - } else { - PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.eristaCpuDvfsTableUnsafeFreqs)->freq); - } - } else { + if (C.eristaCpuUV) { + if(!C.enableEristaCpuUnsafeFreqs) { PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq); + } else { + PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.eristaCpuDvfsTableUnsafeFreqs)->freq); } + } else { + PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq); + } + R_SUCCEED(); +} +Result GpuVmin(u32 *ptr) { + if (!C.eristaGpuVmin) + R_SKIP(); + PATCH_OFFSET(ptr, (int)C.eristaGpuVmin); R_SUCCEED(); - } - Result GpuVmin(u32 *ptr) { - if (!C.eristaGpuVmin) - R_SKIP(); - PATCH_OFFSET(ptr, (int)C.eristaGpuVmin); +} + Result CpuVoltRange(u32 *ptr) { + u32 min_volt_got = *(ptr - 1); + for (const auto &mv : CpuMinVolts) { + if (min_volt_got != mv) + continue; + + if (!C.eristaCpuMaxVolt) + R_SKIP(); + + PATCH_OFFSET(ptr, C.eristaCpuMaxVolt); R_SUCCEED(); + } + R_THROW(ldr::ResultInvalidCpuMinVolt()); } - Result CpuVoltRange(u32 *ptr) { - u32 min_volt_got = *(ptr - 1); - for (const auto &mv : CpuMinVolts) { - if (min_volt_got != mv) - continue; + Result CpuVoltDfll(u32* ptr) { + cvb_cpu_dfll_data *entry = reinterpret_cast(ptr); - if (!C.eristaCpuMaxVolt) - R_SKIP(); - - PATCH_OFFSET(ptr, C.eristaCpuMaxVolt); - R_SUCCEED(); - } - R_THROW(ldr::ResultInvalidCpuMinVolt()); - } - Result CpuVoltDfll(u32* ptr) { - cvb_cpu_dfll_data *entry = reinterpret_cast(ptr); - -// R_UNLESS(entry->tune0_low == 0x0000FFCF, ldr::ResultInvalidCpuVoltDfllEntry()); +// 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()); - if(!C.eristaCpuUV) { - R_SKIP(); - } - PATCH_OFFSET(&(entry->dvco_calibration_max), 0x1C); - PATCH_OFFSET(&(entry->tune1_high), 0x10); - PATCH_OFFSET(&(entry->tune_high_margin_millivolts), 0xc); + if(!C.eristaCpuUV) { + R_SKIP(); + } + PATCH_OFFSET(&(entry->dvco_calibration_max), 0x1C); + PATCH_OFFSET(&(entry->tune1_high), 0x10); + PATCH_OFFSET(&(entry->tune_high_margin_millivolts), 0xc); - switch(C.eristaCpuUV) { - case 1: - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); //process_id 0 // EOS UV1 - PATCH_OFFSET(&(entry->tune1_low), 0x027007FF); - break; - case 2: - PATCH_OFFSET(&(entry->tune0_low), 0x0000EFFF); //process_id 1 // EOS Uv2 - PATCH_OFFSET(&(entry->tune1_low), 0x027407FF); - break; - case 3: - PATCH_OFFSET(&(entry->tune0_low), 0x0000DFFF); //process_id 0 // EOS UV3 - PATCH_OFFSET(&(entry->tune1_low), 0x027807FF); - break; - case 4: - PATCH_OFFSET(&(entry->tune0_low), 0x0000DFDF); //process_id 1 // EOS Uv4 - PATCH_OFFSET(&(entry->tune1_low), 0x027A07FF); - break; - case 5: - PATCH_OFFSET(&(entry->tune0_low), 0x0000CFDF); // EOS UV5 - PATCH_OFFSET(&(entry->tune1_low), 0x037007FF); - break; - default: - break; + switch(C.eristaCpuUV) { + case 1: + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); //process_id 0 // EOS UV1 + PATCH_OFFSET(&(entry->tune1_low), 0x027007FF); + break; + case 2: + PATCH_OFFSET(&(entry->tune0_low), 0x0000EFFF); //process_id 1 // EOS Uv2 + PATCH_OFFSET(&(entry->tune1_low), 0x027407FF); + break; + case 3: + PATCH_OFFSET(&(entry->tune0_low), 0x0000DFFF); //process_id 0 // EOS UV3 + PATCH_OFFSET(&(entry->tune1_low), 0x027807FF); + break; + case 4: + PATCH_OFFSET(&(entry->tune0_low), 0x0000DFDF); //process_id 1 // EOS Uv4 + PATCH_OFFSET(&(entry->tune1_low), 0x027A07FF); + break; + case 5: + PATCH_OFFSET(&(entry->tune0_low), 0x0000CFDF); // EOS UV5 + PATCH_OFFSET(&(entry->tune1_low), 0x037007FF); + 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.eristaGpuUV) { + case 0: + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; + break; + case 1: + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq; + break; + case 2: + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableHigh)->freq; + break; + case 3: + if(C.enableEristaGpuUnsafeFreqs) { + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableUv3UnsafeFreqs)->freq; + } else { + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; + } + break; + default: + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->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 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()); + Result GpuFreqPllLimit(u32 *ptr) { + clk_pll_param *entry = reinterpret_cast(ptr); - // Both instructions should operate on the same register - u8 rd = asm_get_rd(ins1); - if (rd != asm_get_rd(ins2)) - R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + // 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()); + } - u32 max_clock; - switch (C.eristaGpuUV) { - case 0: - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; - break; - case 1: - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq; - break; - case 2: - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableHigh)->freq; - break; - case 3: - if(C.enableEristaGpuUnsafeFreqs) { - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableUv3UnsafeFreqs)->freq; - } else { - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; - } - break; - default: - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->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]); + // Double the max clk simply + u32 max_clk = entry->freq * 2; + entry->freq = max_clk; + R_SUCCEED(); + } - R_SUCCEED(); - } + void MemMtcTableAutoAdjust(EristaMtcTable *table) { + if (C.mtcConf != AUTO_ADJ) + return; - Result GpuFreqPllLimit(u32 *ptr) { - clk_pll_param *entry = reinterpret_cast(ptr); +#define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \ + TABLE->burst_regs.PARAM = VALUE; \ + TABLE->shadow_regs_ca_train.PARAM = VALUE; \ + TABLE->shadow_regs_quse_train.PARAM = VALUE; \ + TABLE->shadow_regs_rdwr_train.PARAM = VALUE; - // 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()); - } +#define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) - // Double the max clk simply - u32 max_clk = entry->freq * 2; - entry->freq = max_clk; - R_SUCCEED(); - } + 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_r2w, R2W); + WRITE_PARAM_ALL_REG(table, emc_w2r, W2R); + WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); + 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); + 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_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_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); - void MemMtcTableAutoAdjust(EristaMtcTable *table) { - if (C.mtcConfErista != AUTO_ADJ) - return; +#define WRITE_PARAM_BURST_MC_REG(TABLE, PARAM, VALUE) TABLE->burst_mc_regs.PARAM = VALUE; - #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \ - TABLE->burst_regs.PARAM = VALUE; \ - TABLE->shadow_regs_ca_train.PARAM = VALUE; \ - TABLE->shadow_regs_quse_train.PARAM = VALUE; \ - TABLE->shadow_regs_rdwr_train.PARAM = VALUE; + constexpr u32 MC_ARB_DIV = 4; + constexpr u32 MC_ARB_SFA = 2; + table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2; + table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA; + 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_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2; + 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; + //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_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_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; + } - #define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) + Result MemFreqMtcTable(u32 *ptr) { + u32 khz_list[] = {1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800}; + u32 khz_list_size = sizeof(khz_list) / sizeof(u32); - 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); - 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_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_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); + // Generate list for mtc table pointers + EristaMtcTable *table_list[khz_list_size]; + for (u32 i = 0; i < khz_list_size; i++) { + u8 *table = reinterpret_cast(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable); + table_list[i] = reinterpret_cast(table); + R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable()); + R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); + } - #define WRITE_PARAM_BURST_MC_REG(TABLE, PARAM, VALUE) TABLE->burst_mc_regs.PARAM = VALUE; + if (C.eristaEmcMaxClock <= EmcClkOSLimit) + R_SKIP(); - constexpr u32 MC_ARB_DIV = 4; - constexpr u32 MC_ARB_SFA = 2; - table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2; - table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA; - 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_ras = CEIL(GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2; - 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; - //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_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_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; - } + // Make room for new mtc table, discarding useless 40.8 MHz table + // 40800 overwritten by 68000, ..., 1331200 overwritten by 1600000, leaving table_list[0] not overwritten + for (u32 i = khz_list_size - 1; i > 0; i--) + std::memcpy(static_cast(table_list[i]), static_cast(table_list[i - 1]), sizeof(EristaMtcTable)); - Result MemFreqMtcTable(u32 *ptr) { - u32 khz_list[] = {1600000, 1331200, 1065600, 800000, 665600, 408000, 204000, 102000, 68000, 40800}; - u32 khz_list_size = sizeof(khz_list) / sizeof(u32); + MemMtcTableAutoAdjust(table_list[0]); + PATCH_OFFSET(ptr, C.eristaEmcMaxClock); - // Generate list for mtc table pointers - EristaMtcTable *table_list[khz_list_size]; - for (u32 i = 0; i < khz_list_size; i++) { - u8 *table = reinterpret_cast(ptr) - offsetof(EristaMtcTable, rate_khz) - i * sizeof(EristaMtcTable); - table_list[i] = reinterpret_cast(table); - R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable()); - R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); - } + // Handle customize table replacement + // if (C.mtcConf == CUSTOMIZED_ALL) { + // MemMtcCustomizeTable(table_list[0], const_cast(C.eristaMtcTable)); + //} - if (C.eristaEmcMaxClock <= EmcClkOSLimit) - R_SKIP(); + R_SUCCEED(); + } - // Make room for new mtc table, discarding useless 40.8 MHz table - // 40800 overwritten by 68000, ..., 1331200 overwritten by 1600000, leaving table_list[0] not overwritten - for (u32 i = khz_list_size - 1; i > 0; i--) - std::memcpy(static_cast(table_list[i]), static_cast(table_list[i - 1]), sizeof(EristaMtcTable)); + Result MemFreqMax(u32 *ptr) { + if (C.eristaEmcMaxClock <= EmcClkOSLimit) + R_SKIP(); - MemMtcTableAutoAdjust(table_list[0]); - PATCH_OFFSET(ptr, C.eristaEmcMaxClock); + PATCH_OFFSET(ptr, C.eristaEmcMaxClock); - // Handle customize table replacement - // if (C.mtcConf == CUSTOMIZED_ALL) { - // MemMtcCustomizeTable(table_list[0], const_cast(C.eristaMtcTable)); - //} + R_SUCCEED(); + } - 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); - Result MemFreqMax(u32 *ptr) { - if (C.eristaEmcMaxClock <= EmcClkOSLimit) - R_SKIP(); + 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, 0xFFEAD0FF }, + {"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 Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit}, + {"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit}, + {"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS}, + {"GPU Vmin", &GpuVmin, 0, nullptr, gpuVmin}, + }; - PATCH_OFFSET(ptr, C.eristaEmcMaxClock); + for (uintptr_t ptr = mapped_nso; + ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); + ptr += sizeof(u32)) { + u32 *ptr32 = reinterpret_cast(ptr); + for (auto &entry : patches) { + if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) + break; + } + } - R_SUCCEED(); - } + for (auto &entry : patches) { + LOGGING("%s Count: %zu", entry.description, entry.patched_count); + if (R_FAILED(entry.CheckResult())) + CRASH(entry.description); + } + } - 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, 0xFFEAD0FF }, - {"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 Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit}, - {"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit}, - {"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS}, - {"GPU Vmin", &GpuVmin, 0, nullptr, gpuVmin}, - }; - - for (uintptr_t ptr = mapped_nso; - ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); - 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); - } - } - - } +} 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..06d1f832 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,486 @@ 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); + + WRITE_PARAM_ALL_REG(table, emc_r2w, R2W); + WRITE_PARAM_ALL_REG(table, emc_w2r, W2R); + WRITE_PARAM_ALL_REG(table, emc_w2p, WTP); + + /* May or may not have to be patched in Micron; let's skip for now. */ + if (!IsMicron()) { + 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_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_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); + + /* Worth replacing with l4t dumps at some point. */ + // 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); + } +} + +}