2133BL for mariko (AA); TODO: proper scaling and adjustments

This commit is contained in:
Lightos1
2025-11-22 20:44:27 +01:00
parent 143d5d7d11
commit d28a01d2c2
5 changed files with 341 additions and 400 deletions

View File

@@ -36,19 +36,19 @@ volatile CustomizeTable C = {
.commonCpuBoostClock = 1785000, // Default boost clock
.commonEmcMemVolt = 1212500, // LPDDR4X JEDEC Specification
.commonEmcMemVolt = 1175000, // LPDDR4X JEDEC Specification
.eristaCpuMaxVolt = 1235,
.eristaEmcMaxClock = 2300000, // Maximum HB-MGCH ram rating
.eristaEmcMaxClock = 1600001, // Maximum HB-MGCH ram rating
.marikoCpuMaxVolt = 1120,
.marikoEmcMaxClock = 1866000, // Hynix NME and Samsung AM-MGCJ Rating (others are 4766MT, 2133MHz)
.marikoEmcMaxClock = 3000000, // Hynix NME and Samsung AM-MGCJ Rating (others are 4766MT, 2133MHz)
.marikoEmcVddqVolt = 640000,
.marikoCpuUV = 5, // No undervolt
.marikoCpuUV = 0, // No undervolt
.marikoGpuUV = 3,
@@ -71,15 +71,15 @@ volatile CustomizeTable C = {
// Defaults - (3-3-2) 0-1-4-3-6
// Primary
.t1_tRCD = 5,
.t2_tRP = 7,
.t3_tRAS = 8,
.t1_tRCD = 0,
.t2_tRP = 0,
.t3_tRAS = 0,
// Secondary
.t4_tRRD = 1,
.t5_tRFC = 2,
.t6_tRTW = 5,
.t7_tWTR = 4,
.t8_tREFI= 6,
.t4_tRRD = 0,
.t5_tRFC = 0,
.t6_tRTW = 0,
.t7_tWTR = 0,
.t8_tREFI= 0,
// .mem_burst_latency = 0, // 0 - 1600l, 1 = 1866bl, 2 = 2133bl /* TODO: Remove/fix. */
@@ -110,12 +110,12 @@ volatile CustomizeTable C = {
710 /* 1075 */,
735 /* 1152 */,
785 /* 1228 */,
780 /* 1267 (Disabled by default) */,
805 /* 1305 (Disabled by default) */,
870 /* 1344 (Disabled by default) */,
870 /* 1382 (Disabled by default) */,
870 /* 1420 (Disabled by default) */,
0 /* 1459 (Disabled by default) */,
785 /* 1267 (Disabled by default) */,
960 /* 1305 (Disabled by default) */,
960 /* 1344 (Disabled by default) */,
960 /* 1382 (Disabled by default) */,
960 /* 1420 (Disabled by default) */,
960 /* 1459 (Disabled by default) */,
0 /* 1497 (Disabled by default) */,
0 /* 1536 (Disabled by default) */,

View File

@@ -46,7 +46,7 @@
u8 cust[4] = {'C', 'U', 'S', 'T'};
u32 custRev = CUST_REV;
u32 mtcConf = AUTO_ADJ;
bool hpMode;
u32 hpMode;
u32 commonCpuBoostClock;
u32 commonEmcMemVolt;
u32 eristaCpuMaxVolt;

View File

@@ -28,37 +28,44 @@ namespace ams::ldr::oc {
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};
/* Secondary timings. */
const std::array<double, 8> tRRD_values = {10.0, 7.5, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0};
const std::array<u32, 6> tRFC_values = {90, 80, 70, 60, 50, 40};
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, 3, 1};
const std::array<u32, 6> tREFpb_values = {3900, 5850, 7800, 11700, 15600, 99999};
const u32 BL = 16;
/* Set to 4 read and 2 write for 1866bl. */
/* For 2131bl: 8 read and 4 write. */
const u32 latency_offset_read = 0;
const u32 latency_offset_write = 0;
const u32 BL = 16;
const u32 RL = 28 + latency_offset_read;
const u32 WL = 14 + latency_offset_write;
/* Precharge to Precharge Delay. (Cycles) */
/* Don't touch! */
const u32 tPPD = 4;
/* DQS output access time from CK_t/CK_c. */
const double tDQSCK_max = 3.5;
const double tWPRE = 2.0;
const double tWPRE = 1.8;
/* tCK Read postamble. */
const double tRPST = 0.5;
/* Minimum Self-Refresh Time. (Entry to Exit) */
const double tSR = 15.0;
/* Exit power down to next valid command delay. */
const double tXP = 7.5;
const double tDQSS_max = 1.25;
const double tDQS2DQ_max = 0.8;
/* TOOD: Fix erista */
namespace pcv::erista {
/* tCK_avg may have to be improved... */
const double tCK_avg = 1000'000.0 / C.eristaEmcMaxClock;
const u32 RL = 28 + latency_offset_read;
const u32 WL = 14 + latency_offset_write;
/* Primary timings. */
const u32 tRCD = tRCD_values[C.t1_tRCD];
const u32 tRPpb = tRP_values[C.t2_tRP];
@@ -76,33 +83,57 @@ namespace ams::ldr::oc {
/* Latency stuff. */
const int tR2W = (int)((3.5 / tCK_avg) + 32 + (BL / 2) - 14 - 6 + tWPRE + 12 - (C.t6_tRTW * 3));
const int tW2R = (int)((tWTR / tCK_avg) + 18 - (BL / 2));
const int tRW2PDEN = (int)((1.25 / tCK_avg) + 46 + (0.8 / tCK_avg) + 6);
const int tRW2PDEN = (int)((tDQSS_max / tCK_avg) + 46 + (tDQS2DQ_max / tCK_avg) + 6);
/* Refresh Cycle time. (All Banks) */
const u32 tRFCab = tRFCpb * 2;
/* Do not touch stuff. */
/* ACTIVATE-to-ACTIVATE command period. (same bank) */
const u32 tRC = tRAS + tRPpb;
/* Minimum Self-Refresh Time. (Entry to Exit) */
const double tSR = 15.0;
/* SELF REFRESH exit to next valid command delay. */
const double tXSR = (double) (tRFCab + 7.5);
/* Exit power down to next valid command delay. */
const double tXP = 7.5; // I assume this is correct.
/* u32ernal READ to PRECHARGE command delay. */
const int pdex2mrr = (tCK_avg * 3.0) + tRCD_values[C.t1_tRCD] + 1;
/* Row Precharge Time. (all banks) */
const double tRPab = tRPpb + 3;
const u32 tRPab = tRPpb + 3;
}
/* TODO. */
namespace pcv::mariko {
const double tCK_avg = 1000'000.0 / C.marikoEmcMaxClock;
const u32 tRCD = tRCD_values[C.t1_tRCD];
const u32 tRPpb = tRP_values[C.t2_tRP];
const u32 tRAS = tRAS_values[C.t3_tRAS];
const double tRRD = tRRD_values[C.t4_tRRD];
const u32 tRFCpb = tRFC_values[C.t5_tRFC];
const u32 tWTR = tWTR_values[C.t7_tWTR];
const u32 RL = 36;
const u32 WL = 18;
const u32 tRC = tRAS + tRPpb;
const u32 tRFCab = tRFCpb * 2;
const double tXSR = (double) (tRFCab + 7.5);
const u32 tFAW = (u32) (tRRD * 4.0);
const double tRPab = tRPpb + 3;
const double tR2P = 7.5; /* Tighten up? */
const u32 tR2W = CEIL(RL + (tDQSCK_max / tCK_avg) + (BL / 2) - WL + tWPRE + FLOOR(tRPST)); /* TODO */
const u32 tRTM = RL + 9 + (tDQSCK_max / tCK_avg) + FLOOR(tRPST) + CEIL(7.5 / tCK_avg);
const u32 tRATM = tRTM + CEIL(tR2P / tCK_avg) - (BL / 2);
/* Note: Dividing WL is probably incorect but it works out by pure chance :) */
const u32 tW2P = WL + (BL / 2) + 1 + CEIL(WL / tCK_avg); /* Tighten? */
const u32 tW2R = WL + (BL / 2) + 1 + CEIL(tWTR / tCK_avg);
const u32 tWTM = WL + (BL / 2) + 1 + CEIL(7.5 / tCK_avg);
const u32 tWATM = tWTM + CEIL(WL / tCK_avg);
const double tMMRI = tRCD + (tCK_avg * 3);
const double tPDEX2MRR = tMMRI + 10;
const u32 tWTPDEN = tW2P + 1 + CEIL(tDQSS_max / tCK_avg) + CEIL(tDQS2DQ_max / tCK_avg) + 6.0;
}
}

View File

@@ -26,8 +26,7 @@
namespace ams::ldr::oc::pcv
{
namespace mariko
{
namespace mariko {
constexpr cvb_entry_t CpuCvbTableDefault[] = {
// CPUB01_CVB_TABLE
{204000, {721589, -12695, 27}, {}},
@@ -119,8 +118,7 @@ namespace ams::ldr::oc::pcv
inline auto asm_set_imm16 = [](u32 ins, u16 imm)
{ return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); };
inline bool GpuMaxClockPatternFn(u32 *ptr32)
{
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
return asm_compare_no_rd(*ptr32, asm_pattern[0]);
}
@@ -168,8 +166,7 @@ namespace ams::ldr::oc::pcv
}
namespace erista
{
namespace erista {
constexpr cvb_entry_t CpuCvbTableDefault[] = {
// CPU_PLL_CVB_TABLE_ODN
{204000, {721094}, {}},
@@ -199,8 +196,7 @@ namespace ams::ldr::oc::pcv
constexpr u16 CpuMinVolts[] = {950, 850, 825, 810};
inline bool CpuMaxVoltPatternFn(u32 *ptr32)
{
inline bool CpuMaxVoltPatternFn(u32 *ptr32) {
u32 val = *ptr32;
return (val == 1132 || val == 1170 || val == 1227);
}
@@ -229,8 +225,7 @@ namespace ams::ldr::oc::pcv
inline auto asm_set_imm16 = [](u32 ins, u16 imm)
{ return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); };
inline bool GpuMaxClockPatternFn(u32 *ptr32)
{
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
return asm_compare_no_rd(*ptr32, asm_pattern[0]);
}
@@ -260,8 +255,7 @@ namespace ams::ldr::oc::pcv
}
template <bool isMariko>
Result CpuFreqCvbTable(u32 *ptr)
{
Result CpuFreqCvbTable(u32 *ptr) {
cvb_entry_t *default_table = isMariko ? (cvb_entry_t *)(&mariko::CpuCvbTableDefault) : (cvb_entry_t *)(&erista::CpuCvbTableDefault);
cvb_entry_t *customize_table = nullptr; // impossible to reach, there will always be a way to set a pointer
@@ -284,12 +278,9 @@ namespace ams::ldr::oc::pcv
}
u32 cpu_max_volt = isMariko ? C.marikoCpuMaxVolt : C.eristaCpuMaxVolt;
u32 cpu_freq_threshold = 1020'000;
if (isMariko)
{
if (isMariko) {
cpu_freq_threshold = C.marikoCpuUV ? 2193'000 : 2091'000;
}
else
{
} else {
cpu_freq_threshold = cpu_max_volt >= 1300 ? 1887'000 : 1428'000;
}
@@ -307,19 +298,13 @@ namespace ams::ldr::oc::pcv
std::memcpy(cpu_cvb_table_head, static_cast<void *>(customize_table), customize_table_size);
// Patch CPU max volt
if (cpu_max_volt)
{
if (cpu_max_volt) {
cvb_entry_t *entry = static_cast<cvb_entry_t *>(cpu_cvb_table_head);
for (size_t i = 0; i < customize_entry_count; i++)
{
if (entry->freq >= cpu_freq_threshold)
{
if (isMariko)
{
for (size_t i = 0; i < customize_entry_count; i++) {
if (entry->freq >= cpu_freq_threshold) {
if (isMariko) {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), cpu_max_volt * 1000);
}
else
{
} else {
PATCH_OFFSET(&(entry->cvb_dfll_param.c0), cpu_max_volt * 1000);
}
}
@@ -331,14 +316,11 @@ namespace ams::ldr::oc::pcv
}
template <bool isMariko>
Result GpuFreqCvbTable(u32 *ptr)
{
Result GpuFreqCvbTable(u32 *ptr) {
cvb_entry_t *default_table = isMariko ? (cvb_entry_t *)(&mariko::GpuCvbTableDefault) : (cvb_entry_t *)(&erista::GpuCvbTableDefault);
cvb_entry_t *customize_table;
if (isMariko)
{
switch (C.marikoGpuUV)
{
if (isMariko) {
switch (C.marikoGpuUV) {
case 0:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
break;
@@ -349,24 +331,18 @@ namespace ams::ldr::oc::pcv
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
break;
case 3:
if(C.enableMarikoGpuUnsafeFreqs)
{
if(C.enableMarikoGpuUnsafeFreqs) {
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableUv3UnsafeFreqs);
}
else
{
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
} else {
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
}
break;
default:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
break;
}
}
else
{
switch (C.eristaGpuUV)
{
} else {
switch (C.eristaGpuUV) {
case 0:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
break;
@@ -377,13 +353,10 @@ namespace ams::ldr::oc::pcv
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableHigh);
break;
case 3:
if(C.enableEristaGpuUnsafeFreqs)
{
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableUv3UnsafeFreqs);
}
else
{
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
if(C.enableEristaGpuUnsafeFreqs) {
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableUv3UnsafeFreqs);
} else {
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
}
break;
default:
@@ -406,23 +379,16 @@ namespace ams::ldr::oc::pcv
std::memcpy(gpu_cvb_table_head, (void *)customize_table, customize_table_size);
// Patch GPU volt
if (C.marikoGpuUV == 3 || C.eristaGpuUV == 3)
{
if (C.marikoGpuUV == 3 || C.eristaGpuUV == 3) {
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
for (size_t i = 0; i < customize_entry_count; i++)
{
if (isMariko)
{
if (C.marikoGpuVoltArray[i] == 0)
{
for (size_t i = 0; i < customize_entry_count; i++) {
if (isMariko) {
if (C.marikoGpuVoltArray[i] == 0) {
continue;
}
PATCH_OFFSET(&(entry->cvb_pll_param.c0), C.marikoGpuVoltArray[i] * 1000);
}
else
{
if (C.eristaGpuVoltArray[i] == 0)
{
} else {
if (C.eristaGpuVoltArray[i] == 0) {
continue;
}
PATCH_OFFSET(&(entry->cvb_pll_param.c0), C.eristaGpuVoltArray[i] * 1000);
@@ -434,12 +400,9 @@ namespace ams::ldr::oc::pcv
PATCH_OFFSET(&(entry->cvb_pll_param.c5), 0);
entry++;
}
}
else if (C.commonGpuVoltOffset)
{
} else if (C.commonGpuVoltOffset) {
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
for (size_t i = 0; i < customize_entry_count; i++)
{
for (size_t i = 0; i < customize_entry_count; i++) {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
entry++;
}
@@ -452,21 +415,18 @@ namespace ams::ldr::oc::pcv
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
template <typename T>
Result MemMtcCustomizeTable(T *dst, T *src)
{
Result MemMtcCustomizeTable(T *dst, T *src) {
constexpr u32 mtc_magic = std::is_same_v<T, MarikoMtcTable> ? MARIKO_MTC_MAGIC : ERISTA_MTC_MAGIC;
R_UNLESS(src->rev == mtc_magic, ldr::ResultInvalidMtcMagic());
constexpr u32 ZERO_VAL = UINT32_MAX;
// Skip params from dvfs_ver to clock_src;
for (size_t offset = offsetof(T, clk_src_emc); offset < sizeof(T); offset += sizeof(u32))
{
for (size_t offset = offsetof(T, clk_src_emc); offset < sizeof(T); offset += sizeof(u32)) {
u32 *src_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(src) + offset);
u32 *dst_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(dst) + offset);
u32 src_val = *src_ent;
if (src_val)
{
if (src_val){
PATCH_OFFSET(dst_ent, src_val == ZERO_VAL ? 0 : src_val);
}
}

View File

@@ -21,47 +21,38 @@
#include "pcv.hpp"
#include "../mtc_timing_value.hpp"
namespace ams::ldr::oc::pcv::mariko
{
namespace ams::ldr::oc::pcv::mariko {
Result GpuVmin(u32 *ptr)
{
Result GpuVmin(u32 *ptr) {
if (!C.marikoGpuVmin)
R_SKIP();
PATCH_OFFSET(ptr, (int)C.marikoGpuVmin);
R_SUCCEED();
}
Result GpuVmax(u32 *ptr)
{
Result GpuVmax(u32 *ptr) {
if (!C.marikoGpuVmax)
R_SKIP();
PATCH_OFFSET(ptr, (int)C.marikoGpuVmax);
R_SUCCEED();
}
Result CpuFreqVdd(u32 *ptr)
{
Result CpuFreqVdd(u32 *ptr) {
dvfs_rail *entry = reinterpret_cast<dvfs_rail *>(reinterpret_cast<u8 *>(ptr) - offsetof(dvfs_rail, freq));
R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry());
R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry());
R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry());
R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry());
if (C.enableMarikoCpuUnsafeFreqs)
{
if (C.enableMarikoCpuUnsafeFreqs) {
PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
}
else
{
if (C.marikoCpuUV)
{
if (!C.enableMarikoCpuUnsafeFreqs)
{
else {
if (C.marikoCpuUV) {
if (!C.enableMarikoCpuUnsafeFreqs) {
PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq);
}
else
{
else {
PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTableUnsafeFreqs)->freq);
}
}
@@ -69,11 +60,9 @@ namespace ams::ldr::oc::pcv::mariko
R_SUCCEED();
}
Result CpuVoltRange(u32 *ptr)
{
Result CpuVoltRange(u32 *ptr) {
u32 min_volt_got = *(ptr - 1);
for (const auto &mv : CpuMinVolts)
{
for (const auto &mv : CpuMinVolts) {
if (min_volt_got != mv)
continue;
@@ -82,14 +71,11 @@ namespace ams::ldr::oc::pcv::mariko
PATCH_OFFSET(ptr, C.marikoCpuMaxVolt);
// Patch vmin for slt
if (C.marikoCpuUV)
{
if (*(ptr - 5) == 620)
{
if (C.marikoCpuUV) {
if (*(ptr - 5) == 620) {
PATCH_OFFSET((ptr - 5), C.marikoCpuVmin);
}
if (*(ptr - 1) == 620)
{
if (*(ptr - 1) == 620) {
PATCH_OFFSET((ptr - 1), 600);
}
}
@@ -98,16 +84,14 @@ namespace ams::ldr::oc::pcv::mariko
R_THROW(ldr::ResultInvalidCpuMinVolt());
}
Result CpuVoltDfll(u32 *ptr)
{
Result CpuVoltDfll(u32 *ptr) {
cvb_cpu_dfll_data *entry = reinterpret_cast<cvb_cpu_dfll_data *>(ptr);
R_UNLESS(entry->tune0_low == 0x0000FFCF, ldr::ResultInvalidCpuVoltDfllEntry());
R_UNLESS(entry->tune0_high == 0x00000000, ldr::ResultInvalidCpuVoltDfllEntry());
R_UNLESS(entry->tune1_low == 0x012207FF, ldr::ResultInvalidCpuVoltDfllEntry());
R_UNLESS(entry->tune1_high == 0x03FFF7FF, ldr::ResultInvalidCpuVoltDfllEntry());
switch (C.marikoCpuUV)
{
switch (C.marikoCpuUV) {
case 0:
break;
case 1:
@@ -164,8 +148,7 @@ namespace ams::ldr::oc::pcv::mariko
R_SUCCEED();
}
Result GpuFreqMaxAsm(u32 *ptr32)
{
Result GpuFreqMaxAsm(u32 *ptr32) {
// Check if both two instructions match the pattern
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1])))
@@ -177,8 +160,7 @@ namespace ams::ldr::oc::pcv::mariko
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
u32 max_clock;
switch (C.marikoGpuUV)
{
switch (C.marikoGpuUV) {
case 0:
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq;
break;
@@ -189,12 +171,10 @@ namespace ams::ldr::oc::pcv::mariko
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq;
break;
case 3:
if (C.enableMarikoGpuUnsafeFreqs)
{
if (C.enableMarikoGpuUnsafeFreqs) {
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableUv3UnsafeFreqs)->freq;
}
else
{
else {
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq;
}
break;
@@ -204,20 +184,20 @@ namespace ams::ldr::oc::pcv::mariko
}
u32 asm_patch[2] = {
asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd),
asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)};
asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)
};
PATCH_OFFSET(ptr32, asm_patch[0]);
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
R_SUCCEED();
}
Result GpuFreqPllLimit(u32 *ptr)
{
Result GpuFreqPllLimit(u32 *ptr) {
clk_pll_param *entry = reinterpret_cast<clk_pll_param *>(ptr);
// All zero except for freq
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++)
{
for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) {
R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry());
}
@@ -227,183 +207,193 @@ namespace ams::ldr::oc::pcv::mariko
R_SUCCEED();
}
/* Get RAM vendor data, ty b0rd2death! */
/* Note: I know this is horrible but I don't care atm. */
bool IsMicron()
{
u64 packed_version;
splGetConfig((SplConfigItem)2, &packed_version);
switch (packed_version)
{
case 11:
case 15:
case 25:
case 26:
case 27:
case 32:
case 33:
case 34:
/* RAM is Micron. */
return true;
default:
/* Not Micron. */
return false;
}
}
void MemMtcTableAutoAdjustBaseLatency(MarikoMtcTable *table) {
#define WRITE_PARAM_ALL_REG(TABLE, PARAM, VALUE) \
TABLE->burst_regs.PARAM = VALUE; \
TABLE->shadow_regs_ca_train.PARAM = VALUE; \
TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
WRITE_PARAM_ALL_REG(table, emc_cfg, 0xf3200000);
WRITE_PARAM_ALL_REG(table, emc_rc, 0x00000070);
WRITE_PARAM_ALL_REG(table, emc_rfc, 0x0000020b);
WRITE_PARAM_ALL_REG(table, emc_ras, 0x0000004f);
WRITE_PARAM_ALL_REG(table, emc_rp, 0x00000022);
WRITE_PARAM_ALL_REG(table, emc_r2w, 0x0000002e);
WRITE_PARAM_ALL_REG(table, emc_w2r, 0x00000025);
WRITE_PARAM_ALL_REG(table, emc_r2p, 0x0000000e);
WRITE_PARAM_ALL_REG(table, emc_w2p, 0x00000033);
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, 0x00000022);
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, 0x00000022);
WRITE_PARAM_ALL_REG(table, emc_rrd, 0x00000013);
WRITE_PARAM_ALL_REG(table, emc_rext, 0x0000001a);
#define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg))
WRITE_PARAM_ALL_REG(table, emc_qsafe, 0x00000038);
WRITE_PARAM_ALL_REG(table, emc_refresh, 0x00001c2d);
WRITE_PARAM_ALL_REG(table, emc_burst_refresh_num, 0x00000000);
WRITE_PARAM_ALL_REG(table, emc_pdex2wr, 0x00000013);
WRITE_PARAM_ALL_REG(table, emc_pdex2rd, 0x00000013);
WRITE_PARAM_ALL_REG(table, emc_pchg2pden, 0x00000004);
WRITE_PARAM_ALL_REG(table, emc_act2pden, 0x0000001b);
WRITE_PARAM_ALL_REG(table, emc_ar2pden, 0x00000004);
WRITE_PARAM_ALL_REG(table, emc_rw2pden, 0x0000003f);
WRITE_PARAM_ALL_REG(table, emc_txsr, 0x00000219);
WRITE_PARAM_ALL_REG(table, emc_tcke, 0x00000010);
WRITE_PARAM_ALL_REG(table, emc_tfaw, 0x0000004b);
WRITE_PARAM_ALL_REG(table, emc_trpab, 0x00000028);
WRITE_PARAM_ALL_REG(table, emc_tclkstable, 0x00000004);
WRITE_PARAM_ALL_REG(table, emc_tclkstop, 0x00000017);
WRITE_PARAM_ALL_REG(table, emc_trefbw, 0x00001c6d);
WRITE_PARAM_ALL_REG(table, emc_tppd, 0x00000004);
WRITE_PARAM_ALL_REG(table, emc_odt_write, 0x00000000);
WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, 0x00000037);
WRITE_PARAM_ALL_REG(table, emc_wext, 0x00000016);
WRITE_PARAM_ALL_REG(table, emc_rfc_slr, 0x00000000);
WRITE_PARAM_ALL_REG(table, emc_mrs_wait_cnt2, 0x01d3001b);
WRITE_PARAM_ALL_REG(table, emc_mrs_wait_cnt, 0x074a0034);
table->emc_mrs = 0x00000000;
table->emc_emrs = 0x00000000;
table->emc_mrw = 0x00170040;
WRITE_PARAM_ALL_REG(table, emc_fbio_spare, 0x00000012);
WRITE_PARAM_ALL_REG(table, emc_fbio_cfg5, 0x9160a00d);
WRITE_PARAM_ALL_REG(table, emc_pdex2cke, 0x00000002);
WRITE_PARAM_ALL_REG(table, emc_cke2pden, 0x00000010);
WRITE_PARAM_ALL_REG(table, emc_r2r, 0x00000000);
WRITE_PARAM_ALL_REG(table, emc_einput, 0x00000015);
WRITE_PARAM_ALL_REG(table, emc_einput_duration, 0x00000020);
WRITE_PARAM_ALL_REG(table, emc_puterm_extra, 0x00000001);
WRITE_PARAM_ALL_REG(table, emc_tckesr, 0x0000001c);
WRITE_PARAM_ALL_REG(table, emc_tpd, 0x0000000e);
table->emc_cfg_2 = 0x0011083d;
WRITE_PARAM_ALL_REG(table, emc_cfg_dig_dll, 0x002c03a9);
WRITE_PARAM_ALL_REG(table, emc_cfg_dig_dll_period, 0x00008000);
WRITE_PARAM_ALL_REG(table, emc_rdv_mask, 0x00000040);
WRITE_PARAM_ALL_REG(table, emc_wdv_mask, 0x00000010);
WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, 0x0000003e);
WRITE_PARAM_ALL_REG(table, emc_rdv_early, 0x0000003c);
WRITE_PARAM_ALL_REG(table, emc_fdpd_ctrl_dq, 0x8020221f);
WRITE_PARAM_ALL_REG(table, emc_fdpd_ctrl_cmd, 0x0220f40f);
table->emc_sel_dpd_ctrl = 0x0004000c;
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, 0x0000070b);
WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, 0x80003873);
WRITE_PARAM_ALL_REG(table, emc_txsrdll, 0x00000219);
WRITE_PARAM_ALL_REG(table, emc_ibdly, 0x1000001f);
WRITE_PARAM_ALL_REG(table, emc_obdly, 0x10000004);
WRITE_PARAM_ALL_REG(table, emc_txdsrvttgen, 0x00000000);
WRITE_PARAM_ALL_REG(table, emc_we_duration, 0x0000000e);
WRITE_PARAM_ALL_REG(table, emc_ws_duration, 0x00000008);
WRITE_PARAM_ALL_REG(table, emc_wev, 0x0000000c);
WRITE_PARAM_ALL_REG(table, emc_cfg_3, 0x00000040);
WRITE_PARAM_ALL_REG(table, emc_wdv_chk, 0x00000006);
WRITE_PARAM_ALL_REG(table, emc_cfg_pipe_2, 0x00000000);
WRITE_PARAM_ALL_REG(table, emc_cfg_pipe_1, 0x0fff0000);
WRITE_PARAM_ALL_REG(table, emc_cfg_pipe, 0x0fff0000);
WRITE_PARAM_ALL_REG(table, emc_quse_width, 0x00000009);
WRITE_PARAM_ALL_REG(table, emc_puterm_width, 0x80000000);
WRITE_PARAM_ALL_REG(table, emc_fbio_cfg7, 0x00003bff);
WRITE_PARAM_ALL_REG(table, emc_rfcpb, 0x00000106);
WRITE_PARAM_ALL_REG(table, emc_ccdmw, 0x00000020);
WRITE_PARAM_ALL_REG(table, emc_config_sample_delay, 0x00000020);
table->dram_timings.t_rp = 0x00000106;
table->dram_timings.t_rfc = 0x0000020b;
if (C.hpMode) {
WRITE_PARAM_ALL_REG(table, emc_cfg, 0x13200000);
} else {
WRITE_PARAM_ALL_REG(table, emc_cfg, 0xF3200000);
}
table->dram_timings.rl = 32; /* */
WRITE_PARAM_ALL_REG(table, emc_wdv, 0x00000010); /* */
WRITE_PARAM_ALL_REG(table, emc_quse, 0x00000028); /* */
WRITE_PARAM_ALL_REG(table, emc_qrst, 0x0007000c); /* These timings cause issues and I have no idea why. */
WRITE_PARAM_ALL_REG(table, emc_rdv, 0x0000003e); /* */
WRITE_PARAM_ALL_REG(table, emc_wsv, 0x0000000e); /* */
WRITE_PARAM_ALL_REG(table, emc_qpop, 0x00000030); /* */
u32 refresh_raw = 0xFFFF;
if (C.t8_tREFI != 6) {
refresh_raw = std::floor(tREFpb_values[C.t8_tREFI] / tCK_avg) - 0x40;
refresh_raw = MIN(refresh_raw, static_cast<u32>(0xFFFF));
}
table->burst_mc_regs.mc_emem_arb_cfg = 0x0000000E;
table->burst_mc_regs.mc_emem_arb_outstanding_req = 0x80000080;
table->burst_mc_regs.mc_emem_arb_timing_rcd = 0x00000007;
table->burst_mc_regs.mc_emem_arb_timing_rp = 0x00000008;
table->burst_mc_regs.mc_emem_arb_timing_rc = 0x0000001C;
table->burst_mc_regs.mc_emem_arb_timing_ras = 0x00000012;
table->burst_mc_regs.mc_emem_arb_timing_faw = 0x00000012;
table->burst_mc_regs.mc_emem_arb_timing_rrd = 0x00000004;
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = 0x00000004;
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = 0x0000000F;
table->burst_mc_regs.mc_emem_arb_timing_r2r = 0x00000001;
table->burst_mc_regs.mc_emem_arb_timing_w2w = 0x00000001;
table->burst_mc_regs.mc_emem_arb_timing_r2w = 0x0000000D;
table->burst_mc_regs.mc_emem_arb_timing_w2r = 0x0000000B;
table->burst_mc_regs.mc_emem_arb_da_turns = 0x05060000;
table->burst_mc_regs.mc_emem_arb_da_covers = 0x000F0A0E;
table->burst_mc_regs.mc_emem_arb_misc0 = 0x726E2A1D;
table->burst_mc_regs.mc_emem_arb_misc1 = 0x70000F0F;
table->burst_mc_regs.mc_emem_arb_misc2 = 0x00000000;
table->burst_mc_regs.mc_emem_arb_ring1_throttle = 0x001F0000;
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = 0x00000041;
table->burst_mc_regs.mc_emem_arb_timing_ccdmw = 0x00000008;
table->burst_mc_regs.mc_emem_arb_dhyst_ctrl = 0x00000002;
table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_0 = 0x0000001A;
table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_1 = 0x0000001A;
table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_2 = 0x0000001A;
table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_3 = 0x0000001A;
table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_4 = 0x0000001A;
table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_5 = 0x0000001A;
table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_6 = 0x0000001A;
table->burst_mc_regs.mc_emem_arb_dhyst_timeout_util_7 = 0x0000001A;
table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = 0x000000F2;
table->la_scale_regs.mc_ftop_ptsa_rate = 0x0000001B;
table->la_scale_regs.mc_ptsa_grant_decrement = 0x00001501;
table->la_scale_regs.mc_latency_allowance_avpc_0 = 0x006D0004;
table->la_scale_regs.mc_latency_allowance_sdmmcaa_0 = 0x006D0005;
table->la_scale_regs.mc_latency_allowance_sdmmca_0 = 0x006D0014;
table->la_scale_regs.mc_latency_allowance_isp2_0 = 0x0000002C;
table->la_scale_regs.mc_latency_allowance_isp2_1 = 0x006D006D;
table->la_scale_regs.mc_latency_allowance_vic_0 = 0x006D0019;
table->la_scale_regs.mc_latency_allowance_nvdec_0 = 0x006D0095;
table->la_scale_regs.mc_latency_allowance_tsec_0 = 0x006D0041;
table->la_scale_regs.mc_latency_allowance_ppcs_1 = 0x006D0080;
table->la_scale_regs.mc_latency_allowance_xusb_0 = 0x006D003D;
table->la_scale_regs.mc_latency_allowance_ppcs_0 = 0x00340049;
table->la_scale_regs.mc_latency_allowance_gpu2_0 = 0x006D0016;
table->la_scale_regs.mc_latency_allowance_hc_1 = 0x0000006D;
table->la_scale_regs.mc_latency_allowance_sdmmc_0 = 0x006D0090;
table->la_scale_regs.mc_latency_allowance_mpcore_0 = 0x006D0004;
table->la_scale_regs.mc_latency_allowance_vi2_0 = 0x0000006D;
table->la_scale_regs.mc_latency_allowance_hc_0 = 0x00080013;
table->la_scale_regs.mc_latency_allowance_gpu_0 = 0x006D0016;
table->la_scale_regs.mc_latency_allowance_sdmmcab_0 = 0x006D0005;
table->la_scale_regs.mc_latency_allowance_nvenc_0 = 0x006D0018;
u32 trefbw = refresh_raw + 0x40;
trefbw = MIN(trefbw, static_cast<u32>(0x3FFF));
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_rc, GET_CYCLE_CEIL(tRC));
WRITE_PARAM_ALL_REG(table, emc_ras, GET_CYCLE_CEIL(tRAS));
WRITE_PARAM_ALL_REG(table, emc_rrd, GET_CYCLE_CEIL(tRRD));
WRITE_PARAM_ALL_REG(table, emc_rfcpb, GET_CYCLE_CEIL(tRFCpb));
WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
WRITE_PARAM_ALL_REG(table, emc_rp, GET_CYCLE_CEIL(tRPpb));
WRITE_PARAM_ALL_REG(table, emc_txsr, MIN(GET_CYCLE_CEIL(tXSR), static_cast<u32>(0x3fe)));
WRITE_PARAM_ALL_REG(table, emc_txsrdll, MIN(GET_CYCLE_CEIL(tXSR), static_cast<u32>(0x3fe)));
WRITE_PARAM_ALL_REG(table, emc_tfaw, GET_CYCLE_CEIL(tFAW));
WRITE_PARAM_ALL_REG(table, emc_trpab, GET_CYCLE_CEIL(tRPab));
WRITE_PARAM_ALL_REG(table, emc_tckesr, GET_CYCLE_CEIL(tSR));
WRITE_PARAM_ALL_REG(table, emc_tcke, GET_CYCLE_CEIL(tXP) + 1);
WRITE_PARAM_ALL_REG(table, emc_tpd, GET_CYCLE_CEIL(tXP));
WRITE_PARAM_ALL_REG(table, emc_tclkstop, GET_CYCLE_CEIL(tXP) + 8);
WRITE_PARAM_ALL_REG(table, emc_r2p, GET_CYCLE_CEIL(tR2P));
WRITE_PARAM_ALL_REG(table, emc_r2w, tR2W);
WRITE_PARAM_ALL_REG(table, emc_trtm, tRTM);
WRITE_PARAM_ALL_REG(table, emc_tratm, tRATM);
WRITE_PARAM_ALL_REG(table, emc_w2p, tW2P);
WRITE_PARAM_ALL_REG(table, emc_w2r, tW2R);
WRITE_PARAM_ALL_REG(table, emc_twtm, tWTM);
WRITE_PARAM_ALL_REG(table, emc_twatm, tWATM);
WRITE_PARAM_ALL_REG(table, emc_rext, 26);
WRITE_PARAM_ALL_REG(table, emc_refresh, refresh_raw);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, refresh_raw / 4);
WRITE_PARAM_ALL_REG(table, emc_trefbw, trefbw);
const u32 dyn_self_ref_control = (((u32)(7605.0 / tCK_avg)) + 260U) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000U);
WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, dyn_self_ref_control);
WRITE_PARAM_ALL_REG(table, emc_pdex2wr, GET_CYCLE_CEIL(10.0));
WRITE_PARAM_ALL_REG(table, emc_pdex2rd, GET_CYCLE_CEIL(10.0));
WRITE_PARAM_ALL_REG(table, emc_pchg2pden, GET_CYCLE_CEIL(1.75));
WRITE_PARAM_ALL_REG(table, emc_ar2pden, GET_CYCLE_CEIL(1.75));
WRITE_PARAM_ALL_REG(table, emc_act2pden, GET_CYCLE_CEIL(14.0));
WRITE_PARAM_ALL_REG(table, emc_cke2pden, GET_CYCLE_CEIL(5.0));
WRITE_PARAM_ALL_REG(table, emc_pdex2mrr, GET_CYCLE_CEIL(tPDEX2MRR));
WRITE_PARAM_ALL_REG(table, emc_rw2pden, tWTPDEN);
WRITE_PARAM_ALL_REG(table, emc_einput, 0xF);
WRITE_PARAM_ALL_REG(table, emc_einput_duration, 0x31);
WRITE_PARAM_ALL_REG(table, emc_obdly, 0x10000002);
WRITE_PARAM_ALL_REG(table, emc_ibdly, 0x1000001C);
WRITE_PARAM_ALL_REG(table, emc_wdv_mask, 0x12);
WRITE_PARAM_ALL_REG(table, emc_quse_width, 0xD);
WRITE_PARAM_ALL_REG(table, emc_quse, 0x2F);
WRITE_PARAM_ALL_REG(table, emc_wdv, 0x12);
WRITE_PARAM_ALL_REG(table, emc_wsv, 0x10);
WRITE_PARAM_ALL_REG(table, emc_wev, 0xE);
WRITE_PARAM_ALL_REG(table, emc_qrst, 0x00080005);
WRITE_PARAM_ALL_REG(table, emc_qsafe, 0x44);
WRITE_PARAM_ALL_REG(table, emc_tr_qpop, 0x3B);
WRITE_PARAM_ALL_REG(table, emc_rdv, 0x49);
WRITE_PARAM_ALL_REG(table, emc_qpop, 0x3B);
WRITE_PARAM_ALL_REG(table, emc_tr_rdv_mask, 0x4B);
WRITE_PARAM_ALL_REG(table, emc_rdv_early, 0x47);
WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, 0x49);
WRITE_PARAM_ALL_REG(table, emc_rdv_mask, 0x4B);
WRITE_PARAM_ALL_REG(table, emc_tr_rdv, 0x49);
constexpr u32 MC_ARB_DIV = 4;
constexpr u32 MC_ARB_SFA = 2;
table->burst_mc_regs.mc_emem_arb_cfg = C.marikoEmcMaxClock / (33.3 * 1000) / MC_ARB_DIV;
table->burst_mc_regs.mc_emem_arb_timing_rcd = (u32) (GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rp = (u32) (GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_rc = (u32) (GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_ras = (u32) (GET_CYCLE_CEIL(tRAS) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_faw = (u32) (GET_CYCLE_CEIL(tFAW) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rrd = (u32) (GET_CYCLE_CEIL(tRRD) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rfcpb = (u32) (GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_rap2pre = (u32) (GET_CYCLE_CEIL(tR2P) / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = (u32) (tW2P / MC_ARB_DIV);
table->burst_mc_regs.mc_emem_arb_timing_r2r = (u32) (table->burst_regs.emc_rext / 4) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_r2w = (u32) (tR2W / MC_ARB_DIV) - 1 + MC_ARB_SFA;
table->burst_mc_regs.mc_emem_arb_timing_w2r = (u32) (tW2R / MC_ARB_DIV) - 1 + MC_ARB_SFA;
u32 da_turns = 0;
da_turns |= u8(table->burst_mc_regs.mc_emem_arb_timing_r2w / 2) << 16;
da_turns |= u8(table->burst_mc_regs.mc_emem_arb_timing_w2r / 2) << 24;
table->burst_mc_regs.mc_emem_arb_da_turns = da_turns;
u32 da_covers = 0;
u8 r_cover = (table->burst_mc_regs.mc_emem_arb_timing_rap2pre + table->burst_mc_regs.mc_emem_arb_timing_rp + table->burst_mc_regs.mc_emem_arb_timing_rcd) / 2;
u8 w_cover = (table->burst_mc_regs.mc_emem_arb_timing_wap2pre + table->burst_mc_regs.mc_emem_arb_timing_rp + table->burst_mc_regs.mc_emem_arb_timing_rcd) / 2;
da_covers |= (u8) (table->burst_mc_regs.mc_emem_arb_timing_rc / 2);
da_covers |= (r_cover << 8);
da_covers |= (w_cover << 16);
table->burst_mc_regs.mc_emem_arb_da_covers = da_covers;
table->burst_mc_regs.mc_emem_arb_misc0 &= 0xFFE08000U;
table->burst_mc_regs.mc_emem_arb_misc0 |= ((table->burst_mc_regs.mc_emem_arb_timing_rc + 1) & 0xFF); /* TODO, check this */
table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0xd0U), (u32)0x115);
table->la_scale_regs.mc_ftop_ptsa_rate = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0x18U), (u32)0x1f);
table->la_scale_regs.mc_ptsa_grant_decrement = MIN((u32)((C.marikoEmcMaxClock / 1600000) * 0x1203U), (u32)0x17ff);
u32 mc_latency_allowance = 0;
if (C.marikoEmcMaxClock / 1000 != 0) {
mc_latency_allowance = 204800 / (C.marikoEmcMaxClock / 1000);
}
const u32 mc_latency_allowance2 = mc_latency_allowance & 0xFF;
const u32 mc_latency_allowance3 = (mc_latency_allowance & 0xFF) << 0x10;
table->la_scale_regs.mc_latency_allowance_xusb_0 = (table->la_scale_regs.mc_latency_allowance_xusb_0 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_sdmmc_0 = (table->la_scale_regs.mc_latency_allowance_sdmmc_0 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_xusb_1 = (table->la_scale_regs.mc_latency_allowance_xusb_1 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_tsec_0 = (table->la_scale_regs.mc_latency_allowance_tsec_0 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_sdmmca_0 = (table->la_scale_regs.mc_latency_allowance_sdmmca_0 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_sdmmcaa_0 = (table->la_scale_regs.mc_latency_allowance_sdmmcaa_0 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_sdmmcab_0 = (table->la_scale_regs.mc_latency_allowance_sdmmcab_0 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_ppcs_1 = (table->la_scale_regs.mc_latency_allowance_ppcs_1 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_mpcore_0 = (table->la_scale_regs.mc_latency_allowance_mpcore_0 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_avpc_0 = (table->la_scale_regs.mc_latency_allowance_avpc_0 & 0xff00ffffU) | mc_latency_allowance3;
u32 mc_latency_allowance_hc_0 = 0;
if (C.marikoEmcMaxClock / 1000 != 0) {
mc_latency_allowance_hc_0 = 35200 / (C.marikoEmcMaxClock / 1000);
}
table->la_scale_regs.mc_latency_allowance_nvdec_0 = (table->la_scale_regs.mc_latency_allowance_nvdec_0 & 0xff00ffffU) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_hc_0 = (table->la_scale_regs.mc_latency_allowance_hc_0 & 0xffffff00U) | mc_latency_allowance_hc_0;
table->la_scale_regs.mc_latency_allowance_isp2_1 = (table->la_scale_regs.mc_latency_allowance_isp2_1 & 0xff00ff00U) | mc_latency_allowance3 | mc_latency_allowance2;
table->la_scale_regs.mc_latency_allowance_hc_1 = (table->la_scale_regs.mc_latency_allowance_hc_1 & 0xffffff00U) | mc_latency_allowance2;
u32 mc_latency_allowance_gpu_0 = 0;
if (C.marikoEmcMaxClock / 1000 != 0) {
mc_latency_allowance_gpu_0 = 40000 / (C.marikoEmcMaxClock / 1000);
}
table->la_scale_regs.mc_latency_allowance_gpu_0 = ((mc_latency_allowance_gpu_0 | table->la_scale_regs.mc_latency_allowance_gpu_0) & 0xff00ff00U) | mc_latency_allowance3;
u32 mc_latency_allowance_gpu2_0 = 0;
if (C.marikoEmcMaxClock / 1000 != 0) {
mc_latency_allowance_gpu2_0 = 40000 / (C.marikoEmcMaxClock / 1000);
}
table->la_scale_regs.mc_latency_allowance_gpu2_0 = ((mc_latency_allowance_gpu2_0 | table->la_scale_regs.mc_latency_allowance_gpu2_0) & 0xff00ff00U) | mc_latency_allowance3;
u32 mc_latency_allowance_nvenc_0 = 0;
if (C.marikoEmcMaxClock / 1000 != 0) {
mc_latency_allowance_nvenc_0 = 38400 / (C.marikoEmcMaxClock / 1000);
}
table->la_scale_regs.mc_latency_allowance_nvenc_0 = ((mc_latency_allowance_nvenc_0 | table->la_scale_regs.mc_latency_allowance_nvenc_0) & 0xff00ff00U) | mc_latency_allowance3;
u32 mc_latency_allowance_vic_0 = 0;
if (C.marikoEmcMaxClock / 1000 != 0) {
mc_latency_allowance_vic_0 = 0xb540 / (C.marikoEmcMaxClock / 1000);
}
table->la_scale_regs.mc_latency_allowance_vic_0 = ((mc_latency_allowance_vic_0 | table->la_scale_regs.mc_latency_allowance_vic_0) & 0xff00ff00U) | mc_latency_allowance3;
table->la_scale_regs.mc_latency_allowance_vi2_0 = (table->la_scale_regs.mc_latency_allowance_vi2_0 & 0xffffff00U) | mc_latency_allowance2;
table->pllm_ss_ctrl1 = 0xb55fe01;
table->pllm_ss_ctrl2 = 0x10170b55;
table->pllmb_ss_ctrl1 = 0xb55fe01;
table->pllmb_ss_ctrl2 = 0x10170b55;
table->dram_timings.t_rp = tRFCpb;
table->dram_timings.t_rfc = tRFCab;
table->dram_timings.rl = RL - 10;
table->emc_mrw2 = 0x8802003F;
table->emc_cfg_2 = 0x11083D;
}
void MemMtcTableAutoAdjust(MarikoMtcTable *table) {
@@ -429,7 +419,7 @@ namespace ams::ldr::oc::pcv::mariko
TABLE->shadow_regs_ca_train.PARAM = VALUE; \
TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
#define GET_CYCLE(PARAM) ((u32)((double)(PARAM) / tCK_avg))
#define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg))
WRITE_PARAM_ALL_REG(table, emc_rc, 0x60);
// WRITE_PARAM_ALL_REG(table, emc_rfc, GET_CYCLE_CEIL(tRFCab));
@@ -483,28 +473,25 @@ namespace ams::ldr::oc::pcv::mariko
// WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rfcpb, CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV))
}
void MemMtcPllmbDivisor(MarikoMtcTable *table)
{
void MemMtcPllmbDivisor(MarikoMtcTable *table) {
// Calculate DIVM and DIVN (clock divisors)
// Common PLL oscillator is 38.4 MHz
// PLLMB_OUT = 38.4 MHz / PLLLMB_DIVM * PLLMB_DIVN
typedef struct
{
typedef struct {
u8 numerator : 4;
u8 denominator : 4;
} pllmb_div;
constexpr pllmb_div div[] = {
{3, 4}, {2, 3}, {1, 2}, {1, 3}, {1, 4}, {0, 2}};
{3, 4}, {2, 3}, {1, 2}, {1, 3}, {1, 4}, {0, 2}
};
constexpr u32 pll_osc_in = 38'400;
u32 divm{}, divn{};
const u32 remainder = C.marikoEmcMaxClock % pll_osc_in;
for (const auto &index : div)
{
for (const auto &index : div) {
// Round down
if (remainder >= pll_osc_in * index.numerator / index.denominator)
{
if (remainder >= pll_osc_in * index.numerator / index.denominator) {
divm = index.denominator;
divn = C.marikoEmcMaxClock / pll_osc_in * divm + index.numerator;
break;
@@ -515,15 +502,13 @@ namespace ams::ldr::oc::pcv::mariko
table->pllmb_divn = divn;
}
Result MemFreqMtcTable(u32 *ptr)
{
Result MemFreqMtcTable(u32 *ptr) {
u32 khz_list[] = {1600000, 1331200, 204000};
u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
// Generate list for mtc table pointers
MarikoMtcTable *table_list[khz_list_size];
for (u32 i = 0; i < khz_list_size; i++)
{
for (u32 i = 0; i < khz_list_size; i++) {
u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(MarikoMtcTable, rate_khz) - i * sizeof(MarikoMtcTable);
table_list[i] = reinterpret_cast<MarikoMtcTable *>(table);
R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
@@ -546,6 +531,7 @@ namespace ams::ldr::oc::pcv::mariko
} else {
MemMtcTableAutoAdjustBaseLatency(table_max);
}
MemMtcPllmbDivisor(table_max);
// Overwrite 13312000 table with unmodified 1600000 table copied back
std::memcpy(reinterpret_cast<void *>(table_alt), reinterpret_cast<void *>(tmp), sizeof(MarikoMtcTable));
@@ -553,17 +539,10 @@ namespace ams::ldr::oc::pcv::mariko
delete tmp;
PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
// Handle customize table replacement
// if (C.mtcConf == CUSTOMIZED_ALL) {
// MemMtcCustomizeTable(table_list[0], reinterpret_cast<MarikoMtcTable *>(reinterpret_cast<u8 *>(C.marikoMtcTable)));
// }
R_SUCCEED();
}
Result MemFreqDvbTable(u32 *ptr)
{
Result MemFreqDvbTable(u32 *ptr) {
emc_dvb_dvfs_table_t *default_end = reinterpret_cast<emc_dvb_dvfs_table_t *>(ptr);
emc_dvb_dvfs_table_t *new_start = default_end + 1;
@@ -579,45 +558,25 @@ namespace ams::ldr::oc::pcv::mariko
#define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000),
if (C.marikoEmcMaxClock < 1862400)
{
if (C.marikoEmcMaxClock < 1862400) {
std::memcpy(new_start, default_end, sizeof(emc_dvb_dvfs_table_t));
}
else if (C.marikoEmcMaxClock < 2131200)
{
emc_dvb_dvfs_table_t oc_table = {1862400, {
700,
675,
650,
}};
} else if (C.marikoEmcMaxClock < 2131200) {
emc_dvb_dvfs_table_t oc_table = {1862400, {700, 675, 650, }};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
}
else if (C.marikoEmcMaxClock < 2400000)
{
emc_dvb_dvfs_table_t oc_table = {2131200, {
725,
700,
675,
}};
} else if (C.marikoEmcMaxClock < 2400000) {
emc_dvb_dvfs_table_t oc_table = {2131200, { 725, 700, 675} };
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
}
else if (C.marikoEmcMaxClock < 2665600)
{
} else if (C.marikoEmcMaxClock < 2665600) {
emc_dvb_dvfs_table_t oc_table = {2400000, {DVB_VOLT(750, 725, 700)}};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
}
else if (C.marikoEmcMaxClock < 2931200)
{
} else if (C.marikoEmcMaxClock < 2931200) {
emc_dvb_dvfs_table_t oc_table = {2665600, {DVB_VOLT(775, 750, 725)}};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
}
else if (C.marikoEmcMaxClock < 3200000)
{
else if (C.marikoEmcMaxClock < 3200000) {
emc_dvb_dvfs_table_t oc_table = {2931200, {DVB_VOLT(800, 775, 750)}};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
}
else
{
} else {
emc_dvb_dvfs_table_t oc_table = {3200000, {DVB_VOLT(800, 800, 775)}};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
}
@@ -629,8 +588,7 @@ namespace ams::ldr::oc::pcv::mariko
R_SUCCEED();
}
Result MemFreqMax(u32 *ptr)
{
Result MemFreqMax(u32 *ptr) {
if (C.marikoEmcMaxClock <= EmcClkOSLimit)
R_SKIP();
@@ -638,10 +596,8 @@ namespace ams::ldr::oc::pcv::mariko
R_SUCCEED();
}
Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val)
{
struct
{
Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) {
struct {
u8 reg;
u8 val;
} __attribute__((packed)) cmd;
@@ -658,15 +614,13 @@ namespace ams::ldr::oc::pcv::mariko
return res;
}
Result EmcVddqVolt(u32 *ptr)
{
Result EmcVddqVolt(u32 *ptr) {
regulator *entry = reinterpret_cast<regulator *>(reinterpret_cast<u8 *>(ptr) - offsetof(regulator, type_2_3.default_uv));
constexpr u32 uv_step = 5'000;
constexpr u32 uv_min = 250'000;
auto validator = [entry]()
{
auto validator = [entry]() {
R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
@@ -692,8 +646,7 @@ namespace ams::ldr::oc::pcv::mariko
R_SUCCEED();
}
void Patch(uintptr_t mapped_nso, size_t nso_size)
{
void Patch(uintptr_t mapped_nso, size_t nso_size) {
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
@@ -715,23 +668,20 @@ namespace ams::ldr::oc::pcv::mariko
{"GPU Vmax", &GpuVmax, 0, nullptr, gpuVmax},
};
for (uintptr_t ptr = mapped_nso;
ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable);
ptr += sizeof(u32))
{
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) {
u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
for (auto &entry : patches)
{
if (R_SUCCEEDED(entry.SearchAndApply(ptr32)))
for (auto &entry : patches) {
if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) {
break;
}
}
}
for (auto &entry : patches)
{
for (auto &entry : patches) {
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
if (R_FAILED(entry.CheckResult()))
if (R_FAILED(entry.CheckResult())) {
CRASH(entry.description);
}
}
}