Better dram timings

This commit is contained in:
Lightos1
2026-01-12 20:49:49 +01:00
parent b8faa12495
commit a23db6e63d
5 changed files with 82 additions and 413 deletions

View File

@@ -29,16 +29,12 @@ namespace ams::ldr::oc::pcv::mariko {
/* TODO: This function is quite uggly, refactor! */
void CalculateMiscTimings() {
rdv = 0x39 + C.mem_burst_read_latency;
einput_duration = 0x1C;
quse_width = 0x8;
for (u32 i = 0; i < g_misc_table_size; i++) {
const auto& e = g_misc_table[i];
if (C.marikoEmcMaxClock >= e.min_freq) {
rdv += e.rdv_inc;
if (e.einput) einput_duration = e.einput;
if (e.quse_width) quse_width = e.quse_width;
}
}
@@ -54,14 +50,6 @@ namespace ams::ldr::oc::pcv::mariko {
}
}
void CalculateObdly() {
obdly = 0x10000002 + C.mem_burst_write_latency;
if (auto patch = FindObdlyPatch()) {
obdly += patch->adjust;
}
}
void CalculateTWTPDEN() {
tWTPDEN = tW2P + 1 + CEIL(tDQSS_max / tCK_avg) + CEIL(tDQS2DQ_max / tCK_avg) + 6;
if (C.marikoEmcMaxClock >= 2'233'000 && C.marikoEmcMaxClock < 2'533'000) tWTPDEN++;
@@ -76,24 +64,6 @@ namespace ams::ldr::oc::pcv::mariko {
}
}
void CalculateQrst() {
qrst = 0x00070000;
u32 qrst_calc = ROUND(22.1 - (C.marikoEmcMaxClock / 1000000.0) * 8.0) + C.mem_burst_read_latency;
u32 qrst_low = MAX(static_cast<u32>(0), qrst_calc);
if (C.marikoEmcMaxClock >= 2'533'000) {
qrst = INCREMENT_HIGH_BYTES_BY(qrst, 1);
} else if (C.marikoEmcMaxClock == 2'800'000) {
qrst = SET_HIGH_BYTES(qrst, 6);
}
qrst = SET_LOW_BYTES(qrst, qrst_low);
if (auto patch = FindQrstPatch()) {
qrst = INCREMENT_LOW_BYTES_BY(qrst, patch->adjust);
}
}
void CalculateQsafe() {
qsafe = ROUND((C.marikoEmcMaxClock / 1000.0) / 138.0 + 37.4) + C.mem_burst_read_latency;
if (auto patch = FindQsafePatch()) {
@@ -132,10 +102,8 @@ namespace ams::ldr::oc::pcv::mariko {
void CalculateTimings() {
CalculateMiscTimings();
CalculateIbdly();
CalculateObdly();
CalculateTWTPDEN();
CalculateTR2W();
CalculateQrst();
CalculateQsafe();
CalculateQpop();
CalculatePdex2rw();

View File

@@ -20,29 +20,29 @@
namespace ams::ldr::oc::pcv::mariko {
const MiscTimings g_misc_table[] = {
{1'866'000, 1, 0x20, 0x9, },
{2'133'000, 1, 0x24, 0xA, },
{2'166'000, 1, 0, 0, },
{2'233'000, 0, 0x25, 0, },
{2'300'000, 0, 0x26, 0xB, },
{2'333'000, 0, 0x27, 0, },
{2'366'000, 1, 0x26, 0xA, },
{2'433'000, 0, 0x27, 0, },
{2'466'000, 0, 0x2A, 0, },
{2'500'000, 0, 0x28, 0xB, },
{2'533'000, 0, 0x29, 0, },
{2'566'000, 1, 0, 0, },
{2'633'000, 0, 0x2A, 0, },
{2'700'000, 0, 0x2B, 0xC, },
{2'733'000, 0, 0x2C, 0, },
{2'766'000, 1, 0x2B, 0xB, },
{2'833'000, 0, 0x2C, 0, },
{2'866'000, 0, 0, 0, },
{2'900'000, 0, 0, 0, },
{2'933'000, 0, 0x2E, 0xC, },
{2'966'000, 1, 0, 0, },
{3'033'000, 0, 0x2F, 0, },
{3'133'000, 1, 0x31, 0xD, },
{1'866'000, 0x20, },
{2'133'000, 0x24, },
{2'166'000, 0, },
{2'233'000, 0x25, },
{2'300'000, 0x26, },
{2'333'000, 0x27, },
{2'366'000, 0x26, },
{2'433'000, 0x27, },
{2'466'000, 0x2A, },
{2'500'000, 0x28, },
{2'533'000, 0x29, },
{2'566'000, 0, },
{2'633'000, 0x2A, },
{2'700'000, 0x2B, },
{2'733'000, 0x2C, },
{2'766'000, 0x2B, },
{2'833'000, 0x2C, },
{2'866'000, 0, },
{2'900'000, 0, },
{2'933'000, 0x2E, },
{2'966'000, 0, },
{3'033'000, 0x2F, },
{3'133'000, 0x31, },
};
const u32 g_misc_table_size = sizeof(g_misc_table) / sizeof(g_misc_table[0]);
@@ -98,39 +98,6 @@ namespace ams::ldr::oc::pcv::mariko {
return nullptr;
}
const AdjustPatch g_obdly_patches[] = {
{2'533'000, -2},
{2'566'000, -2},
{2'600'000, -2},
{2'633'000, -2},
{2'666'000, -2},
{2'700'000, -2},
{2'733'000, -2},
{2'766'000, -2},
{2'800'000, -2},
{2'833'000, -2},
{2'866'000, -2},
{2'900'000, -2},
{2'933'000, -2},
{2'966'000, -2},
{3'000'000, -2},
{3'033'000, -2},
{3'066'000, -2},
{3'100'000, -2},
{3'133'000, -2},
{3'166'000, -2},
{3'200'000, -2},
};
const u32 g_obdly_table_size = sizeof(g_obdly_patches) / sizeof(g_obdly_patches[0]);
const AdjustPatch *FindObdlyPatch() {
for (u32 i = 0; i < g_obdly_table_size; i++)
if (g_obdly_patches[i].freq == C.marikoEmcMaxClock)
return &g_obdly_patches[i];
return nullptr;
}
const AdjustPatch g_tr2w_patches[] = {
{2'500'000, 1},
{2'533'000, 1},
@@ -149,42 +116,6 @@ namespace ams::ldr::oc::pcv::mariko {
return nullptr;
}
const AdjustPatch g_qrst_patches[] = {
{2'166'000, 1},
{2'200'000, 1},
{2'233'000, 1},
{2'266'000, 1},
{2'366'000, 2},
{2'400'000, 2},
{2'433'000, 1},
{2'466'000, 2},
{2'500'000, 1},
{2'533'000, -1},
{2'600'000, 1},
{2'700'000, -1},
{2'733'000, -1},
{2'766'000, 1},
{2'800'000, 1},
{2'833'000, 1},
{2'866'000, 1},
{2'900'000, 1},
{2'933'000, -1},
{2'966'000, 1},
{3'000'000, 1},
{3'100'000, 1},
{3'166'000, 1},
{3'200'000, 1},
};
const u32 g_qrst_table_size = sizeof(g_qrst_patches) / sizeof(g_qrst_patches[0]);
const AdjustPatch *FindQrstPatch() {
for (u32 i = 0; i < g_qrst_table_size; i++)
if (g_qrst_patches[i].freq == C.marikoEmcMaxClock)
return &g_qrst_patches[i];
return nullptr;
}
const AdjustPatch g_qsafe_patches[] = {
{2'166'000, 1},
{2'200'000, 1},

View File

@@ -37,18 +37,10 @@ namespace ams::ldr::oc::pcv::mariko {
extern const u32 g_ibdly_table_size;
const AdjustPatch *FindIbdlyPatch();
extern const AdjustPatch g_obdly_patches[];
extern const u32 g_obdly_table_size;
const AdjustPatch *FindObdlyPatch();
extern const AdjustPatch g_tr2w_patches[];
extern const u32 g_tr2w_table_size;
const AdjustPatch *FindTR2WPatch();
extern const AdjustPatch g_qrst_patches[];
extern const u32 q_qrst_table_size;
const AdjustPatch *FindQrstPatch();
extern const AdjustPatch g_qsafe_patches[];
extern const u32 g_qsafe_table_size;
const AdjustPatch *FindQsafePatch();
@@ -63,9 +55,7 @@ namespace ams::ldr::oc::pcv::mariko {
struct MiscTimings {
u32 min_freq;
u32 rdv_inc;
u32 einput;
u32 quse_width;
};
extern const MiscTimings g_misc_table[];

View File

@@ -27,37 +27,36 @@ namespace ams::ldr::oc {
#define FLOOR(A) std::floor(A)
#define ROUND(A) std::lround(A)
#define GET_LOW_BYTES(x) ((u16)((x) & 0xFFFF))
#define SET_LOW_BYTES(x, val) (((x) & 0xFFFF0000) | ((u32)(val) & 0xFFFF))
#define INCREMENT_LOW_BYTES_BY(x, n) SET_LOW_BYTES((x), (GET_LOW_BYTES(x) + (n)))
#define GET_HIGH_BYTES(x) ((u16)(((x) >> 16) & 0xFFFF))
#define SET_HIGH_BYTES(x, val) (((x) & 0x0000FFFF) | (((u32)(val) & 0xFFFF) << 16))
#define INCREMENT_HIGH_BYTES_BY(x, n) SET_HIGH_BYTES((x), (GET_HIGH_BYTES(x) + (n)))
#define PACK_U32(high, low) ((static_cast<uint32_t>(high) << 16) | (static_cast<uint32_t>(low) & 0xFFFF))
/* Primary timings. */
const std::array<u32, 8> tRCD_values = { 18, 17, 16, 15, 14, 13, 12, 11 };
const std::array<u32, 8> tRP_values = { 18, 17, 16, 15, 14, 13, 12, 11 };
const std::array<u32, 10> tRAS_values = { 42, 36, 34, 32, 30, 28, 26, 24, 22, 20 };
const std::array<double, 7> tRRD_values = { /*10.0,*/ 7.5, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 }; /* 10.0 is used for <2133mhz; do we care? */
const std::array<double, 7> tRRD_values = { /*10.0,*/ 7.5, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 }; /* 10.0 is used for <2133mhz; do we care? 8gb uses 7.5 tRRD on >=1331. */
const std::array<u32, 11> tRFC_values = { 140, 130, 120, 110, 100, 90, 80, 70, 60, 50, 40 };
const std::array<u32, 10> tWTR_values = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
const std::array<u32, 6> tREFpb_values = { 3900, 5850, 7800, 11700, 15600, 99999 };
/* Burst latency, not to be confused with base latency (tWRL). */
const u32 BL = 16;
/* Base latency for read and write (tWRL). */
const u32 RL = 28 + C.mem_burst_read_latency;
const u32 WL = 14 + C.mem_burst_write_latency;
/* Switch uses RL_DBI, todo: get rid of non DBI_RL. */
const u32 RL_DBI = RL + 4;
/* Precharge to Precharge Delay. (Cycles) */
/* Precharge to Precharge Delay. (tCK) */
const u32 tPPD = 4;
/* DQS output access time from CK_t/CK_c. */
const double tDQSCK_max = 3.5;
/* Write preamble. (tCK) */
const u32 tWPRE = 2;
/* tCK Read postamble. */
/* Read postamble. (tCK) */
const double tRPST = 0.5;
/* Minimum Self-Refresh Time. (Entry to Exit) */
@@ -66,86 +65,61 @@ namespace ams::ldr::oc {
/* Exit power down to next valid command delay. */
const double tXP = 7.5;
/* 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;
/* Write recovery time. */
const u32 tWR = 18;
/* TOOD: Fix erista */
namespace pcv::erista {
// const double tCK_avg = 1000'000.0 / C.eristaEmcMaxClock;
//
// /* Primary timings. */
// const u32 tRCD = tRCD_values[C.t1_tRCD];
// const u32 tRPpb = tRP_values[C.t2_tRP];
// const u32 tRAS = tRAS_values[C.t3_tRAS];
//
// /* Secondary timings. */
// const double tRRD = tRRD_values[C.t4_tRRD];
// const u32 tRFCpb = tRFC_values[C.t5_tRFC];
// const u32 tWTR = tWTR_values[C.t7_tWTR];
// const u32 tREFpb = tREFpb_values[C.t8_tREFI];
//
// /* Four-bank ACTIVATE Window */
// const u32 tFAW = (u32) (tRRD * 4.0);
//
// /* Latency stuff. */
// const int tR2W = (int)((3.5 / tCK_avg) + 32 + (BL / 2) - 14 - 6 + tWPRE + 12 - (C.t6_tRTW * 3));
// const int tW2R = (int)((tWTR / tCK_avg) + 18 - (BL / 2));
// const int tRW2PDEN = (int)((tDQSS_max / tCK_avg) + 46 + (tDQS2DQ_max / tCK_avg) + 6);
//
// /* Refresh Cycle time. (All Banks) */
// const u32 tRFCab = tRFCpb * 2;
//
// /* ACTIVATE-to-ACTIVATE command period. (same bank) */
// const u32 tRC = tRAS + tRPpb;
//
// /* SELF REFRESH exit to next valid command delay. */
// const double tXSR = (double) (tRFCab + 7.5);
//
// /* u32ernal READ to PRECHARGE command delay. */
// const int pdex2mrr = (tCK_avg * 3.0) + tRCD_values[C.t1_tRCD] + 1;
//
// /* Row Precharge Time. (all banks) */
// const u32 tRPab = tRPpb + 3;
}
namespace pcv::mariko {
const double tCK_avg = 1000'000.0 / C.marikoEmcMaxClock;
const u32 tRCD = tRCD_values[C.t1_tRCD];
const u32 tRPpb = tRP_values[C.t2_tRP];
const u32 tRAS = tRAS_values[C.t3_tRAS];
const u32 tRCD = tRCD_values[C.t1_tRCD];
const u32 tRPpb = tRP_values[C.t2_tRP];
const u32 tRAS = tRAS_values[C.t3_tRAS];
const double tRRD = tRRD_values[C.t4_tRRD];
const u32 tRFCpb = tRFC_values[C.t5_tRFC];
const u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
const u32 tRFCpb = tRFC_values[C.t5_tRFC];
const u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
const u32 tRC = tRAS + tRPpb;
const u32 tRFCab = tRFCpb * 2;
const double tXSR = (double) (tRFCab + 7.5);
const u32 tFAW = static_cast<u32>(tRRD * 4.0);
const u32 tRC = tRAS + tRPpb;
const u32 tRFCab = tRFCpb * 2;
const double tXSR = static_cast<double>(tRFCab + 7.5);
const u32 tFAW = static_cast<u32>(tRRD * 4.0);
const double tRPab = tRPpb + 3;
const u32 tR2P = 12 + (C.mem_burst_read_latency / 2);
inline u32 tR2W;
const u32 tRTM = RL + 9 + (tDQSCK_max / tCK_avg) + FLOOR(tRPST) + CEIL(10 / tCK_avg); // Fix?
const u32 tRATM = tRTM + CEIL(10 / tCK_avg) - 12; // Fix?
inline u32 rdv;
const u32 quse = FLOOR((-0.0048159 * (C.marikoEmcMaxClock / 1000.0)) + RL_DBI) + (FLOOR((C.marikoEmcMaxClock / 1000.0) * 0.0050997) * 1.5134);
const u32 tRTM = RL + 9 + (tDQSCK_max / tCK_avg) + FLOOR(tRPST) + CEIL(10 / tCK_avg); // Fix?
const u32 tRATM = tRTM + CEIL(10 / tCK_avg) - 12; // Fix?
const u32 rdv = FLOOR(17.02046755653219 + (RL_DBI + ((C.marikoEmcMaxClock / 1000.0) * 0.00510056573299173)));
const u32 quse = FLOOR((-0.0048159 * (C.marikoEmcMaxClock / 1000.0)) + RL_DBI) + (FLOOR((C.marikoEmcMaxClock / 1000.0) * 0.0050997) * 1.5134);
const u32 einput = quse - ((C.marikoEmcMaxClock / 1000.0) * 0.01);
inline u32 einput_duration;
inline u32 ibdly;
inline u32 obdly;
inline u32 quse_width;
const u32 obdly = 0x10000000 + CEIL(MAX((WL + (2.072347067198409 * CEIL(((C.marikoEmcMaxClock / 1000.0) * -0.0008701518090699537) + 1.1926184709583145))) - 12.368815500608948, -1.8792921762826563e-9));
const u32 quse_width = CEIL(((3.7165006256863955 - (C.marikoEmcMaxClock / 1000.0)) + (-0.002446584377651142 * (C.marikoEmcMaxClock / 1000.0))) - FLOOR((C.marikoEmcMaxClock / 1000.0) / -0.9952024303111688));
inline u32 rext;
inline u32 qrst;
const u32 qrstHighDuration = FLOOR(((C.marikoEmcMaxClock / 1000.0) * 0.001477125119082522) + 4.272302254983803);
const u32 qrstLow = CEIL(MAX(((C.marikoEmcMaxClock / 1000.0) * -0.010085158701622026) + ((rdv + (-15.612107759528982 - quse_width)) - qrstHighDuration), -0.0004475366008085334));
const u32 qrst = PACK_U32(qrstHighDuration, qrstLow);
inline u32 qsafe;
inline u32 qpop;
const u32 tW2P = (CEIL(WL * 1.7303) * 2) - 5;
const u32 tW2P = (CEIL(WL * 1.7303) * 2) - 5;
inline u32 tWTPDEN;
const u32 tW2R = CEIL(MAX(WL + (0.010322547033278747 * (C.marikoEmcMaxClock / 1000.0)), (WL * -0.2067922202979121) + FLOOR(((RL_DBI * -0.1331159971685554) + WL) * 3.654131957826108)) - (tWTR / tCK_avg));
const u32 tWTM = WL + (BL / 2) + 1 + CEIL(7.5 / tCK_avg);
const u32 tW2R = CEIL(MAX(WL + (0.010322547033278747 * (C.marikoEmcMaxClock / 1000.0)), (WL * -0.2067922202979121) + FLOOR(((RL_DBI * -0.1331159971685554) + WL) * 3.654131957826108)) - (tWTR / tCK_avg));
const u32 tWTM = WL + (BL / 2) + 1 + CEIL(7.5 / tCK_avg);
const u32 tWATM = tWTM + CEIL(tWR / tCK_avg);
const u32 wdv = WL;
@@ -157,7 +131,7 @@ namespace ams::ldr::oc {
const u32 tCKE = CEIL(1.0795 * CEIL(0.0074472 * (C.marikoEmcMaxClock / 1000.0)));
const double tMMRI = tRCD + (tCK_avg * 3);
const double tMMRI = tRCD + (tCK_avg * 3);
const double pdex2mrr = tMMRI + 10; /* Do this properly? */
}

View File

@@ -378,13 +378,19 @@ namespace ams::ldr::oc::pcv::mariko {
if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) {
R_THROW(ldr::ResultInvalidGpuPllEntry());
}
PATCH_OFFSET(ptr, 3600000);
R_SUCCEED();
}
void MemMtcTableAutoAdjust(MarikoMtcTable *table) {
/* Official Tegra X1 TRM, sign up for nvidia developer program (free) to download:
* https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual
* Section 18.11.1: MC Registers
* Section 18.11.2: EMC Registers
*/
#define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \
TABLE->burst_regs.PARAM = VALUE; \
TABLE->shadow_regs_ca_train.PARAM = VALUE; \
@@ -392,6 +398,10 @@ namespace ams::ldr::oc::pcv::mariko {
#define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg))
/* Ram power down */
/* B31: DRAM_CLKSTOP_PD */
/* B30: DRAM_CLKSTOP_SR */
/* B29: DRAM_ACPD */
if (C.hpMode) {
WRITE_PARAM_ALL_REG(table, emc_cfg, 0x13200000);
} else {
@@ -492,10 +502,13 @@ namespace ams::ldr::oc::pcv::mariko {
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = CEIL(tR2P / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(tW2P / MC_ARB_DIV) + MC_ARB_SFA;
/* Two consecutive reads between two different dram modules. */
/* Only be above 1 for 8gb ram. */
if (table->burst_mc_regs.mc_emem_arb_timing_r2r > 1) {
table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / 4) - 1 + MC_ARB_SFA;
}
/* Same as r2r but for write. */
if (table->burst_mc_regs.mc_emem_arb_timing_w2w > 1) {
table->burst_mc_regs.mc_emem_arb_timing_w2w = CEIL(table->burst_regs.emc_wext / MC_ARB_DIV) - 1 + MC_ARB_SFA;
}
@@ -565,220 +578,13 @@ namespace ams::ldr::oc::pcv::mariko {
table->emc_cfg_2 = 0x11083D;
}
// void MemMtcTableAutoAdjust(MarikoMtcTable *table) {
// /* Official Tegra X1 TRM, sign up for nvidia developer program (free) to download:
// * https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual
// * Section 18.11: MC Registers
// *
// * Retail Mariko: 200FBGA 16Gb DDP LPDDR4X SDRAM x 2
// * x16/Ch, 1Ch/die, Double-die, 2Ch, 1CS(rank), 8Gb density per die
// * 64Mb x 16DQ x 8banks x 2channels = 2048MB (x32DQ) per package
// *
// * Devkit Mariko: 200FBGA 32Gb DDP LPDDR4X SDRAM x 2
// * x16/Ch, 1Ch/die, Quad-die, 2Ch, 2CS(rank), 8Gb density per die
// * X1+ EMC can R/W to both ranks at the same time, resulting in doubled DQ
// * 64Mb x 32DQ x 8banks x 2channels = 4096MB (x64DQ) per package
// *
// * If you have access to LPDDR4(X) specs or datasheets (from manufacturers or Google),
// * you'd better calculate timings yourself rather than relying on following algorithm.
// */
//
/* #define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \
// TABLE->burst_regs.PARAM = VALUE; \
// TABLE->shadow_regs_ca_train.PARAM = VALUE; \
TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
*/
// #define GET_CYCLE(PARAM) u32(CEIL(double(PARAM) / tCK_avg))
/* This condition is insane but it's done in eos. */
/* Need to clean up at some point. */
// u32 rext;
// u32 wext;
// if (C.marikoEmcMaxClock < 3200001) {
// if (C.marikoEmcMaxClock < 2133001) {
// rext = 26;
// wext = 22;
// } else {
// rext = 28;
// wext = 22;
//
// if (2400000 < C.marikoEmcMaxClock) {
// wext = 25;
// }
// }
// } else {
// rext = 30;
// wext = 25;
// }
//
// u32 refresh_raw = 0xFFFF;
// u32 trefbw = 0;
//
// if (C.t8_tREFI != 6) {
// refresh_raw = static_cast<u32>(std::floor(static_cast<double>(tREFpb_values[C.t8_tREFI]) / tCK_avg)) - 0x40;
// refresh_raw = MIN(refresh_raw, static_cast<u32>(0xFFFF));
// }
//
// trefbw = refresh_raw + 0x40;
// trefbw = MIN(trefbw, static_cast<u32>(0x3FFF));
//
// /* Primary timings. */
// WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE(tRCD));
// WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE(tRCD));
// WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE(tRAS));
// WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE(tRPpb));
//
// /* Secondary timings. */
// WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE(tRRD));
// WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE(tRFCab));
// WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE(tRFCpb));
// WRITE_PARAM_ALL_REG(table, emc_r2w, tR2W);
// WRITE_PARAM_ALL_REG(table, emc_w2r, tW2R);
// WRITE_PARAM_ALL_REG(table, emc_r2p, (u32) 0xC);
// WRITE_PARAM_ALL_REG(table, emc_w2p, (u32) 0x2D);
//
// WRITE_PARAM_ALL_REG(table, emc_rext, rext);
// WRITE_PARAM_ALL_REG(table, emc_wext, wext);
//
// WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE(tRPab));
// WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE(tFAW));
// WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE(tRC));
//
// WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE(tSR));
// WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE(tXP) + 2);
// WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE(tXP));
// WRITE_PARAM_ALL_REG(table, emc_tclkstop, GET_CYCLE(tXP) + 8);
//
// WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE(tXSR), (u32) 1022));
// WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE(tXSR), (u32) 1022));
//
// const u32 dyn_self_ref_control = (((u32)(7605.0 / tCK_avg)) + 260U) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000U);
// WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, dyn_self_ref_control);
//
// WRITE_PARAM_ALL_REG(table, emc_rw2pden, ams::ldr::oc::pcv::erista::tRW2PDEN);
// WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE(10.0));
// WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE(10.0));
//
// WRITE_PARAM_ALL_REG(table, emc_pchg2pden, GET_CYCLE(1.75));
// WRITE_PARAM_ALL_REG(table, emc_ar2pden, GET_CYCLE(1.75));
// WRITE_PARAM_ALL_REG(table, emc_pdex2cke, GET_CYCLE(1.75));
// WRITE_PARAM_ALL_REG(table, emc_act2pden, GET_CYCLE(14.0));
// WRITE_PARAM_ALL_REG(table, emc_cke2pden, GET_CYCLE(5.0));
// WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE(ams::ldr::oc::pcv::erista::pdex2mrr));
//
// WRITE_PARAM_ALL_REG(table, emc_refresh, refresh_raw);
// WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, (u32) (refresh_raw / 4));
// WRITE_PARAM_ALL_REG(table, emc_trefbw, trefbw);
//
// const u32 mc_tRCD = (int)((double)(GET_CYCLE(tRCD) >> 2) - 2.0);
// const u32 mc_tRPpb = (int)(((double)(GET_CYCLE(tRPpb) >> 2) - 1.0) + 2.0);
// const u32 mc_tRC = (uint)((double)(GET_CYCLE(tRC) >> 2) - 1.0);
// const u32 mc_tR2W = (uint)(((double)((uint)tR2W >> 2) - 1.0) + 2.0);
// const u32 mc_tW2R = (uint)(((double)(tW2R >> 2) - 1.0) + 2.0);
// const u32 mc_tRAS = MIN(GET_CYCLE(tRAS), (u32) 0x7F);
// const u32 mc_tRRD = MIN(GET_CYCLE(tRRD), (u32) 31);
//
// table->burst_mc_regs.mc_emem_arb_cfg = (int)(((double) C.marikoEmcMaxClock / 33300.0) * 0.25);
// table->burst_mc_regs.mc_emem_arb_timing_ras = (int) ((double) (mc_tRAS >> 2) - 2.0);
// table->burst_mc_regs.mc_emem_arb_timing_rcd = (int) ((double) (GET_CYCLE(tRCD) >> 2) - 2.0);
// table->burst_mc_regs.mc_emem_arb_timing_rp = (int) (((double) (GET_CYCLE(tRPpb) >> 2) - 1.0) + 2.0);
// table->burst_mc_regs.mc_emem_arb_timing_rc = (int) ((double) (GET_CYCLE(tRC) >> 2) - 1.0);
// table->burst_mc_regs.mc_emem_arb_timing_faw = (int) ((double)(GET_CYCLE(tFAW) >> 2) - 1.0);
// table->burst_mc_regs.mc_emem_arb_timing_rrd = (int)((double)(mc_tRRD >> 2) - 1.0);
// table->burst_mc_regs.mc_emem_arb_timing_rap2pre = 3;
// table->burst_mc_regs.mc_emem_arb_timing_wap2pre = 11;
// table->burst_mc_regs.mc_emem_arb_timing_r2w = (uint)(((double)((uint)tR2W >> 2) - 1.0) + 2.0);
// table->burst_mc_regs.mc_emem_arb_timing_w2r = (uint)(((double)(tW2R >> 2) - 1.0) + 2.0);
//
// u32 mc_r2r = table->burst_mc_regs.mc_emem_arb_timing_r2r;
// if (mc_r2r > 1) {
// mc_r2r = (uint)(((double)(long)((double)rext * 0.25) - 1.0) + 2.0);
// table->burst_mc_regs.mc_emem_arb_timing_r2r = mc_r2r;
// }
//
// u32 mc_w2w = table->burst_mc_regs.mc_emem_arb_timing_w2w;
// if (mc_w2w > 1) {
// mc_w2w = (uint)(((double)(long)((double)wext / 4.0) - 1.0) + 2.0);
// table->burst_mc_regs.mc_emem_arb_timing_w2w = mc_w2w;
// }
//
// table->burst_mc_regs.mc_emem_arb_da_turns = ((mc_tW2R >> 1) << 0x18) | ((mc_tR2W >> 1) << 0x10) | ((mc_r2r >> 1) << 8) | ((mc_w2w >> 1));
// table->burst_mc_regs.mc_emem_arb_da_covers = (((uint)(mc_tRCD + 3 + mc_tRPpb) >> 1 & 0xff) << 8) | (((uint)(mc_tRCD + 11 + mc_tRPpb) >> 1 & 0xff) << 0x10) | ((mc_tRC >> 1) & 0xff);
// table->burst_mc_regs.mc_emem_arb_misc0 = (table->burst_mc_regs.mc_emem_arb_misc0 & 0xffe08000U) | ((mc_tRC + 1) & 0xff);
// table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0xd0U), (u32)0x115);
// table->la_scale_regs.mc_ftop_ptsa_rate = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0x18U), (u32)0x1f);
// table->la_scale_regs.mc_ptsa_grant_decrement = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0x1203U), (u32)0x17ff);
//
// u32 mc_latency_allowance = 0;
// if (C.marikoEmcMaxClock / 1000 != 0) {
// mc_latency_allowance = 204800 / (C.marikoEmcMaxClock / 1000);
// }
//
// const u32 mc_latency_allowance2 = mc_latency_allowance & 0xFF;
// const u32 mc_latency_allowance3 = (mc_latency_allowance & 0xFF) << 0x10;
// table->la_scale_regs.mc_latency_allowance_xusb_0 = (table->la_scale_regs.mc_latency_allowance_xusb_0 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_sdmmc_0 = (table->la_scale_regs.mc_latency_allowance_sdmmc_0 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_xusb_1 = (table->la_scale_regs.mc_latency_allowance_xusb_1 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_tsec_0 = (table->la_scale_regs.mc_latency_allowance_tsec_0 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_sdmmca_0 = (table->la_scale_regs.mc_latency_allowance_sdmmca_0 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_sdmmcaa_0 = (table->la_scale_regs.mc_latency_allowance_sdmmcaa_0 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_sdmmcab_0 = (table->la_scale_regs.mc_latency_allowance_sdmmcab_0 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_ppcs_1 = (table->la_scale_regs.mc_latency_allowance_ppcs_1 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_mpcore_0 = (table->la_scale_regs.mc_latency_allowance_mpcore_0 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_avpc_0 = (table->la_scale_regs.mc_latency_allowance_avpc_0 & 0xff00ffffU) | mc_latency_allowance3;
//
// u32 mc_latency_allowance_hc_0 = 0;
// if (C.marikoEmcMaxClock / 1000 != 0) {
// mc_latency_allowance_hc_0 = 35200 / (C.marikoEmcMaxClock / 1000);
// }
//
// table->la_scale_regs.mc_latency_allowance_nvdec_0 = (table->la_scale_regs.mc_latency_allowance_nvdec_0 & 0xff00ffffU) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_hc_0 = (table->la_scale_regs.mc_latency_allowance_hc_0 & 0xffffff00U) | mc_latency_allowance_hc_0;
//
// table->la_scale_regs.mc_latency_allowance_isp2_1 = (table->la_scale_regs.mc_latency_allowance_isp2_1 & 0xff00ff00U) | mc_latency_allowance3 | mc_latency_allowance2;
// table->la_scale_regs.mc_latency_allowance_hc_1 = (table->la_scale_regs.mc_latency_allowance_hc_1 & 0xffffff00U) | mc_latency_allowance2;
//
// u32 mc_latency_allowance_gpu_0 = 0;
// if (C.marikoEmcMaxClock / 1000 != 0) {
// mc_latency_allowance_gpu_0 = 40000 / (C.marikoEmcMaxClock / 1000);
// }
//
// table->la_scale_regs.mc_latency_allowance_gpu_0 = ((mc_latency_allowance_gpu_0 | table->la_scale_regs.mc_latency_allowance_gpu_0) & 0xff00ff00U) | mc_latency_allowance3;
//
// u32 mc_latency_allowance_gpu2_0 = 0;
// if (C.marikoEmcMaxClock / 1000 != 0) {
// mc_latency_allowance_gpu2_0 = 40000 / (C.marikoEmcMaxClock / 1000);
// }
//
// table->la_scale_regs.mc_latency_allowance_gpu2_0 = ((mc_latency_allowance_gpu2_0 | table->la_scale_regs.mc_latency_allowance_gpu2_0) & 0xff00ff00U) | mc_latency_allowance3;
//
// u32 mc_latency_allowance_nvenc_0 = 0;
// if (C.marikoEmcMaxClock / 1000 != 0) {
// mc_latency_allowance_nvenc_0 = 38400 / (C.marikoEmcMaxClock / 1000);
// }
//
// table->la_scale_regs.mc_latency_allowance_nvenc_0 = ((mc_latency_allowance_nvenc_0 | table->la_scale_regs.mc_latency_allowance_nvenc_0) & 0xff00ff00U) | mc_latency_allowance3;
//
// u32 mc_latency_allowance_vic_0 = 0;
// if (C.marikoEmcMaxClock / 1000 != 0) {
// mc_latency_allowance_vic_0 = 0xb540 / (C.marikoEmcMaxClock / 1000);
// }
//
// table->la_scale_regs.mc_latency_allowance_vic_0 = ((mc_latency_allowance_vic_0 | table->la_scale_regs.mc_latency_allowance_vic_0) & 0xff00ff00U) | mc_latency_allowance3;
// table->la_scale_regs.mc_latency_allowance_vi2_0 = (table->la_scale_regs.mc_latency_allowance_vi2_0 & 0xffffff00U) | mc_latency_allowance2;
//
// table->burst_mc_regs.mc_emem_arb_timing_rfcpb = GET_CYCLE(tRFCpb) >> 2;
//
// if (C.hpMode) {
// WRITE_PARAM_ALL_REG(table, emc_cfg, 0x13200000);
// }
//
// table->dram_timings.t_rp = tRFCpb;
// table->dram_timings.t_rfc = tRFCab;
// table->emc_cfg_2 = 0x11083d;
// (void) table;
// }
// WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE(10.0));
// WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE(10.0));
// WRITE_PARAM_ALL_REG(table, emc_pchg2pden, GET_CYCLE(1.75));
// WRITE_PARAM_ALL_REG(table, emc_ar2pden, GET_CYCLE(1.75));
// WRITE_PARAM_ALL_REG(table, emc_pdex2cke, GET_CYCLE(1.75));
// WRITE_PARAM_ALL_REG(table, emc_act2pden, GET_CYCLE(14.0));
// WRITE_PARAM_ALL_REG(table, emc_cke2pden, GET_CYCLE(5.0));
void MemMtcPllmbDivisor(MarikoMtcTable *table) {
constexpr u32 PllOscInKHz = 38400;