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 d55d82a9..23751c7c 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -386,10 +386,10 @@ void MemMtcTableAutoAdjustBaseLatency(MarikoMtcTable *table) { 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->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; @@ -475,33 +475,104 @@ void MemMtcTableAutoAdjustBaseLatency(MarikoMtcTable *table) { // WRITE_PARAM_BURST_MC_REG(table, mc_emem_arb_timing_rfcpb, CEIL(GET_CYCLE_CEIL(tRFCpb) / MC_ARB_DIV)) } + /* NOTE: This is reverse engineered eos code, naming may be inaccurate. */ 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 u32 PllOscInKHz = 38400; + constexpr u32 PllOscHalfKHz = 19200; - constexpr pllmb_div div[] = { - {3, 4}, {2, 3}, {1, 2}, {1, 3}, {1, 4}, {0, 2} - }; + u32 target_freq_khz = C.marikoEmcMaxClock; + u32 target_freq_for_div = C.marikoEmcMaxClock; - 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; + u32 divm_candidate = (C.marikoEmcMaxClock / PllOscHalfKHz) & 0xFF; + + u32 remainder_half = C.marikoEmcMaxClock - (divm_candidate * PllOscHalfKHz); + u32 remainder_full = C.marikoEmcMaxClock - (((C.marikoEmcMaxClock / PllOscInKHz) & 0xFF) * PllOscInKHz); + + bool better_with_half = (remainder_half < remainder_full); + bool fractional_nonzero = (static_cast((((double)target_freq_khz / 19200.0 - (double)divm_candidate) - 0.5) * 8192.0)) != 0; + + divm_candidate = (better_with_half && fractional_nonzero) + 1; + + u32 div_step_khz = 0; + if (divm_candidate != 0) { + div_step_khz = PllOscInKHz / divm_candidate; + } + + table->pllmb_divm = divm_candidate; + + double div_step_d = static_cast(div_step_khz); + + u32 divn_integer = 0; + if (div_step_khz != 0) { + divn_integer = target_freq_for_div / div_step_khz; + } + + double divn_int_d = static_cast(divn_integer & 0xFF); + table->pllmb_divn = divn_integer & 0xFF; + + u32 divn_fraction = static_cast(((static_cast(target_freq_khz) / div_step_d - divn_int_d) - 0.5) * 8192.0); + + double fma_out = std::fma(static_cast(static_cast(divn_fraction)), 1.0 / 8192.0, divn_int_d + 0.5); + + u32 actual_freq_khz = static_cast(fma_out * (38400.0 / static_cast(divm_candidate))); + + constexpr u32 SscTargetCenterKHz = 0x241a31; + constexpr u32 SscTargetToleranceKHz = 0x20b6f; + + u32 diff_from_ssc_center = target_freq_for_div - SscTargetCenterKHz; + + if (diff_from_ssc_center > SscTargetToleranceKHz) { + table->pllm_ss_cfg &= 0xBFFFFFFF; + table->pllmb_ss_cfg &= 0xBFFFFFFF; + + u32 needs_adjustment = (target_freq_for_div < actual_freq_khz) ? 1 : 0; + u32 pll_misc = (table->pllm_ss_ctrl2 & 0xFFFF0000) | (divn_fraction - needs_adjustment); + + table->pllm_ss_ctrl2 = pll_misc; + table->pllmb_ss_ctrl2 = pll_misc; + return; + } + + u32 divn_fraction_ssc = static_cast(((((double)actual_freq_khz * 0.997) / div_step_d - divn_int_d) - 0.5) * 8192.0); + + double delta_scaled = (0.3 / div_step_d + 0.3 / div_step_d) * static_cast(static_cast(divn_fraction - divn_fraction_ssc)); + + s32 delta_int = static_cast(delta_scaled); + double delta_frac = delta_scaled - static_cast(delta_int); + + u32 setup_value; + if (delta_frac <= 0.5) { + double round_val = 0.0; + if (delta_int + static_cast(delta_frac + delta_frac) != 0) { + round_val = 0.5; + } + + if (static_cast(delta_frac + delta_frac) == 0) { + setup_value = static_cast(round_val); + } else { + setup_value = static_cast(round_val + round_val) | 0x1000; + } + } else { + s32 frac_doubled = static_cast((delta_frac - 0.5) + (delta_frac - 0.5)); + double round_val = 0.5; + if (delta_int + frac_doubled != 0) { + round_val = 1.0; + } + + if (frac_doubled == 0) { + setup_value = static_cast(round_val + round_val) | 0x1000; + } else { + setup_value = static_cast(round_val); } } - table->pllmb_divm = divm; - table->pllmb_divn = divn; + u32 ctrl1 = (divn_fraction_ssc & 0xFFFF) | (divn_fraction << 16); + u32 ctrl2 = (divn_fraction & 0xFFFF) | (setup_value << 16); + + table->pllm_ss_ctrl1 = ctrl1; + table->pllm_ss_ctrl2 = ctrl2; + table->pllmb_ss_ctrl1 = ctrl1; + table->pllmb_ss_ctrl2 = ctrl2; } Result MemFreqMtcTable(u32 *ptr) {