diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 152863a8..95d219cf 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -56,13 +56,13 @@ volatile CustomizeTable C = { * - System instabilities * - NAND corruption */ -.eristaEmcMaxClock = 2132640, +.eristaEmcMaxClock = 2265280, /* Mariko CPU: * - Max Voltage in mV: * Default voltage: 1120 */ -.marikoCpuMaxVolt = 1257, +.marikoCpuMaxVolt = 1120, /* Mariko EMC(RAM): * - RAM Clock in kHz: @@ -99,23 +99,17 @@ volatile CustomizeTable C = { .enableEristaCpuUnsafeFreqs = ENABLED, -.commonGpuVoltOffset = 0, // TODO: Split tRCD, tRP and tRAS into separate timings +.commonGpuVoltOffset = 0, -.marikoEmcDvbShift = 4, - -.ramTimingPresetOne = 0, - -.ramTimingPresetTwo = 1, - -.ramTimingPresetThree = 0, - -.ramTimingPresetFour = 2, - -.ramTimingPresetFive = 4, - -.ramTimingPresetSix = 4, // Keep at 4, most optimal - -.ramTimingPresetSeven = 0, // Sets the BL of the ram. Change to 2 to get 1866BL and set to 0 to keep the default 1600BL +.t1_tRCD = 4, +.t2_tRP = 5, +.t3_tRAS = 9, +.t4_tRRD = 1, +.t5_tRFC = 2, +.t6_tRTW = 6, +.t7_tWTR = 4, +.t8_tREFI = 6, +.mem_burst_latency = 2, // Erista default (HB-MGCH ST timing) // @@ -161,7 +155,7 @@ volatile CustomizeTable C = { 875 /* 691 */, 900 /* 768 */, 950 /* 844 */, - 975 /* 921 */, + 900 /* 921 */, 910 /* 998 (Disabled by default) */, 950 /* 1075 (Disabled by default) */, 1000 /* 1152 (Disabled by default) */, diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index d3381efd..ab14c69e 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -16,96 +16,100 @@ * along with this program. If not, see . */ -#pragma once + #pragma once -#define CUST_REV 11 - -#include "oc_common.hpp" -#include "pcv/pcv_common.hpp" - -namespace ams::ldr::oc { - -#include "mtc_timing_table.hpp" - -enum MtcConfig: u32 { - AUTO_ADJ_ALL = 0, - CUSTOM_ADJ_ALL = 1, - NO_ADJ_ALL = 2, - - CUSTOMIZED_ALL = 4, -}; - -using CustomizeCpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit]; -using CustomizeGpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit]; -static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(CustomizeGpuDvfsTable)); -static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(pcv::cvb_entry_t) * pcv::DvfsTableEntryLimit); - -constexpr uint32_t ERISTA_MTC_MAGIC = 0x43544D45; // EMTC -constexpr uint32_t MARIKO_MTC_MAGIC = 0x43544D4D; // MMTC - -typedef struct CustomizeTable { - u8 cust[4] = {'C', 'U', 'S', 'T'}; - u32 custRev = CUST_REV; - u32 mtcConf = AUTO_ADJ_ALL; - u32 commonCpuBoostClock; - u32 commonEmcMemVolt; - u32 eristaCpuMaxVolt; - u32 eristaEmcMaxClock; - u32 marikoCpuMaxVolt; - u32 marikoEmcMaxClock; - u32 marikoEmcVddqVolt; - u32 marikoCpuUV; - u32 marikoGpuUV; - - u32 eristaCpuUV; - u32 eristaGpuUV; - - u32 enableMarikoGpuUnsafeFreqs; - u32 enableEristaGpuUnsafeFreqs; - - u32 enableMarikoCpuUnsafeFreqs; - u32 enableEristaCpuUnsafeFreqs; - - u32 commonGpuVoltOffset; - // advanced config - u32 marikoEmcDvbShift; - u32 ramTimingPresetOne; - u32 ramTimingPresetTwo; - u32 ramTimingPresetThree; - u32 ramTimingPresetFour; - u32 ramTimingPresetFive; - u32 ramTimingPresetSix; - u32 ramTimingPresetSeven; - // - u32 marikoGpuVoltArray[24]; - u32 eristaGpuVoltArray[15]; - - CustomizeCpuDvfsTable eristaCpuDvfsTable; - CustomizeCpuDvfsTable marikoCpuDvfsTable; - CustomizeCpuDvfsTable marikoCpuDvfsTableSLT; - - CustomizeGpuDvfsTable eristaGpuDvfsTable; - CustomizeGpuDvfsTable eristaGpuDvfsTableSLT; - CustomizeGpuDvfsTable eristaGpuDvfsTableHigh; - - - CustomizeGpuDvfsTable marikoGpuDvfsTable; - CustomizeGpuDvfsTable marikoGpuDvfsTableSLT; - CustomizeGpuDvfsTable marikoGpuDvfsTableHiOPT; - //EristaMtcTable* eristaMtcTable; - //MarikoMtcTable* marikoMtcTable; - CustomizeGpuDvfsTable eristaGpuDvfsTableUv3UnsafeFreqs; - CustomizeGpuDvfsTable marikoGpuDvfsTableUv3UnsafeFreqs; - CustomizeCpuDvfsTable marikoCpuDvfsTableUnsafeFreqs; - CustomizeCpuDvfsTable eristaCpuDvfsTableUnsafeFreqs; - -} CustomizeTable; -//static_assert(sizeof(CustomizeTable) == sizeof(u8) * 4 + sizeof(u32) * 10 + sizeof(CustomizeCpuDvfsTable) * 5 + sizeof(void*) * 2); -//static_assert(sizeof(CustomizeTable) == 7000); - -extern volatile CustomizeTable C; - -//extern volatile EristaMtcTable EristaMtcTablePlaceholder; -//extern volatile MarikoMtcTable MarikoMtcTablePlaceholder; - -} \ No newline at end of file + #define CUST_REV 11 + + #include "oc_common.hpp" + #include "pcv/pcv_common.hpp" + + namespace ams::ldr::oc { + + #include "mtc_timing_table.hpp" + + enum MtcConfig: u32 { + AUTO_ADJ_ALL = 0, + CUSTOM_ADJ_ALL = 1, + NO_ADJ_ALL = 2, + + CUSTOMIZED_ALL = 4, + }; + + using CustomizeCpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit]; + using CustomizeGpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit]; + static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(CustomizeGpuDvfsTable)); + static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(pcv::cvb_entry_t) * pcv::DvfsTableEntryLimit); + + constexpr uint32_t ERISTA_MTC_MAGIC = 0x43544D45; // EMTC + constexpr uint32_t MARIKO_MTC_MAGIC = 0x43544D4D; // MMTC + + typedef struct CustomizeTable { + u8 cust[4] = {'C', 'U', 'S', 'T'}; + u32 custRev = CUST_REV; + u32 mtcConf = AUTO_ADJ_ALL; + u32 commonCpuBoostClock; + u32 commonEmcMemVolt; + u32 eristaCpuMaxVolt; + u32 eristaEmcMaxClock; + u32 marikoCpuMaxVolt; + u32 marikoEmcMaxClock; + u32 marikoEmcVddqVolt; + u32 marikoCpuUV; + u32 marikoGpuUV; + + u32 eristaCpuUV; + u32 eristaGpuUV; + + u32 enableMarikoGpuUnsafeFreqs; + u32 enableEristaGpuUnsafeFreqs; + + u32 enableMarikoCpuUnsafeFreqs; + u32 enableEristaCpuUnsafeFreqs; + + u32 commonGpuVoltOffset; + + u32 marikoEmcDvbShift; + + // advanced config + u32 t1_tRCD; + u32 t2_tRP; + u32 t3_tRAS; + u32 t4_tRRD; + u32 t5_tRFC; + u32 t6_tRTW; + u32 t7_tWTR; + u32 t8_tREFI; + u32 mem_burst_latency; + + u32 marikoGpuVoltArray[24]; + u32 eristaGpuVoltArray[15]; + + CustomizeCpuDvfsTable eristaCpuDvfsTable; + CustomizeCpuDvfsTable marikoCpuDvfsTable; + CustomizeCpuDvfsTable marikoCpuDvfsTableSLT; + + CustomizeGpuDvfsTable eristaGpuDvfsTable; + CustomizeGpuDvfsTable eristaGpuDvfsTableSLT; + CustomizeGpuDvfsTable eristaGpuDvfsTableHigh; + + + CustomizeGpuDvfsTable marikoGpuDvfsTable; + CustomizeGpuDvfsTable marikoGpuDvfsTableSLT; + CustomizeGpuDvfsTable marikoGpuDvfsTableHiOPT; + //EristaMtcTable* eristaMtcTable; + //MarikoMtcTable* marikoMtcTable; + CustomizeGpuDvfsTable eristaGpuDvfsTableUv3UnsafeFreqs; + CustomizeGpuDvfsTable marikoGpuDvfsTableUv3UnsafeFreqs; + CustomizeCpuDvfsTable marikoCpuDvfsTableUnsafeFreqs; + CustomizeCpuDvfsTable eristaCpuDvfsTableUnsafeFreqs; + + } CustomizeTable; + //static_assert(sizeof(CustomizeTable) == sizeof(u8) * 4 + sizeof(u32) * 10 + sizeof(CustomizeCpuDvfsTable) * 5 + sizeof(void*) * 2); + //static_assert(sizeof(CustomizeTable) == 7000); + + extern volatile CustomizeTable C; + + //extern volatile EristaMtcTable EristaMtcTablePlaceholder; + //extern volatile MarikoMtcTable MarikoMtcTablePlaceholder; + + } \ No newline at end of file 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 04173066..13c94636 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp @@ -16,220 +16,223 @@ * from GCC preprocessor output */ -#pragma once -#include "oc_common.hpp" -namespace ams::ldr::oc { - #define MAX(A, B) std::max(A, B) - #define MIN(A, B) std::min(A, B) - #define CEIL(A) std::ceil(A) - #define FLOOR(A) std::floor(A) + #pragma once - //Preset One - const std::array tRCD_values = {18, 17, 16, 15, 14, 13, 12, 11}; - const std::array tRP_values = {18, 17, 16, 15, 14, 13, 12, 11}; - const std::array tRAS_values = {42, 36, 34, 32, 30, 28, 26, 24, 22, 20}; - - // Preset Two - const std::array tRRD_values = {10, 7.5, 6, 5, 4, 3, 2, 1}; - const std::array tFAW_values = {40, 30, 24, 16, 12}; - - // Preset Three - const std::array tWR_values = {18, 15, 15, 12, 12, 8}; - const std::array tRTP_values = {7.5, 7.5, 6, 6, 4, 4}; - - // Preset Four - const std::array tRFC_values = {140, 120, 100, 80, 70, 60}; - - // Preset Five - const std::array tWTR_values = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; - - // Preset Six - const std::array tREFpb_values = {488, 976, 1952, 3256, 9999}; - - const u32 TIMING_PRESET_ONE = C.ramTimingPresetOne; - const u32 TIMING_PRESET_TWO = C.ramTimingPresetTwo; - const u32 TIMING_PRESET_THREE = C.ramTimingPresetThree; - const u32 TIMING_PRESET_FOUR = C.ramTimingPresetFour; - const u32 TIMING_PRESET_FIVE = C.ramTimingPresetFive; - const u32 TIMING_PRESET_SIX = C.ramTimingPresetSix; - const u32 TIMING_PRESET_SEVEN = C.ramTimingPresetSeven; - - // Burst Length - const u32 BL = 16; - - // tRFCpb (refresh cycle time per bank) in ns for 8Gb density - const u32 tRFCpb = !TIMING_PRESET_FOUR ? 140 : tRFC_values[TIMING_PRESET_FOUR-1]; - - // tRFCab (refresh cycle time all banks) in ns for 8Gb density - const u32 tRFCab = !TIMING_PRESET_FOUR ? 280 : 2*tRFCpb; - - // tRAS (row active time) in ns - const u32 tRAS = !TIMING_PRESET_ONE ? 42 : tRAS_values[TIMING_PRESET_ONE-1]; - - // tRPpb (row precharge time per bank) in ns - const u32 tRPpb = !TIMING_PRESET_ONE ? 18 : tRP_values[TIMING_PRESET_ONE-1]; - - // tRPab (row precharge time all banks) in ns - const u32 tRPab = !TIMING_PRESET_ONE ? 21 : tRPpb + 3; - - // tRC (ACTIVATE-ACTIVATE command period same bank) in ns - const u32 tRC = tRPpb + tRAS; - - // DQS output access time from CK_t/CK_c - const double tDQSCK_min = 1.5; - // DQS output access time from CK_t/CK_c - const double tDQSCK_max = 3.5; - // Write preamble (tCK) - const double tWPRE = 1.8; - // Read postamble (tCK) - const double tRPST = 0.4; - // WRITE command to first DQS transition(max) (tCK) - const double tDQSS_max = 1.25; - // DQ-to-DQS offset(max) (ns) - const double tDQS2DQ_max = 0.8; - // DQS_t, DQS_c to DQ skew total, per group, per access (DBI Disabled) - const double tDQSQ = 0.18; - - // Write-to-Read delay - const u32 tWTR = !TIMING_PRESET_FIVE ? 10 : tWTR_values[TIMING_PRESET_FIVE-1]; - - // Internal READ-to-PRE-CHARGE command delay in ns - const double tRTP = !TIMING_PRESET_THREE ? 7.5 : tRTP_values[TIMING_PRESET_THREE-1]; - - // 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 = !TIMING_PRESET_ONE ? 18 : tRCD_values[TIMING_PRESET_ONE-1]; - - // tRRD (Active bank-A to Active bank-B) in ns - const double tRRD = !TIMING_PRESET_TWO ? 10. : tRRD_values[TIMING_PRESET_TWO-1]; - - // tREFpb (average refresh interval per bank) in ns for 8Gb density - const u32 tREFpb = !TIMING_PRESET_SIX ? 488 : tREFpb_values[TIMING_PRESET_SIX-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; - - // 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; - - // Valid CS requirement after CKE input LOW - const double tCKELCS = 5; - - // Valid CS requirement before CKE input HIGH - const double tCSCKEH = 1.75; - - // 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; - - // Minimum self refresh time (entry to exit) - const u32 tSR = 15; - - // tFAW (Four-bank Activate Window) in ns - const u32 tFAW = !TIMING_PRESET_TWO ? 40 : tFAW_values[TIMING_PRESET_TWO-1]; - - // Valid Clock requirement before CKE Input HIGH in ns - const double tCKCKEH = 1.75; - - // p78 The first valid data is available RL × t CK + t DQSCK + t DQSQ - //const u32 QUSE = RL + CEIL(tDQSCK_min/tCK_avg + tDQSQ); - - namespace pcv::erista { - // tCK_avg (average clock period) in ns - const double tCK_avg = 1000'000. / C.eristaEmcMaxClock; - - // Write Latency - const u32 WL = 14 + TIMING_PRESET_SEVEN; - // Read Latency - const u32 RL = 32 + TIMING_PRESET_SEVEN; - - // 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 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.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 + TIMING_PRESET_SEVEN; - // Read Latency - const u32 RL = 32 + TIMING_PRESET_SEVEN; - - // 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)); - - // 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; - } -} + #include "oc_common.hpp" + + namespace ams::ldr::oc { + #define MAX(A, B) std::max(A, B) + #define MIN(A, B) std::min(A, B) + #define CEIL(A) std::ceil(A) + #define FLOOR(A) std::floor(A) + + //Preset One + const std::array tRCD_values = {18, 17, 16, 15, 14, 13, 12, 11}; + const std::array tRP_values = {18, 17, 16, 15, 14, 13, 12, 11}; + const std::array tRAS_values = {42, 36, 34, 32, 30, 28, 26, 24, 22, 20}; + + // Preset Two + const std::array tRRD_values = {10, 7.5, 6, 5, 4, 3, 2, 1}; + const std::array tFAW_values = {40, 30, 24, 16, 12}; + + // Preset Three + const std::array tWR_values = {18, 15, 15, 12, 12, 8}; // TODO: identify what exactly eos tRTW even is (is it even real?) + const std::array tRTP_values = {7.5, 7.5, 6, 6, 4, 4}; + + // Preset Four + const std::array tRFC_values = {140, 120, 100, 80, 70, 60}; + + // Preset Five + const std::array tWTR_values = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + + // Preset Six + const std::array tREFpb_values = {488, 976, 1952, 3256, 9999, 9999}; + + // const u32 TIMING_PRESET_ONE = C.ramTimingPresetOne; + // const u32 TIMING_PRESET_TWO = C.ramTimingPresetTwo; + const u32 TIMING_PRESET_THREE = 0; + // const u32 TIMING_PRESET_FOUR = C.ramTimingPresetFour; + // const u32 TIMING_PRESET_FIVE = C.ramTimingPresetFive; + // const u32 TIMING_PRESET_SIX = C.ramTimingPresetSix; + // const u32 TIMING_PRESET_SEVEN = C.ramTimingPresetSeven; + + // 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]; + + // tRFCab (refresh cycle time all banks) in ns for 8Gb density + const u32 tRFCab = !C.t5_tRFC ? 280 : 2*tRFCpb; + + // tRAS (row active time) in ns + const u32 tRAS = !C.t3_tRAS ? 42 : tRAS_values[C.t3_tRAS-1]; + + // tRPpb (row precharge time per bank) in ns + const u32 tRPpb = !C.t2_tRP ? 18 : tRP_values[C.t2_tRP-1]; + + // tRPab (row precharge time all banks) in ns + const u32 tRPab = !C.t2_tRP ? 21 : tRPpb + 3; + + // tRC (ACTIVATE-ACTIVATE command period same bank) in ns + const u32 tRC = tRPpb + tRAS; + + // DQS output access time from CK_t/CK_c + const double tDQSCK_min = 1.5; + // DQS output access time from CK_t/CK_c + const double tDQSCK_max = 3.5; + // Write preamble (tCK) + const double tWPRE = 1.8; + // Read postamble (tCK) + const double tRPST = 0.4; + // WRITE command to first DQS transition(max) (tCK) + const double tDQSS_max = 1.25; + // DQ-to-DQS offset(max) (ns) + const double tDQS2DQ_max = 0.8; + // DQS_t, DQS_c to DQ skew total, per group, per access (DBI Disabled) + const double tDQSQ = 0.18; + + // Write-to-Read delay + const u32 tWTR = !C.t7_tWTR ? 10 : tWTR_values[C.t7_tWTR-1]; + + // Internal READ-to-PRE-CHARGE command delay in ns + const double tRTP = !TIMING_PRESET_THREE ? 7.5 : tRTP_values[TIMING_PRESET_THREE-1]; + + // 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]; + + // tRRD (Active bank-A to Active bank-B) in ns + const double tRRD = !C.t4_tRRD ? 10. : tRRD_values[C.t4_tRRD-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; + + // 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; + + // 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; + + // Valid CS requirement after CKE input LOW + const double tCKELCS = 5; + + // Valid CS requirement before CKE input HIGH + const double tCSCKEH = 1.75; + + // 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; + + // 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; + + // p78 The first valid data is available RL × t CK + t DQSCK + t DQSQ + //const u32 QUSE = RL + CEIL(tDQSCK_min/tCK_avg + tDQSQ); + + 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 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.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 = CEIL (RL + CEIL(tDQSCK_max/tCK_avg) + BL/2 - WL + tWPRE + FLOOR(tRPST)); + + // 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; + } + } + \ No newline at end of file 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 02b22b12..43c80156 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp @@ -16,330 +16,311 @@ * along with this program. If not, see . */ -#include "pcv.hpp" -#include "../mtc_timing_value.hpp" - -namespace ams::ldr::oc::pcv::erista -{ - - 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 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 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(EristaMtcTable *table) - { - if (C.mtcConf != AUTO_ADJ_ALL) - return; - -#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; - -#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_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_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)); - 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); - -#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; - 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; - } - - void MemMtcTableCustomAdjust(EristaMtcTable *table) - { - if (C.mtcConf != CUSTOM_ADJ_ALL) - return; - - constexpr u32 MC_ARB_DIV = 4; - constexpr u32 MC_ARB_SFA = 2; - - if (TIMING_PRESET_ONE) - { - 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); - } - - if (TIMING_PRESET_TWO) - { - 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; - } - - if (TIMING_PRESET_THREE) - { - 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_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); - } - - if (TIMING_PRESET_FOUR) - { - 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); - } - - if (TIMING_PRESET_FIVE) - { - 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; - } - - if (TIMING_PRESET_SIX) - { - 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); - } - - if (TIMING_PRESET_SEVEN) - { - 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_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); - } - - 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); - - // 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()); - } - - if (C.eristaEmcMaxClock <= EmcClkOSLimit) - R_SKIP(); - - // 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)); - - MemMtcTableAutoAdjust(table_list[0]); - PATCH_OFFSET(ptr, C.eristaEmcMaxClock); - - // Handle customize table replacement - // if (C.mtcConf == CUSTOMIZED_ALL) { - // MemMtcCustomizeTable(table_list[0], const_cast(C.eristaMtcTable)); - //} - - R_SUCCEED(); - } - - Result MemFreqMax(u32 *ptr) - { - if (C.eristaEmcMaxClock <= EmcClkOSLimit) - R_SKIP(); - - PATCH_OFFSET(ptr, C.eristaEmcMaxClock); - - 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 Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq}, - {"CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn}, - {"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}, - }; - - 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); - } - } - -} \ No newline at end of file + #include "pcv.hpp" + #include "../mtc_timing_value.hpp" + + namespace ams::ldr::oc::pcv::erista + { + + 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 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 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(EristaMtcTable *table) + { + if (C.mtcConf != AUTO_ADJ_ALL) + return; + + #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; + + #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_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_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)); + 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); + + #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; + 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; + } + + void MemMtcTableCustomAdjust(EristaMtcTable *table) + { + if (C.mtcConf != 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_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_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); + } + + 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); + + // 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()); + } + + if (C.eristaEmcMaxClock <= EmcClkOSLimit) + R_SKIP(); + + // 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)); + + MemMtcTableAutoAdjust(table_list[0]); + PATCH_OFFSET(ptr, C.eristaEmcMaxClock); + + // Handle customize table replacement + // if (C.mtcConf == CUSTOMIZED_ALL) { + // MemMtcCustomizeTable(table_list[0], const_cast(C.eristaMtcTable)); + //} + + R_SUCCEED(); + } + + Result MemFreqMax(u32 *ptr) + { + if (C.eristaEmcMaxClock <= EmcClkOSLimit) + R_SKIP(); + + PATCH_OFFSET(ptr, C.eristaEmcMaxClock); + + 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 Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq}, + {"CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn}, + {"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}, + }; + + 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); + } + } + + } \ No newline at end of file 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 1ff36b86..31eb40a0 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -16,644 +16,630 @@ * along with this program. If not, see . */ -#include "pcv.hpp" -#include "../mtc_timing_value.hpp" - -namespace ams::ldr::oc::pcv::mariko { - -Result CpuFreqVdd(u32* ptr) { - dvfs_rail* entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(dvfs_rail, freq)); - - R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry()); - R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry()); - R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry()); - R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry()); - - if (C.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); - } + #include "pcv.hpp" + #include "../mtc_timing_value.hpp" + + namespace ams::ldr::oc::pcv::mariko { + + Result CpuFreqVdd(u32* ptr) { + dvfs_rail* entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(dvfs_rail, freq)); + + R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry()); + R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry()); + R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry()); + R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry()); + + if (C.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(); + } + + 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(); + + PATCH_OFFSET(ptr, C.marikoCpuMaxVolt); + // Patch vmin for slt + if (C.marikoCpuUV) { + if (*(ptr-5) == 620) { + PATCH_OFFSET((ptr-5), 600); + } + if (*(ptr-1) == 620) { + PATCH_OFFSET((ptr-1), 600); + } + + } + 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_high == 0x00000000, ldr::ResultInvalidCpuVoltDfllEntry()); + R_UNLESS(entry->tune1_low == 0x012207FF, ldr::ResultInvalidCpuVoltDfllEntry()); + R_UNLESS(entry->tune1_high == 0x03FFF7FF, ldr::ResultInvalidCpuVoltDfllEntry()); + + if (C.marikoCpuUV) { + if (C.marikoCpuUV == 1) { + PATCH_OFFSET(&(entry->tune0_low), 0x0000FF90); //process_id 0 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x00000000); + } + else if (C.marikoCpuUV == 2) { + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFA0); //process_id 1 + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x00000000); + } + else if (C.marikoCpuUV == 3) { + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x024417FF); + } + else if (C.marikoCpuUV == 4) { + PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); + PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); + PATCH_OFFSET(&(entry->tune1_high), 0x028817FF); + } + } + + 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.mtcConf != 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)) - R_SUCCEED(); -} - -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(); - - PATCH_OFFSET(ptr, C.marikoCpuMaxVolt); - // Patch vmin for slt - if (C.marikoCpuUV) { - if (*(ptr-5) == 620) { - PATCH_OFFSET((ptr-5), 600); - } - if (*(ptr-1) == 620) { - PATCH_OFFSET((ptr-1), 600); - } - - } - 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_high == 0x00000000, ldr::ResultInvalidCpuVoltDfllEntry()); - R_UNLESS(entry->tune1_low == 0x012207FF, ldr::ResultInvalidCpuVoltDfllEntry()); - R_UNLESS(entry->tune1_high == 0x03FFF7FF, ldr::ResultInvalidCpuVoltDfllEntry()); - - if (C.marikoCpuUV) { - if (C.marikoCpuUV == 1) { - PATCH_OFFSET(&(entry->tune0_low), 0x0000FF90); //process_id 0 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x00000000); - } - else if (C.marikoCpuUV == 2) { - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFA0); //process_id 1 - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x00000000); - } - else if (C.marikoCpuUV == 3) { - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x024417FF); - } - else if (C.marikoCpuUV == 4) { - PATCH_OFFSET(&(entry->tune0_low), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune0_high), 0x0000FFFF); - PATCH_OFFSET(&(entry->tune1_low), 0x021107FF); - PATCH_OFFSET(&(entry->tune1_high), 0x028817FF); - } - } - - 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.mtcConf != 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.mtcConf != CUSTOM_ADJ_ALL) - return; - - constexpr u32 MC_ARB_DIV = 4; - constexpr u32 MC_ARB_SFA = 2; - - if (TIMING_PRESET_ONE) { - 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_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.mtcConf != CUSTOM_ADJ_ALL) + return; - } - - if (TIMING_PRESET_TWO) { - 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; - } - - if (TIMING_PRESET_THREE) { - 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); - } - - if (TIMING_PRESET_FOUR) { - 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); - } - - if (TIMING_PRESET_FIVE) { - 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; - } - - if (TIMING_PRESET_SIX) { - 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); - } - - if (TIMING_PRESET_SEVEN) { - 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 } - }; - - 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); - } -} - -} \ No newline at end of file + 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 } + }; + + 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); + } + } + + } \ No newline at end of file