diff --git a/README.md b/README.md index 238a6fa0..a0fcc8f1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Switch OC Suite -[![License: GPL v2](https://img.shields.io/badge/License-GPL_v2-blue.svg)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) +[![License: GPL v2](https://img.shields.io/badge/License-GPL_v2-blue.svg)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. @@ -22,14 +22,7 @@ Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. - See [README for sys-clk-OC](https://github.com/KazushiMe/Switch-OC-Suite/blob/master/Source/sys-clk-OC/README.md) - DRAM Overclock (Safe: 1862.4 MHz) - - Unsafe: Up to 2131 MHz with DRAM bus overvolting depending on your DRAM chip - - Modded sys-clk and ReverseNX-RT - - CPU & GPU frequency governor (Experimental) - - Set charging current (100 mA - 2000 mA) and charging limit (20% - 100%) - - Global Profile - - Sync ReverseNX Mode - - Mariko variant (HAC-001-01, HDH-001, HEG-001) - CPU / GPU Overclock (Safe: 1963 / 998 MHz) - Unsafe @@ -39,50 +32,44 @@ Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. - DRAM Overclock (Safe: 1996.8 MHz) - - Modded sys-clk and ReverseNX-RT - - Auto CPU Boost - - CPU & GPU frequency governor (Experimental) - - Set charging current (100 mA - 2000 mA) and charging limit (20% - 100%) - - Global Profile - - Sync ReverseNX Mode +- Modded sys-clk and ReverseNX-RT + - Auto CPU Boost + - For faster game loading + - Enable CPU Boost (1785 MHz) when CPU Core#3 (System Core) is stressed (mainly I/O operations). + - Effective only when charger is connected. -- Auto CPU Boost - - For faster game loading - - Enable CPU Boost (1785 MHz) when CPU Core#3 (System Core) is stressed (mainly I/O operations). - - Effective only when charger is connected. + - CPU & GPU frequency governor (Experimental) + - Adjust frequency based on load. Might decrease power draw but can introduce stutters. Can be turned off for specific titles. -- CPU & GPU frequency governor (Experimental) - - Adjust frequency based on load. Might decrease power draw but can introduce stutters. Can be turned off for specific titles. + - Set charging current (100 mA - 2000 mA) and charging limit (20% - 100%) + - Long-term use of charge limit may render the battery gauge inaccurate. Performing full cycles could help recalibration, or try [battery_desync_fix_nx](https://github.com/CTCaer/battery_desync_fix_nx). -- Setting charge limit (20% - 100%) - - Long-term use of charge limit may render the battery gauge inaccurate. Performing full cycles could help recalibration, or try [battery_desync_fix_nx](https://github.com/CTCaer/battery_desync_fix_nx). + - Global Profile + - Designated a dummy title id `0xA111111111111111`. + - Priority: "Temp overrides" > "Application profile" > "Global profile" > "System default". -- Global profile - - Designated a dummy title id `0xA111111111111111`. - - Priority: "Temp overrides" > "Application profile" > "Global profile" > "System default". - -- Sync ReverseNX Mode - - No need to change clocks manually after toggling modes in ReverseNX (-RT and -Tool) + - Sync ReverseNX Mode + - No need to change clocks manually after toggling modes in ReverseNX (-RT and -Tool) - **[System Settings (Optional)](https://github.com/KazushiMe/Switch-OC-Suite/blob/master/system_settings.md)** ## Installation -1. Download latest [release](https://github.com/KazushiMe/Switch-OC-Suite/releases/latest). +1. Download latest [release](https://kazushime.github.io/Switch-OC-Suite/#download). 2. Copy all files in `SdOut` to the root of SD card. 3. Grab `x.x.x_loader.kip` for your Atmosphere version, rename it to `loader.kip` and place it in `/atmosphere/kips/`. -4. Customization via [online loader configurator](https://kazushime.github.io/Switch-OC-Suite/): +4. Customization via [online loader configurator](https://kazushime.github.io/Switch-OC-Suite/#config):
| Defaults | Mariko | Erista | | ---------- | ------------- | ------------ | | CPU OC | 2397 MHz Max | 2091 MHz Max | | CPU Boost | 1785 MHz | N/A | - | CPU Volt | 1235 mV Max | 1257 mV Max | + | CPU Volt | 1235 mV Max | 1235 mV Max | | GPU OC | 1305 MHz Max | N/A | | RAM OC | 1996 MHz Max | 1862 MHz Max | | RAM Volt | Disabled | Disabled | diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 57352099..f477e9b3 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -51,9 +51,9 @@ volatile CustomizeTable C = { */ .marikoGpuMaxClock = 1305600, -/* Mariko EMC: +/* Mariko EMC(RAM): * - RAM Clock in kHz: - * Values should be > 1600000, and divided evenly by 9600. + * Values should be ≥ 1600000, and divided evenly by 9600. * [WARNING] * RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM: * - Graphical glitches @@ -61,21 +61,21 @@ volatile CustomizeTable C = { * - NAND corruption */ .marikoEmcMaxClock = 1996800, -/* - RAM Voltage in uV - * Range: 600'000 to 650'000 uV +/* - EMC Vddq (Mariko Only) Voltage in uV + * Range: 550'000 to 650'000 uV * Value should be divided evenly by 5'000 * Default: 600'000 * Not enabled by default. * This will not work without sys-clk-OC. */ -.marikoEmcVolt = 0, +.marikoEmcVddqVolt = 0, /* Erista CPU: * - Max Voltage in mV */ .eristaCpuMaxVolt = 1235, -/* Erista EMC: +/* Erista EMC(RAM): * - RAM Clock in kHz * [WARNING] * RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM: @@ -84,13 +84,14 @@ volatile CustomizeTable C = { * - NAND corruption */ .eristaEmcMaxClock = 1862400, -/* - RAM Voltage in uV - * Range: 600'000 to 1250'000 uV - * Value should be divided evenly by 12'500 - * Default(HOS): 1125'000 +/* - EMC Vddq (Erista Only) and RAM Vdd2 Voltage in uV + * Range: 1100'000 to 1250'000 uV + * Erista Default(HOS): 1125'000 (bootloader: 1100'000) + * Mariko Default: 1100'000 (It will not work without sys-clk-OC.) + * Value should be divided evenly by 12'500. * Not enabled by default. */ -.eristaEmcVolt = 0, +.commonEmcMemVolt = 0, }; } \ No newline at end of file diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index f80f87cd..5b9bc90b 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -39,10 +39,10 @@ typedef struct __attribute__((packed)) CustomizeTable { u32 marikoCpuMaxVolt; u32 marikoGpuMaxClock; u32 marikoEmcMaxClock; - u32 marikoEmcVolt; + u32 marikoEmcVddqVolt; u32 eristaCpuMaxVolt; u32 eristaEmcMaxClock; - u32 eristaEmcVolt; + u32 commonEmcMemVolt; } CustomizeTable; extern volatile CustomizeTable C; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp index 3982885b..1c4d16d5 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp @@ -42,6 +42,7 @@ namespace ams::ldr { R_DEFINE_ERROR_RESULT(InvalidRegulatorEntry, 1011); R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1012); R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1013); + R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1014); } namespace ams::ldr::oc { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp index e0cac740..f67fea59 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp @@ -29,14 +29,85 @@ Result MemFreqPllmLimit(u32* ptr) { R_SUCCEED(); } +Result MemVoltHandler(u32* ptr) { + // ptr value might be default_uv or max_uv + regulator* entries[2] = { + reinterpret_cast(reinterpret_cast(ptr) - offsetof(regulator, type_1.default_uv)), + reinterpret_cast(reinterpret_cast(ptr) - offsetof(regulator, type_1.max_uv)), + }; + + constexpr u32 uv_step = 12'500; + constexpr u32 uv_min = 600'000; + + auto validator = [](regulator* entry) { + R_UNLESS(entry->id == 1, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type == 1, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_1.volt_reg == 0x17, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_1.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_1.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry()); + R_SUCCEED(); + }; + + regulator* entry = nullptr; + for (auto& i : entries) { + if (R_SUCCEEDED(validator(i))) + entry = i; + } + + R_UNLESS(entry, ldr::ResultInvalidRegulatorEntry()); + + u32 emc_uv = C.commonEmcMemVolt; + if (!emc_uv) + R_SKIP(); + + if (emc_uv % uv_step) + emc_uv = emc_uv / uv_step * uv_step; // rounding + + PatchOffset(ptr, emc_uv); + + R_SUCCEED(); +} + void SafetyCheck() { - if (C.custRev != CUST_REV || - C.marikoCpuMaxVolt > 1300 || - C.eristaCpuMaxVolt > 1300 || - (C.eristaEmcVolt && (C.eristaEmcVolt < 600'000 || C.eristaEmcVolt > 1250'000)) || - (C.marikoEmcVolt && (C.marikoEmcVolt < 600'000 || C.marikoEmcVolt > 650'000))) - { + if (C.custRev != CUST_REV) CRASH("Triggered"); + + struct sValidator { + volatile u32 value; + u32 min; + u32 max; + bool value_required = false; + + Result check() { + if (!value_required && !value) + R_SUCCEED(); + + if (min && value < min) + R_THROW(ldr::ResultSafetyCheckFailure()); + if (max && value > max) + R_THROW(ldr::ResultSafetyCheckFailure()); + + R_SUCCEED(); + } + }; + + sValidator validators[] = { + { C.marikoCpuMaxClock, 1785'000, 3000'000 }, + { C.marikoCpuBoostClock, 1020'000, 3000'000, true }, + { C.marikoCpuMaxVolt, 1100, 1300 }, + { C.marikoGpuMaxClock, 768'000, 1536'000 }, + { C.marikoEmcMaxClock, 1600'000, 2400'000 }, + { C.marikoEmcVddqVolt, 550'000, 650'000 }, + { C.eristaCpuMaxVolt, 1100, 1300 }, + { C.eristaEmcMaxClock, 1600'000, 2400'000 }, + { C.commonEmcMemVolt, 1100'000, 1250'000 }, + }; + + printf("marikoCpuMaxClock: %u\n", C.marikoCpuMaxClock); + + for (auto& i : validators) { + if (R_FAILED(i.check())) + CRASH("Triggered"); } } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index 01143df4..17082f58 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -117,11 +117,12 @@ static_assert(sizeof(regulator) == 0x120); constexpr u32 CpuClkOSLimit = 1785'000; -constexpr u32 MemClkOSLimit = 1600'000; +constexpr u32 EmcClkOSLimit = 1600'000; #define R_SKIP() R_SUCCEED() Result MemFreqPllmLimit(u32* ptr); +Result MemVoltHandler(u32* ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2 template Result MemMtcTableClone(Table* des, Table* src) { 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 acc68cf9..b6d78d27 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp @@ -72,7 +72,7 @@ Result MemFreqMtcTable(u32* ptr) { R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); } - if (C.eristaEmcMaxClock <= MemClkOSLimit) + if (C.eristaEmcMaxClock <= EmcClkOSLimit) R_SKIP(); // Make room for new mtc table, discarding useless 40.8 MHz table @@ -86,7 +86,7 @@ Result MemFreqMtcTable(u32* ptr) { } Result MemFreqMax(u32* ptr) { - if (C.eristaEmcMaxClock <= MemClkOSLimit) + if (C.eristaEmcMaxClock <= EmcClkOSLimit) R_SKIP(); PatchOffset(ptr, C.eristaEmcMaxClock); @@ -94,27 +94,13 @@ Result MemFreqMax(u32* ptr) { R_SUCCEED(); } -Result MemVoltHandler(u32* ptr) { - u32 emc_uv = C.eristaEmcVolt; - if (!emc_uv) - R_SKIP(); - - constexpr u32 uv_step = 12'500; - if (emc_uv % uv_step) - emc_uv = emc_uv / uv_step * uv_step; // rounding - - PatchOffset(ptr, emc_uv); - - R_SUCCEED(); -} - void Patch(uintptr_t mapped_nso, size_t nso_size) { PatcherEntry patches[] = { { "CPU Freq Table", &CpuFreqCvbTable, 1, nullptr, CpuClkOSLimit }, { "CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn }, - { "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, MemClkOSLimit }, - { "MEM Freq Max", &MemFreqMax, 0, nullptr, MemClkOSLimit }, - { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, MemClkPllmLimit }, + { "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 }, }; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp index 1bf329ae..f6ff4a30 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp @@ -56,7 +56,7 @@ inline bool CpuMaxVoltPatternFn(u32* ptr32) { } constexpr u32 MemVoltHOS = 1125'000; -constexpr u32 MemClkPllmLimit = 1866'000'000; +constexpr u32 EmcClkPllmLimit = 1866'000'000; constexpr u32 MTC_TABLE_REV = 7; 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 52fbc835..56000503 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -153,7 +153,7 @@ void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) { return; #define ADJUST_PROP(TARGET, REF) \ - (u32)(std::ceil(REF + ((C.marikoEmcMaxClock-MemClkOSAlt)*(TARGET-REF))/(MemClkOSLimit-MemClkOSAlt))) + (u32)(std::ceil(REF + ((C.marikoEmcMaxClock-EmcClkOSAlt)*(TARGET-REF))/(EmcClkOSLimit-EmcClkOSAlt))) #define ADJUST_PARAM(TARGET, REF) \ TARGET = ADJUST_PROP(TARGET, REF); @@ -319,7 +319,7 @@ Result MemFreqMtcTable(u32* ptr) { R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); } - if (C.marikoEmcMaxClock <= MemClkOSLimit) + if (C.marikoEmcMaxClock <= EmcClkOSLimit) R_SKIP(); MarikoMtcTable *table_alt = table_list[1], *table_max = table_list[0]; @@ -349,7 +349,7 @@ Result MemFreqDvbTable(u32* ptr) { bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0; R_UNLESS(validated, ldr::ResultInvalidDvbTable()); - if (C.marikoEmcMaxClock <= MemClkOSLimit) + if (C.marikoEmcMaxClock <= EmcClkOSLimit) R_SKIP(); if (C.marikoEmcMaxClock <= 1862400) { @@ -364,25 +364,30 @@ Result MemFreqDvbTable(u32* ptr) { } Result MemFreqMax(u32* ptr) { - if (C.marikoEmcMaxClock <= MemClkOSLimit) + if (C.marikoEmcMaxClock <= EmcClkOSLimit) R_SKIP(); PatchOffset(ptr, C.marikoEmcMaxClock); R_SUCCEED(); } -Result MemVoltHandler(u32* ptr) { +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; - if (entry->id != 2 || entry->type != 3 || - entry->type_2_3.step_uv != uv_step || - entry->type_2_3.min_uv != uv_min) - R_THROW(ldr::ResultInvalidRegulatorEntry()); + 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.marikoEmcVolt; + u32 emc_uv = C.marikoEmcVddqVolt; if (!emc_uv) R_SKIP(); @@ -402,11 +407,12 @@ void Patch(uintptr_t mapped_nso, size_t nso_size) { { "GPU Freq Table", &GpuFreqCvbTable, 1, nullptr, GpuClkOfficial }, { "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, { "GPU Freq PLL", &GpuFreqPllLimit, 1, nullptr, GpuClkPllLimit }, - { "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, MemClkOSLimit }, - { "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, MemClkOSLimit }, - { "MEM Freq Max", &MemFreqMax, 0, nullptr, MemClkOSLimit }, - { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, MemClkPllmLimit }, - { "MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltDefault } + { "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; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp index 46c23175..76528eeb 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp @@ -115,9 +115,10 @@ constexpr emc_dvb_dvfs_table_t EmcDvbTableDefault[] = { { 1600000, { 675, 650, 637, } }, }; -constexpr u32 MemClkOSAlt = 1331'200; -constexpr u32 MemClkPllmLimit = 2133'000'000; -constexpr u32 MemVoltDefault = 600'000; +constexpr u32 EmcClkOSAlt = 1331'200; +constexpr u32 EmcClkPllmLimit = 2133'000'000; +constexpr u32 EmcVddqDefault = 600'000; +constexpr u32 MemVdd2Default = 1100'000; constexpr u32 MTC_TABLE_REV = 3; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp index 7429474a..9cecb100 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/ptm/ptm.cpp @@ -65,6 +65,7 @@ bool PtmTablePatternFn(u32* ptr) { void Patch(uintptr_t mapped_nso, size_t nso_size) { #ifdef ATMOSPHERE_IS_STRATOSPHERE + // Ptm patcher is disabled for Erista bool isMariko = (spl::GetSocType() == spl::SocType_Mariko); if (!isMariko) return; diff --git a/Source/sys-clk-OC/README.md b/Source/sys-clk-OC/README.md index 67a75a0d..32f492aa 100644 --- a/Source/sys-clk-OC/README.md +++ b/Source/sys-clk-OC/README.md @@ -203,8 +203,8 @@ The `[values]` section allows you to alter timings in sys-clk, you should not ne | Key | Desc | Default | |:------------------------:|-------------------------------------------------------------------------------|:---------:| -|**allow_unsafe_freq** | (Mariko Only) Allow unsafe frequencies (CPU > 1963.5 MHz, GPU > 921.6 MHz) | OFF | -|**auto_cpu_boost** | (Mariko Only) Auto-boost CPU when system Core #3 utilization ≥ 95% | ON | +|**allow_unsafe_freq** | Allow unsafe frequencies (CPU > 1963.5 MHz, GPU > 921.6 MHz) | OFF | +|**auto_cpu_boost** | Auto-boost CPU when system Core #3 utilization ≥ 95% | OFF | |**sync_reversenx_mode** | Sync nominal profile (mode) with ReverseNX (-Tool and -RT) | ON | |**charging_current** | Charging current limit (100 mA - 2000 mA) | 2000 mA | |**charging_limit_perc** | Charging limit (20% - 100%) | 100%(OFF) | diff --git a/Source/sys-clk-OC/common/include/sysclk/config.h b/Source/sys-clk-OC/common/include/sysclk/config.h index 5868c084..310048d9 100644 --- a/Source/sys-clk-OC/common/include/sysclk/config.h +++ b/Source/sys-clk-OC/common/include/sysclk/config.h @@ -67,8 +67,8 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val) case SysClkConfigValue_CsvWriteIntervalMs: case SysClkConfigValue_AllowUnsafeFrequencies: case SysClkConfigValue_GovernorExperimental: - return 0ULL; case SysClkConfigValue_AutoCPUBoost: + return 0ULL; case SysClkConfigValue_SyncReverseNXMode: return 1ULL; case SysClkConfigValue_ChargingCurrentLimit: diff --git a/Source/sys-clk-OC/common/include/sysclk/i2c.h b/Source/sys-clk-OC/common/include/sysclk/i2c.h index d5c2ea66..1a85dd36 100644 --- a/Source/sys-clk-OC/common/include/sysclk/i2c.h +++ b/Source/sys-clk-OC/common/include/sysclk/i2c.h @@ -16,11 +16,11 @@ const u8 MAX17050_CURRENT_REG = 0x0A; // Buck Converter typedef enum I2c_BuckConverter_Reg { - I2c_Max77620_SD1VOLT_REG = 0x16, + I2c_Max77620_SD1VOLT_REG = 0x17, // Used for Erista DDR VDDQ+VDD2 / Mariko VDD2 I2c_Max77621_VOLT_REG = 0x00, I2c_Max77812_CPUVOLT_REG = 0x26, I2c_Max77812_GPUVOLT_REG = 0x23, - I2c_Max77812_MEMVOLT_REG = 0x25, // Master 3 (GPU 1 + 2, DRAM 3, CPU 4) + I2c_Max77812_MEMVOLT_REG = 0x25, // Master 3 (GPU 1 + 2, DRAM 3, CPU 4), used for Mariko VDDQ } I2c_BuckConverter_Reg; typedef struct I2c_BuckConverter_Domain { @@ -33,12 +33,13 @@ typedef struct I2c_BuckConverter_Domain { u8 por_val; } I2c_BuckConverter_Domain; -const I2c_BuckConverter_Domain I2c_Erista_CPU = { I2cDevice_Max77621Cpu, I2c_Max77621_VOLT_REG, 0x7F, 6250, 606250, 1400000, }; -const I2c_BuckConverter_Domain I2c_Erista_GPU = { I2cDevice_Max77621Gpu, I2c_Max77621_VOLT_REG, 0x7F, 6250, 606250, 1400000, }; -const I2c_BuckConverter_Domain I2c_Erista_DRAM = { I2cDevice_Max77620Pmic, I2c_Max77620_SD1VOLT_REG, 0x7F, 12500, 600000, 1250000, }; -const I2c_BuckConverter_Domain I2c_Mariko_CPU = { I2cDevice_Max77812_2, I2c_Max77812_CPUVOLT_REG, 0xFF, 5000, 250000, 1525000, 0x78 }; -const I2c_BuckConverter_Domain I2c_Mariko_GPU = { I2cDevice_Max77812_2, I2c_Max77812_GPUVOLT_REG, 0xFF, 5000, 250000, 1525000, 0x78 }; -const I2c_BuckConverter_Domain I2c_Mariko_DRAM = { I2cDevice_Max77812_2, I2c_Max77812_MEMVOLT_REG, 0xFF, 5000, 250000, 650000, 0x78 }; +const I2c_BuckConverter_Domain I2c_Erista_CPU = { I2cDevice_Max77621Cpu, I2c_Max77621_VOLT_REG, 0x7F, 6250, 606250, 1400000, }; +const I2c_BuckConverter_Domain I2c_Erista_GPU = { I2cDevice_Max77621Gpu, I2c_Max77621_VOLT_REG, 0x7F, 6250, 606250, 1400000, }; +const I2c_BuckConverter_Domain I2c_Erista_DRAM = { I2cDevice_Max77620Pmic, I2c_Max77620_SD1VOLT_REG, 0x7F, 12500, 600000, 1250000, }; +const I2c_BuckConverter_Domain I2c_Mariko_CPU = { I2cDevice_Max77812_2, I2c_Max77812_CPUVOLT_REG, 0xFF, 5000, 250000, 1525000, 0x78 }; +const I2c_BuckConverter_Domain I2c_Mariko_GPU = { I2cDevice_Max77812_2, I2c_Max77812_GPUVOLT_REG, 0xFF, 5000, 250000, 1525000, 0x78 }; +const I2c_BuckConverter_Domain I2c_Mariko_DRAM_VDDQ = { I2cDevice_Max77812_2, I2c_Max77812_MEMVOLT_REG, 0xFF, 5000, 250000, 650000, 0x78 }; +const I2c_BuckConverter_Domain I2c_Mariko_DRAM_VDD2 = { I2cDevice_Max77620Pmic, I2c_Max77620_SD1VOLT_REG, 0x7F, 12500, 600000, 1250000, }; u32 I2c_BuckConverter_GetMvOut(const I2c_BuckConverter_Domain* domain); Result I2c_BuckConverter_SetMvOut(const I2c_BuckConverter_Domain* domain, u32 mvolt); diff --git a/Source/sys-clk-OC/common/src/i2c.c b/Source/sys-clk-OC/common/src/i2c.c index cf69ef5d..54f1121c 100644 --- a/Source/sys-clk-OC/common/src/i2c.c +++ b/Source/sys-clk-OC/common/src/i2c.c @@ -99,17 +99,16 @@ u8 I2c_BuckConverter_MvOutToMultiplier(const I2c_BuckConverter_Domain* domain, u u32 I2c_BuckConverter_GetMvOut(const I2c_BuckConverter_Domain* domain) { u8 val; - Result res; - // Retry 3 times if received POR value - for (int i = 0; i < 3; i++) { - res = I2cRead_OutU8(domain->device, domain->reg, &val); - if (R_FAILED(res)) + // Retry 5 times if received POR value + for (int i = 0; i < 5; i++) { + if (R_FAILED(I2cRead_OutU8(domain->device, domain->reg, &val))) return 0u; + // Wait 1us + svcSleepThread(1E3); + if (!domain->por_val || val != domain->por_val) break; - - svcSleepThread(1000); } return I2c_BuckConverter_MultiplierToMvOut(domain, val & domain->volt_mask); } @@ -128,7 +127,8 @@ Result I2c_BuckConverter_SetMvOut(const I2c_BuckConverter_Domain* domain, u32 mv if (R_FAILED(res)) return res; - svcSleepThread(1000); + // 5ms Ramp delay + svcSleepThread(5E6); u8 new_val; res = I2cRead_OutU8(domain->device, domain->reg, &new_val); if (R_FAILED(res)) diff --git a/Source/sys-clk-OC/overlay/src/ui/gui/misc_gui.cpp b/Source/sys-clk-OC/overlay/src/ui/gui/misc_gui.cpp index 99a1512c..ebb4bd83 100644 --- a/Source/sys-clk-OC/overlay/src/ui/gui/misc_gui.cpp +++ b/Source/sys-clk-OC/overlay/src/ui/gui/misc_gui.cpp @@ -61,10 +61,7 @@ void MiscGui::listUI() sysclkIpcGetConfigValues(this->configList); this->listElement->addItem(new tsl::elm::CategoryHeader("Config")); - if (this->isMariko) { - addConfigToggle(SysClkConfigValue_AutoCPUBoost); - } - + addConfigToggle(SysClkConfigValue_AutoCPUBoost); addConfigToggle(SysClkConfigValue_SyncReverseNXMode); addConfigToggle(SysClkConfigValue_GovernorExperimental); @@ -85,7 +82,7 @@ void MiscGui::listUI() uint32_t current_ma = val * 100; this->configList->values[SysClkConfigValue_ChargingCurrentLimit] = current_ma; - snprintf(chargingCurrentBarDesc, sizeof(chargingCurrentBarDesc), "Battery Charging Current: %lu mA (Now: %+.2f mA)", this->configList->values[SysClkConfigValue_ChargingCurrentLimit], this->i2cInfo->batCurrent); + snprintf(chargingCurrentBarDesc, sizeof(chargingCurrentBarDesc), "Charging Current: %lu mA (Now: %+d mA)", this->configList->values[SysClkConfigValue_ChargingCurrentLimit], (int)this->i2cInfo->batCurrent); this->chargingCurrentHeader->setText(chargingCurrentBarDesc); Result rc = sysclkIpcSetConfigValues(this->configList); @@ -172,11 +169,11 @@ void MiscGui::refresh() { this->backlightToggle->setState(lblstatus); this->chargingCurrentBar->setProgress(this->configList->values[SysClkConfigValue_ChargingCurrentLimit] / 100); - snprintf(chargingCurrentBarDesc, sizeof(chargingCurrentBarDesc), "Battery Charging Current: %lu mA (Now: %+.2f mA)", this->configList->values[SysClkConfigValue_ChargingCurrentLimit], this->i2cInfo->batCurrent); + snprintf(chargingCurrentBarDesc, sizeof(chargingCurrentBarDesc), "Charging Current: %lu mA (Now: %+d mA)", this->configList->values[SysClkConfigValue_ChargingCurrentLimit], (int)this->i2cInfo->batCurrent); this->chargingCurrentHeader->setText(chargingCurrentBarDesc); this->chargingLimitBar->setProgress(this->configList->values[SysClkConfigValue_ChargingLimitPercentage]); - snprintf(chargingLimitBarDesc, sizeof(chargingLimitBarDesc), "Battery Charging Limit: %lu%% (Now: %u%%)", this->configList->values[SysClkConfigValue_ChargingLimitPercentage], this->batteryChargePerc); + snprintf(chargingLimitBarDesc, sizeof(chargingLimitBarDesc), "Charging Limit: %lu%% (Now: %u%%)", this->configList->values[SysClkConfigValue_ChargingLimitPercentage], this->batteryChargePerc); this->chargingLimitHeader->setText(chargingLimitBarDesc); I2cGetInfo(this->i2cInfo); diff --git a/Source/sys-clk-OC/overlay/src/ui/gui/misc_gui.h b/Source/sys-clk-OC/overlay/src/ui/gui/misc_gui.h index 8f106401..9137e238 100644 --- a/Source/sys-clk-OC/overlay/src/ui/gui/misc_gui.h +++ b/Source/sys-clk-OC/overlay/src/ui/gui/misc_gui.h @@ -34,7 +34,8 @@ class MiscGui : public BaseMenuGui float batCurrent; u32 cpuVolt; u32 gpuVolt; - u32 dramVolt; + u32 emcVddq; + u32 memVdd2; } I2cInfo; void PsmUpdate(uint32_t dispatchId = 0) @@ -60,9 +61,17 @@ class MiscGui : public BaseMenuGui float batCurrent = I2c_Max17050_GetBatteryCurrent(); i2cInfo->batCurrent = std::abs(batCurrent) < 10. ? 0. : batCurrent; - i2cInfo->cpuVolt = I2c_BuckConverter_GetMvOut(isMariko ? &I2c_Mariko_CPU : &I2c_Erista_CPU); - i2cInfo->gpuVolt = I2c_BuckConverter_GetMvOut(isMariko ? &I2c_Mariko_GPU : &I2c_Erista_GPU); - i2cInfo->dramVolt = I2c_BuckConverter_GetMvOut(isMariko ? &I2c_Mariko_DRAM : &I2c_Erista_DRAM); + if (isMariko) { + i2cInfo->cpuVolt = I2c_BuckConverter_GetMvOut(&I2c_Mariko_CPU); + i2cInfo->gpuVolt = I2c_BuckConverter_GetMvOut(&I2c_Mariko_GPU); + i2cInfo->emcVddq = I2c_BuckConverter_GetMvOut(&I2c_Mariko_DRAM_VDDQ); + i2cInfo->memVdd2 = I2c_BuckConverter_GetMvOut(&I2c_Mariko_DRAM_VDD2); + } else { + i2cInfo->cpuVolt = I2c_BuckConverter_GetMvOut(&I2c_Mariko_CPU); + i2cInfo->gpuVolt = I2c_BuckConverter_GetMvOut(&I2c_Mariko_GPU); + i2cInfo->emcVddq = I2c_BuckConverter_GetMvOut(&I2c_Erista_DRAM); + i2cInfo->memVdd2 = i2cInfo->emcVddq; + } I2c_Bq24193_GetFastChargeCurrentLimit(reinterpret_cast(&(chargeInfo->ChargeCurrentLimit))); @@ -98,7 +107,7 @@ class MiscGui : public BaseMenuGui "%s\n\n" "%dmV\n" "%dmV\n" - "%dmV\n" + "Vddq %dmV, Vdd2 %dmV\n" , PsmInfoChargerTypeToStr(chargeInfo->ChargerType), chargWattsInfo, (float)chargeInfo->VoltageAvg / 1000, (float)chargeInfo->BatteryTemperature / 1000, @@ -110,7 +119,7 @@ class MiscGui : public BaseMenuGui batCurInfo, i2cInfo->cpuVolt, i2cInfo->gpuVolt, - i2cInfo->dramVolt + i2cInfo->emcVddq, i2cInfo->memVdd2 ); } @@ -166,8 +175,8 @@ class MiscGui : public BaseMenuGui "GPU Volt:\n"\ "DRAM Volt:"; char infoVals[300] = ""; - char chargingLimitBarDesc[50] = ""; - char chargingCurrentBarDesc[60] = ""; + char chargingLimitBarDesc[40] = ""; + char chargingCurrentBarDesc[50] = ""; u32 batteryChargePerc = 0; u8 frameCounter = 60; }; diff --git a/Source/sys-clk-OC/sysmodule/src/clock_manager.cpp b/Source/sys-clk-OC/sysmodule/src/clock_manager.cpp index 46ef5ce4..87236aad 100644 --- a/Source/sys-clk-OC/sysmodule/src/clock_manager.cpp +++ b/Source/sys-clk-OC/sysmodule/src/clock_manager.cpp @@ -217,10 +217,10 @@ bool ClockManager::RefreshContext() this->rnxSync->ToggleSync(this->GetConfig()->GetConfigValue(SysClkConfigValue_SyncReverseNXMode)); bool allowUnsafe = this->GetConfig()->GetConfigValue(SysClkConfigValue_AllowUnsafeFrequencies); Clocks::SetAllowUnsafe(allowUnsafe); - if (Clocks::GetIsMariko()) { + + this->governor->SetAutoCPUBoost(this->GetConfig()->GetConfigValue(SysClkConfigValue_AutoCPUBoost)); + if (Clocks::GetIsMariko()) this->governor->SetCPUBoostHz(Clocks::GetNearestHz(SysClkModule_CPU, SysClkProfile_EnumMax, Clocks::boostCpuFreq)); - this->governor->SetAutoCPUBoost(this->GetConfig()->GetConfigValue(SysClkConfigValue_AutoCPUBoost)); - } } bool enabled = this->GetConfig()->Enabled(); diff --git a/Source/sys-clk-OC/sysmodule/src/config.cpp b/Source/sys-clk-OC/sysmodule/src/config.cpp index 4d287ba8..c5e9a0e6 100644 --- a/Source/sys-clk-OC/sysmodule/src/config.cpp +++ b/Source/sys-clk-OC/sysmodule/src/config.cpp @@ -69,9 +69,7 @@ void Config::Load() } // Erista: Disable Mariko only features - if (!Clocks::GetIsMariko()) { - this->configValues[SysClkConfigValue_AutoCPUBoost] = 0; - } + // if (!Clocks::GetIsMariko()) { } this->loaded = true; } diff --git a/Source/sys-clk-OC/sysmodule/src/file_utils.cpp b/Source/sys-clk-OC/sysmodule/src/file_utils.cpp index 7351cb11..d80d0aac 100644 --- a/Source/sys-clk-OC/sysmodule/src/file_utils.cpp +++ b/Source/sys-clk-OC/sysmodule/src/file_utils.cpp @@ -292,8 +292,13 @@ Result FileUtils::CustParser(const char* filepath, size_t filesize) { Clocks::maxMemFreq = table.marikoEmcMaxClock * 1000; if (table.marikoEmcVolt && table.marikoEmcVolt >= 600'000 && table.marikoEmcVolt <= 650'000) { u32 mvolt = table.marikoEmcVolt / 1000; - Result res = I2c_BuckConverter_SetMvOut(&I2c_Mariko_DRAM, mvolt); - LogLine("Set EMC volt to %u mV: %s", mvolt, R_FAILED(res) ? "Failed" : "OK"); + Result res = I2c_BuckConverter_SetMvOut(&I2c_Mariko_DRAM_VDDQ, mvolt); + LogLine("Set EMC Vddq volt to %u mV: %s", mvolt, R_FAILED(res) ? "Failed" : "OK"); + } + if (table.commonEmcMemVolt && table.commonEmcMemVolt >= 1100'000 && table.commonEmcMemVolt <= 1250'000) { + u32 mvolt = table.commonEmcMemVolt / 1000; + Result res = I2c_BuckConverter_SetMvOut(&I2c_Mariko_DRAM_VDD2, mvolt); + LogLine("Set MEM Vdd2 volt to %u mV: %s", mvolt, R_FAILED(res) ? "Failed" : "OK"); } } else { if (table.eristaEmcMaxClock) diff --git a/Source/sys-clk-OC/sysmodule/src/file_utils.h b/Source/sys-clk-OC/sysmodule/src/file_utils.h index 1e76832a..d8a66240 100644 --- a/Source/sys-clk-OC/sysmodule/src/file_utils.h +++ b/Source/sys-clk-OC/sysmodule/src/file_utils.h @@ -30,8 +30,6 @@ class FileUtils static void Exit(); static Result Initialize(); static bool IsInitialized(); - static bool IsLogEnabled(); - static bool ExistReverseNXTool(); static void InitializeAsync(); static void LogLine(const char *format, ...); static void WriteContextToCsv(const SysClkContext* context); @@ -51,7 +49,7 @@ class FileUtils uint32_t marikoEmcVolt; uint32_t eristaCpuMaxVolt; uint32_t eristaEmcMaxClock; - uint32_t eristaEmcVolt; + uint32_t commonEmcMemVolt; } CustTable; static void RefreshFlags(bool force); diff --git a/pages/dist/index.html b/pages/dist/index.html index aedc5605..e4156ef6 100644 --- a/pages/dist/index.html +++ b/pages/dist/index.html @@ -4,10 +4,16 @@ - Switch OC Suite + Switch OC Suite > Project Homepage | Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2. + + @@ -79,21 +85,7 @@ -
  • DRAM Overclock (Safe: 1862.4 MHz) -
      -
    • Unsafe: Up to 2131 MHz with DRAM bus overvolting depending on your - DRAM chip
    • -
    -
  • -
  • Modded sys-clk and ReverseNX-RT -
      -
    • CPU & GPU frequency governor (Experimental)
    • -
    • Set charging current (100 mA - 2000 mA) and charging limit (20% - - 100%)
    • -
    • Global Profile
    • -
    • Sync ReverseNX Mode
    • -
    -
  • +
  • DRAM Overclock (Safe: 1862.4 MHz)
  • @@ -114,55 +106,46 @@
  • DRAM Overclock (Safe: 1996.8 MHz)
  • -
  • Modded sys-clk and ReverseNX-RT + +
  • +
    + Modded sys-clk and ReverseNX-RT + -
    -
    - Auto CPU Boost - -
    -
    - CPU & GPU frequency governor (Experimental) - -
    -
    - Setting charge limit (20% - 100%) - -
    -
    - Global profile - -
    -
    - Sync ReverseNX Mode -
    @@ -201,7 +184,7 @@ CPU Volt 1235 mV Max - 1257 mV Max + 1235 mV Max GPU OC @@ -336,7 +319,7 @@ -
    +
    Generating link, please wait... Generating link, please wait... Generating link, please wait... diff --git a/pages/dist/main.js b/pages/dist/main.js index ee21df64..e8354d4a 100644 --- a/pages/dist/main.js +++ b/pages/dist/main.js @@ -8,10 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; /* Config: Cust */ -var CUST_REV; +const CUST_REV = 3; var buffer; class CustEntry { - constructor(name, size, desc, defval, minmax = [0, 1000000], step = 1, extra_validator) { + constructor(id, name, size, desc, defval, minmax = [0, 1000000], step = 1, zeroable = true) { + this.id = id; this.name = name; this.size = size; this.desc = desc; @@ -19,80 +20,106 @@ class CustEntry { this.min = minmax[0]; this.max = minmax[1]; this.step = step; - this.validator = extra_validator; + this.zeroable = zeroable; this.value = null; this.offset = null; } ; + validate() { + let tip = new ErrorToolTip(this.id); + tip.clear(); + if (Number.isNaN(this.value) || this.value === null) { + tip.setMsg(`Invalid value: Not a number`); + tip.show(); + return false; + } + if (this.zeroable && this.value == 0) + return true; + if (this.value < this.min || this.value > this.max) { + tip.setMsg(`Expected range: [${this.min}, ${this.max}], got ${this.value}.`); + tip.show(); + return false; + } + if (this.value % this.step != 0) { + tip.setMsg(`${this.value} % ${this.step} ≠ 0`); + tip.show(); + return false; + } + return true; + } + ; + update() { + this.value = Number(document.getElementById(this.id).value); + } + getInputElement() { + return document.getElementById(this.id); + } + createForm() { + let form = document.getElementById("form"); + let input = this.getInputElement(); + if (!input) { + let grid = document.createElement("div"); + grid.classList.add("grid"); + // Label and input + input = document.createElement("input"); + input.min = String(this.min); + input.max = String(this.max); + input.id = this.id; + input.type = "number"; + input.step = String(this.step); + let label = document.createElement("label"); + label.setAttribute("for", this.id); + label.innerHTML = this.name; + label.appendChild(input); + grid.appendChild(label); + // Description in blockquote style + let desc = document.createElement("blockquote"); + desc.style["margin-top"] = "0"; + desc.innerHTML = this.desc; + desc.setAttribute("for", this.id); + grid.appendChild(desc); + grid.style["margin-top"] = "3rem"; + form.appendChild(grid); + let tooltip = new ErrorToolTip(this.id); + tooltip.addChangeListener(); + } + input.value = String(this.value); + } + clearForm() { + let input = this.getInputElement(); + if (input) { + input.parentElement.parentElement.remove(); + } + } } -var CustTable = null; -const CustTableV2 = [ - new CustEntry("mtcConf", 2, "DRAM Timing\ -
  • 0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default)
  • \ -
  • 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.
  • ", 0, [0, 3]), - new CustEntry("marikoCpuMaxClock", 4, "Mariko CPU Max Clock in kHz\ -
  • System default: 1785000
  • \ -
  • 2397000 might be unreachable for some SoCs.
  • ", 2397000, [1785000, 3000000], 100, (x) => (x % 100) == 0), - new CustEntry("marikoCpuBoostClock", 4, "Mariko CPU Boost Clock in kHz\ -
  • System default: 1785000
  • \ -
  • Boost clock will be applied when applications request higher CPU frequency for quicker loading.
  • \ -
  • This will be set regardless of whether sys-clk is enabled.
  • ", 1785000, [1785000, 3000000], 100, (x) => (x % 100) == 0), - new CustEntry("marikoCpuMaxVolt", 4, "Mariko CPU Max Voltage in mV\ -
  • System default: 1120
  • \ -
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", 1235, [1100, 1300]), - new CustEntry("marikoGpuMaxClock", 4, "Mariko GPU Max Clock in kHz\ -
  • System default: 921600
  • \ -
  • Tegra X1+ official maximum: 1267200
  • \ -
  • 1305600 might be unreachable for some SoCs.
  • ", 1305600, [768000, 1536000], 100, (x) => (x % 100) == 0), - new CustEntry("marikoEmcMaxClock", 4, "Mariko RAM Max Clock in kHz\ -
  • Values should be > 1600000, and divided evenly by 3200.
  • \ -
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", 1996800, [1612800, 2400000], 3200, (x) => (x % 3200) == 0), - new CustEntry("eristaOCEnable", 4, "Erista CPU Enable Overclock\ -
  • Not tested
  • ", 1, [0, 1]), - new CustEntry("eristaCpuMaxVolt", 4, "Erista CPU Max Voltage in mV\ -
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", 1235, [0, 1300], 1, (x) => x >= 1100), - new CustEntry("eristaEmcMaxClock", 4, "Erista RAM Max Clock in kHz\ -
  • Values should be > 1600000, and divided evenly by 3200.
  • \ -
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", 1862400, [1600000, 2400000], 3200, (x) => (x % 3200) == 0), - new CustEntry("eristaEmcVolt", 4, "Erista RAM Voltage in uV\ -
  • Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.
  • \ -
  • Not enabled by default
  • ", 0, [0, 1250000], 12500, (x) => (x % 12500) == 0 && x >= 1100000), -]; -const CustTableV3 = [ - new CustEntry("mtcConf", 2, "DRAM Timing\ -
  • 0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default)
  • \ -
  • 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.
  • \ -
  • 2: NO_ADJ_ALL: No timing adjustment for both Erista and Mariko. Might achieve better performance on Mariko but lower maximum frequency is expected.", 0, [0, 3]), - new CustEntry("marikoCpuMaxClock", 4, "Mariko CPU Max Clock in kHz\ -
  • System default: 1785000
  • \ -
  • 2397000 might be unreachable for some SoCs.
  • ", 2397000, [1785000, 3000000], 100, (x) => (x % 100) == 0), - new CustEntry("marikoCpuBoostClock", 4, "Mariko CPU Boost Clock in kHz\ -
  • System default: 1785000
  • \ -
  • Boost clock will be applied when applications request higher CPU frequency for quicker loading.
  • \ -
  • This will be set regardless of whether sys-clk is enabled.
  • ", 1785000, [1785000, 3000000], 100, (x) => (x % 100) == 0), - new CustEntry("marikoCpuMaxVolt", 4, "Mariko CPU Max Voltage in mV\ -
  • System default: 1120
  • \ -
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", 1235, [1100, 1300]), - new CustEntry("marikoGpuMaxClock", 4, "Mariko GPU Max Clock in kHz\ -
  • System default: 921600
  • \ -
  • Tegra X1+ official maximum: 1267200
  • \ -
  • 1305600 might be unreachable for some SoCs.
  • ", 1305600, [768000, 1536000], 100, (x) => (x % 100) == 0), - new CustEntry("marikoEmcMaxClock", 4, "Mariko RAM Max Clock in kHz\ -
  • Values should be > 1600000, and divided evenly by 3200.
  • \ -
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", 1996800, [1612800, 2400000], 3200, (x) => (x % 3200) == 0), - new CustEntry("marikoEmcVolt", 4, "Mariko RAM Voltage in uV\ -
  • Acceptable range: 600000 ≤ x ≤ 650000
  • \ -
  • Value should be divided evenly by 5'000
  • \ -
  • Not enabled by default.
  • \ -
  • This will not work without sys-clk-OC.
  • ", 1, [0, 1]), - new CustEntry("eristaCpuMaxVolt", 4, "Erista CPU Max Voltage in mV\ -
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", 1235, [0, 1300], 1, (x) => x >= 1100), - new CustEntry("eristaEmcMaxClock", 4, "Erista RAM Max Clock in kHz\ -
  • Values should be > 1600000, and divided evenly by 3200.
  • \ -
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", 1862400, [1600000, 2400000], 3200, (x) => (x % 3200) == 0), - new CustEntry("eristaEmcVolt", 4, "Erista RAM Voltage in uV\ -
  • Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.
  • \ -
  • Not enabled by default
  • ", 0, [0, 1250000], 12500, (x) => (x % 12500) == 0 && x >= 1100000), +var CustTable = [ + new CustEntry("mtcConf", "DRAM Timing", 2, "
  • 0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default)
  • \ +
  • 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.
  • \ +
  • 2: NO_ADJ_ALL: No timing adjustment for both Erista and Mariko. Might achieve better performance on Mariko but lower maximum frequency is expected.", 0, [0, 2], 1), + new CustEntry("marikoCpuMaxClock", "Mariko CPU Max Clock in kHz", 4, "
  • System default: 1785000
  • \ +
  • 2397000 might be unreachable for some SoCs.
  • ", 2397000, [1785000, 3000000], 1), + new CustEntry("marikoCpuBoostClock", "Mariko CPU Boost Clock in kHz", 4, "
  • System default: 1785000
  • \ +
  • Boost clock will be applied when applications request higher CPU frequency for quicker loading.
  • \ +
  • This will be set regardless of whether sys-clk is enabled.
  • ", 1785000, [1020000, 3000000], 1, false), + new CustEntry("marikoCpuMaxVolt", "Mariko CPU Max Voltage in mV", 4, "
  • System default: 1120
  • \ +
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", 1235, [1100, 1300], 5), + new CustEntry("marikoGpuMaxClock", "Mariko GPU Max Clock in kHz", 4, "
  • System default: 921600
  • \ +
  • Tegra X1+ official maximum: 1267200
  • \ +
  • 1305600 might be unreachable for some SoCs.
  • ", 1305600, [768000, 1536000], 100), + new CustEntry("marikoEmcMaxClock", "Mariko RAM Max Clock in kHz", 4, "
  • Values should be ≥ 1600000, and divided evenly by 3200.
  • \ +
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", 1996800, [1600000, 2400000], 3200), + new CustEntry("marikoEmcVddqVolt", "EMC Vddq (Mariko Only) Voltage in uV", 4, "
  • Acceptable range: 550000 ≤ x ≤ 650000
  • \ +
  • Value should be divided evenly by 5000
  • \ +
  • Default: 600000
  • \ +
  • Not enabled by default.
  • \ +
  • This will not work without sys-clk-OC.
  • ", 0, [550000, 650000], 5000), + new CustEntry("eristaCpuMaxVolt", "Erista CPU Max Voltage in mV", 4, "
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", 1235, [1100, 1300], 1), + new CustEntry("eristaEmcMaxClock", "Erista RAM Max Clock in kHz", 4, "
  • Values should be ≥ 1600000, and divided evenly by 3200.
  • \ +
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", 1862400, [1600000, 2400000], 3200), + new CustEntry("commonEmcMemVolt", "EMC Vddq (Erista Only) & RAM Vdd2 Voltage in uV", 4, "
  • Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.
  • \ +
  • Erista Default (HOS): 1125000 (bootloader: 1100000)
  • \ +
  • Mariko Default: 1100000 (It will not work without sys-clk-OC)
  • \ +
  • Not enabled by default
  • ", 0, [1100000, 1250000], 12500), ]; function FindMagicOffset(buffer) { let view = new DataView(buffer); @@ -104,9 +131,12 @@ function FindMagicOffset(buffer) { throw new Error("Invalid loader.kip file"); } class ErrorToolTip { - constructor(id) { + constructor(id, msg) { this.id = id; this.element = document.getElementById(id); + if (msg) { + this.setMsg(msg); + } } ; setMsg(msg) { @@ -134,52 +164,28 @@ class ErrorToolTip { addChangeListener() { if (this.element) { this.element.addEventListener('change', (_evt) => { - let obj = CustTable.filter((obj) => { return obj.name === this.id; })[0]; + let obj = CustTable.filter((obj) => { return obj.id === this.id; })[0]; obj.value = Number(this.element.value); - ValidateCustEntry(obj); + obj.validate(); }); } } } ; -function ValidateCustEntry(entry) { - let elementId = entry.name; - let tip = new ErrorToolTip(elementId); - tip.clear(); - if (entry.value == 0) - return null; - if (entry.value < entry.min || entry.value > entry.max) { - tip.setMsg(`Expected range: [${entry.min}, ${entry.max}], got ${entry.value}.`); - tip.show(); - return tip; - } - if (entry.validator && !entry.validator(entry.value)) { - tip.setMsg(`Invalid value: ${entry.value}. Did not pass this validator: ${entry.validator}.`); - tip.show(); - return tip; - } - return null; -} -function ValidateCust() { - let tooltips = []; - for (let i of CustTable) { - let tip = ValidateCustEntry(i); - if (tip) { - tooltips.push(tip); - } - } - if (tooltips.length > 0) { - throw new Error("Invalid cust"); - } -} function SaveCust(buffer) { let view = new DataView(buffer); let storage = {}; - storage["custRev"] = CUST_REV; for (let i of CustTable) { - let id = i.name; - i.value = Number(document.getElementById(id).value); - storage[i.name] = i.value; + i.update(); + if (!i.validate() || i.value === null) { + document.getElementById(i.id).focus(); + throw new Error(`Invalid ${i.name}`); + } + if (!i.offset) { + document.getElementById(i.id).focus(); + throw new Error(`Failed to get offset for ${i.name}`); + ; + } switch (i.size) { case 2: view.setUint16(i.offset, i.value, true); @@ -188,11 +194,12 @@ function SaveCust(buffer) { view.setUint32(i.offset, i.value, true); break; default: - document.getElementById(i.name).focus(); - throw new Error("Unknown size at " + i); + document.getElementById(i.id).focus(); + throw new Error(`Unknown size at ${i.name}`); } + storage[i.id] = i.value; } - ValidateCust(); + storage["custRev"] = CUST_REV; localStorage.setItem("last_saved", JSON.stringify(storage)); let a = document.createElement("a"); a.href = window.URL.createObjectURL(new Blob([buffer], { type: "application/octet-stream" })); @@ -225,68 +232,34 @@ function LoadLastSaved() { } function LoadDefault() { for (let i of CustTable) { - let id = i.name; - document.getElementById(id).value = String(i.defval); + document.getElementById(i.id).value = String(i.defval); } } function ClearHTMLForm() { - var _a, _b; - if (!CustTable) { - return; - } for (let i of CustTable) { - let id = i.name; - let input = document.getElementById(id); - (_b = (_a = input.parentElement) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.remove(); + i.clearForm(); } } function UpdateHTMLForm() { - let dict = Object.assign({}, ...CustTable.map((x) => ({ [x.name]: x }))); - let form = document.getElementById("form"); for (let i of CustTable) { - let id = i.name; - let input = document.getElementById(id); - if (!input) { - let grid = document.createElement("div"); - grid.classList.add("grid"); - // Label and input box - input = document.createElement("input"); - input.min = dict[i.name].min; - input.max = dict[i.name].max; - input.id = id; - input.type = "number"; - input.step = dict[i.name].step; - let label = document.createElement("label"); - label.setAttribute("for", id); - label.innerHTML = id; - label.appendChild(input); - grid.appendChild(label); - // Description in blockquote style - let desc = dict[i.name].desc; - let block = document.createElement("blockquote"); - block.style["margin-top"] = "0"; - block.innerHTML = desc; - block.setAttribute("for", id); - grid.appendChild(block); - grid.style["margin-top"] = "3rem"; - form.appendChild(grid); - let tooltip = new ErrorToolTip(id); - tooltip.addChangeListener(); - } - input.value = dict[i.name].value; + i.createForm(); } let default_btn = document.getElementById("load_default"); default_btn.removeAttribute("disabled"); default_btn.addEventListener('click', () => { LoadDefault(); }); + let last_btn = document.getElementById("load_saved"); if (LastSaved()) { - let last_btn = document.getElementById("load_saved"); + last_btn.style.removeProperty("display"); last_btn.removeAttribute("disabled"); last_btn.addEventListener('click', () => { LoadLastSaved(); }); } + else { + last_btn.style.setProperty("display", "none"); + } let save_btn = document.getElementById("save"); save_btn.removeAttribute("disabled"); save_btn.addEventListener('click', () => { @@ -303,17 +276,10 @@ function ParseCust(magicOffset, buffer) { let view = new DataView(buffer); let offset = magicOffset + 4; let rev = view.getUint16(offset, true); - if (rev != 2 && rev != 3) { - throw new Error("Unsupported custRev, expected: 2 or 3, got " + rev); + if (rev != CUST_REV) { + throw new Error(`Unsupported custRev, expected: ${CUST_REV}, got ${rev}`); } - CUST_REV = rev; document.getElementById("cust_rev").innerHTML = `Cust V${CUST_REV} is loaded.`; - if (rev == 2) { - CustTable = CustTableV2; - } - else { - CustTable = CustTableV3; - } offset += 2; for (let i of CustTable) { i.offset = offset; @@ -325,10 +291,11 @@ function ParseCust(magicOffset, buffer) { i.value = view.getUint32(offset, true); break; default: - document.getElementById(i.name).focus(); + document.getElementById(i.id).focus(); throw new Error("Unknown size at " + i); } offset += i.size; + i.validate(); } } const fileInput = document.getElementById("file"); @@ -342,7 +309,6 @@ fileInput.addEventListener('change', (event) => { let offset = FindMagicOffset(buffer); ClearHTMLForm(); ParseCust(offset, buffer); - ValidateCust(); UpdateHTMLForm(); } catch (e) { @@ -378,7 +344,9 @@ function fetchRelease() { let info = { OCSuiteVer: latestVerFromSuite, LoaderKipUrl: loaderKip.browser_download_url, + LoaderKipTime: loaderKip.updated_at, SdOutZipUrl: sdOut.browser_download_url, + SdOutZipTime: sdOut.updated_at, AMSVer: correspondingVerFromAMS, AMSUrl: amsReleaseUrl }; @@ -400,11 +368,11 @@ function updateDownloadUrls() { }; let info = yield fetchRelease(); if (info) { - const loaderKipName = `loader.kip ${info.OCSuiteVer}`; + const loaderKipName = `loader.kip ${info.OCSuiteVer}
    ${info.LoaderKipTime}`; updateHref("loader_kip_btn", loaderKipName, info.LoaderKipUrl); - const sdOutName = `SdOut.zip ${info.OCSuiteVer}`; + const sdOutName = `SdOut.zip ${info.OCSuiteVer}
    ${info.SdOutZipTime}`; updateHref("sdout_zip_btn", sdOutName, info.SdOutZipUrl); - const amsName = `Atmosphere-NX ${info.AMSVer}`; + const amsName = `Atmosphere-NX ${info.AMSVer}`; updateHref("ams_btn", amsName, info.AMSUrl); } }); diff --git a/pages/src/index.html b/pages/src/index.html index aedc5605..e4156ef6 100644 --- a/pages/src/index.html +++ b/pages/src/index.html @@ -4,10 +4,16 @@ - Switch OC Suite + Switch OC Suite > Project Homepage | Overclocking suite for Horizon OS (HOS) running on Atmosphere CFW. Licensed under GPL v2. + + @@ -79,21 +85,7 @@ -
  • DRAM Overclock (Safe: 1862.4 MHz) -
      -
    • Unsafe: Up to 2131 MHz with DRAM bus overvolting depending on your - DRAM chip
    • -
    -
  • -
  • Modded sys-clk and ReverseNX-RT -
      -
    • CPU & GPU frequency governor (Experimental)
    • -
    • Set charging current (100 mA - 2000 mA) and charging limit (20% - - 100%)
    • -
    • Global Profile
    • -
    • Sync ReverseNX Mode
    • -
    -
  • +
  • DRAM Overclock (Safe: 1862.4 MHz)
  • @@ -114,55 +106,46 @@
  • DRAM Overclock (Safe: 1996.8 MHz)
  • -
  • Modded sys-clk and ReverseNX-RT + +
  • +
    + Modded sys-clk and ReverseNX-RT + -
    -
    - Auto CPU Boost - -
    -
    - CPU & GPU frequency governor (Experimental) - -
    -
    - Setting charge limit (20% - 100%) - -
    -
    - Global profile - -
    -
    - Sync ReverseNX Mode -
    @@ -201,7 +184,7 @@ CPU Volt 1235 mV Max - 1257 mV Max + 1235 mV Max GPU OC @@ -336,7 +319,7 @@ -
    +
    Generating link, please wait... Generating link, please wait... Generating link, please wait... diff --git a/pages/src/main.ts b/pages/src/main.ts index 4af12410..7f929000 100644 --- a/pages/src/main.ts +++ b/pages/src/main.ts @@ -1,20 +1,22 @@ /* Config: Cust */ -var CUST_REV: number | null; +const CUST_REV = 3; var buffer: string | ArrayBuffer; class CustEntry { + id: string; name: string; size: number; desc: string; defval: number; min: number; max: number; - step: number; - validator: Function | null; + step: number; // also as quotient + zeroable: boolean; value: number | null; offset: number | null; - constructor(name: string, size: number, desc: string, defval: number, minmax = [0, 1_000_000], step = 1, extra_validator?) { + constructor(id: string, name: string, size: number, desc: string, defval: number, minmax: [number, number] = [0, 1_000_000], step: number = 1, zeroable: boolean = true) { + this.id = id; this.name = name; this.size = size; this.desc = desc; @@ -22,229 +24,194 @@ class CustEntry { this.min = minmax[0]; this.max = minmax[1]; this.step = step; - this.validator = extra_validator; + this.zeroable = zeroable; this.value = null; this.offset = null; }; + + validate(): boolean { + let tip = new ErrorToolTip(this.id); + tip.clear(); + if (Number.isNaN(this.value) || this.value === null) { + tip.setMsg(`Invalid value: Not a number`); + tip.show(); + return false; + } + if (this.zeroable && this.value == 0) + return true; + if (this.value < this.min || this.value > this.max) { + tip.setMsg(`Expected range: [${this.min}, ${this.max}], got ${this.value}.`); + tip.show(); + return false; + } + if (this.value % this.step != 0) { + tip.setMsg(`${this.value} % ${this.step} ≠ 0`); + tip.show(); + return false; + } + return true; + }; + + update() { + this.value = Number((document.getElementById(this.id) as HTMLInputElement).value); + } + + getInputElement(): HTMLInputElement | null { + return document.getElementById(this.id) as HTMLInputElement; + } + + createForm() { + let form = document.getElementById("form")!; + let input = this.getInputElement(); + if (!input) { + let grid = document.createElement("div"); + grid.classList.add("grid"); + + // Label and input + input = document.createElement("input"); + input.min = String(this.min); + input.max = String(this.max); + input.id = this.id; + input.type = "number"; + input.step = String(this.step); + let label = document.createElement("label"); + label.setAttribute("for", this.id); + label.innerHTML = this.name; + label.appendChild(input); + grid.appendChild(label); + + // Description in blockquote style + let desc = document.createElement("blockquote"); + desc.style["margin-top"] = "0"; + desc.innerHTML = this.desc; + desc.setAttribute("for", this.id); + grid.appendChild(desc); + + grid.style["margin-top"] = "3rem"; + form.appendChild(grid); + + let tooltip = new ErrorToolTip(this.id); + tooltip.addChangeListener(); + } + input.value = String(this.value!); + } + + clearForm() { + let input = this.getInputElement(); + if (input) { + input.parentElement!.parentElement!.remove(); + } + } } -var CustTable: Array | null = null; - -const CustTableV2: Array = [ +var CustTable: Array = [ new CustEntry( "mtcConf", + "DRAM Timing", 2, - "DRAM Timing\ -
  • 0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default)
  • \ -
  • 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.
  • ", + "
  • 0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default)
  • \ +
  • 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.
  • \ +
  • 2: NO_ADJ_ALL: No timing adjustment for both Erista and Mariko. Might achieve better performance on Mariko but lower maximum frequency is expected.", 0, - [0, 3], + [0, 2], + 1 ), new CustEntry( "marikoCpuMaxClock", + "Mariko CPU Max Clock in kHz", 4, - "Mariko CPU Max Clock in kHz\ -
  • System default: 1785000
  • \ -
  • 2397000 might be unreachable for some SoCs.
  • ", + "
  • System default: 1785000
  • \ +
  • 2397000 might be unreachable for some SoCs.
  • ", 2397_000, [1785_000, 3000_000], - 100, - (x: number) => (x % 100) == 0 + 1, ), new CustEntry( "marikoCpuBoostClock", + "Mariko CPU Boost Clock in kHz", 4, - "Mariko CPU Boost Clock in kHz\ -
  • System default: 1785000
  • \ -
  • Boost clock will be applied when applications request higher CPU frequency for quicker loading.
  • \ -
  • This will be set regardless of whether sys-clk is enabled.
  • ", + "
  • System default: 1785000
  • \ +
  • Boost clock will be applied when applications request higher CPU frequency for quicker loading.
  • \ +
  • This will be set regardless of whether sys-clk is enabled.
  • ", 1785_000, - [1785_000, 3000_000], - 100, - (x: number) => (x % 100) == 0 + [1020_000, 3000_000], + 1, + false ), new CustEntry( "marikoCpuMaxVolt", + "Mariko CPU Max Voltage in mV", 4, - "Mariko CPU Max Voltage in mV\ -
  • System default: 1120
  • \ -
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", + "
  • System default: 1120
  • \ +
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", 1235, [1100, 1300], + 5 ), new CustEntry( "marikoGpuMaxClock", + "Mariko GPU Max Clock in kHz", 4, - "Mariko GPU Max Clock in kHz\ -
  • System default: 921600
  • \ -
  • Tegra X1+ official maximum: 1267200
  • \ -
  • 1305600 might be unreachable for some SoCs.
  • ", + "
  • System default: 921600
  • \ +
  • Tegra X1+ official maximum: 1267200
  • \ +
  • 1305600 might be unreachable for some SoCs.
  • ", 1305_600, [768_000, 1536_000], 100, - (x: number) => (x % 100) == 0 ), new CustEntry( "marikoEmcMaxClock", + "Mariko RAM Max Clock in kHz", 4, - "Mariko RAM Max Clock in kHz\ -
  • Values should be > 1600000, and divided evenly by 3200.
  • \ -
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", + "
  • Values should be ≥ 1600000, and divided evenly by 3200.
  • \ +
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", 1996_800, - [1612_800, 2400_000], + [1600_000, 2400_000], 3200, - (x: number) => (x % 3200) == 0 ), new CustEntry( - "eristaOCEnable", + "marikoEmcVddqVolt", + "EMC Vddq (Mariko Only) Voltage in uV", 4, - "Erista CPU Enable Overclock\ -
  • Not tested
  • ", - 1, - [0, 1] + "
  • Acceptable range: 550000 ≤ x ≤ 650000
  • \ +
  • Value should be divided evenly by 5000
  • \ +
  • Default: 600000
  • \ +
  • Not enabled by default.
  • \ +
  • This will not work without sys-clk-OC.
  • ", + 0, + [550_000, 650_000], + 5000, ), new CustEntry( "eristaCpuMaxVolt", + "Erista CPU Max Voltage in mV", 4, - "Erista CPU Max Voltage in mV\ -
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", - 1235, - [0, 1300], - 1, - (x: number) => x >= 1100 - ), - new CustEntry( - "eristaEmcMaxClock", - 4, - "Erista RAM Max Clock in kHz\ -
  • Values should be > 1600000, and divided evenly by 3200.
  • \ -
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", - 1862_400, - [1600_000, 2400_000], - 3200, - (x: number) => (x % 3200) == 0 - ), - new CustEntry( - "eristaEmcVolt", - 4, - "Erista RAM Voltage in uV\ -
  • Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.
  • \ -
  • Not enabled by default
  • ", - 0, - [0, 1250_000], - 12500, - (x: number) => (x % 12500) == 0 && x >= 1100000 - ), -]; - -const CustTableV3: Array = [ - new CustEntry( - "mtcConf", - 2, - "DRAM Timing\ -
  • 0: AUTO_ADJ_MARIKO_SAFE: Auto adjust timings for LPDDR4 ≤3733 Mbps specs, 8Gb density. (Default)
  • \ -
  • 1: AUTO_ADJ_MARIKO_4266: Auto adjust timings for LPDDR4X 4266 Mbps specs, 8Gb density.
  • \ -
  • 2: NO_ADJ_ALL: No timing adjustment for both Erista and Mariko. Might achieve better performance on Mariko but lower maximum frequency is expected.", - 0, - [0, 3], - ), - new CustEntry( - "marikoCpuMaxClock", - 4, - "Mariko CPU Max Clock in kHz\ -
  • System default: 1785000
  • \ -
  • 2397000 might be unreachable for some SoCs.
  • ", - 2397_000, - [1785_000, 3000_000], - 100, - (x: number) => (x % 100) == 0 - ), - new CustEntry( - "marikoCpuBoostClock", - 4, - "Mariko CPU Boost Clock in kHz\ -
  • System default: 1785000
  • \ -
  • Boost clock will be applied when applications request higher CPU frequency for quicker loading.
  • \ -
  • This will be set regardless of whether sys-clk is enabled.
  • ", - 1785_000, - [1785_000, 3000_000], - 100, - (x: number) => (x % 100) == 0 - ), - new CustEntry( - "marikoCpuMaxVolt", - 4, - "Mariko CPU Max Voltage in mV\ -
  • System default: 1120
  • \ -
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", + "
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", 1235, [1100, 1300], - ), - new CustEntry( - "marikoGpuMaxClock", - 4, - "Mariko GPU Max Clock in kHz\ -
  • System default: 921600
  • \ -
  • Tegra X1+ official maximum: 1267200
  • \ -
  • 1305600 might be unreachable for some SoCs.
  • ", - 1305_600, - [768_000, 1536_000], - 100, - (x: number) => (x % 100) == 0 - ), - new CustEntry( - "marikoEmcMaxClock", - 4, - "Mariko RAM Max Clock in kHz\ -
  • Values should be > 1600000, and divided evenly by 3200.
  • \ -
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", - 1996_800, - [1612_800, 2400_000], - 3200, - (x: number) => (x % 3200) == 0 - ), - new CustEntry( - "marikoEmcVolt", - 4, - "Mariko RAM Voltage in uV\ -
  • Acceptable range: 600000 ≤ x ≤ 650000
  • \ -
  • Value should be divided evenly by 5'000
  • \ -
  • Not enabled by default.
  • \ -
  • This will not work without sys-clk-OC.
  • ", 1, - [0, 1] - ), - new CustEntry( - "eristaCpuMaxVolt", - 4, - "Erista CPU Max Voltage in mV\ -
  • Acceptable range: 1100 ≤ x ≤ 1300
  • ", - 1235, - [0, 1300], - 1, - (x: number) => x >= 1100 ), new CustEntry( "eristaEmcMaxClock", + "Erista RAM Max Clock in kHz", 4, - "Erista RAM Max Clock in kHz\ -
  • Values should be > 1600000, and divided evenly by 3200.
  • \ -
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", + "
  • Values should be ≥ 1600000, and divided evenly by 3200.
  • \ +
  • WARNING: RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM
  • ", 1862_400, [1600_000, 2400_000], 3200, - (x: number) => (x % 3200) == 0 ), new CustEntry( - "eristaEmcVolt", + "commonEmcMemVolt", + "EMC Vddq (Erista Only) & RAM Vdd2 Voltage in uV", 4, - "Erista RAM Voltage in uV\ -
  • Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.
  • \ -
  • Not enabled by default
  • ", + "
  • Acceptable range: 1100000 ≤ x ≤ 1250000, and it should be divided evenly by 12500.
  • \ +
  • Erista Default (HOS): 1125000 (bootloader: 1100000)
  • \ +
  • Mariko Default: 1100000 (It will not work without sys-clk-OC)
  • \ +
  • Not enabled by default
  • ", 0, - [0, 1250_000], + [1100_000, 1250_000], 12500, - (x: number) => (x % 12500) == 0 && x >= 1100000 ), ]; @@ -263,9 +230,10 @@ class ErrorToolTip { message: string | null; element: HTMLElement | null; - constructor(id: string) { + constructor(id: string, msg?: string) { this.id = id; this.element = document.getElementById(id); + if (msg) { this.setMsg(msg); } }; setMsg(msg: string) { @@ -295,71 +263,43 @@ class ErrorToolTip { addChangeListener() { if (this.element) { this.element.addEventListener('change', (_evt) => { - let obj = CustTable!.filter((obj) => { return obj.name === this.id; })[0]; + let obj = CustTable.filter((obj) => { return obj.id === this.id; })[0]; obj.value = Number((this.element as HTMLInputElement).value); - ValidateCustEntry(obj); + obj.validate(); }); } } }; -function ValidateCustEntry(entry: CustEntry): ErrorToolTip | null { - let elementId = entry.name; - let tip = new ErrorToolTip(elementId); - tip.clear(); - if (entry.value! == 0) - return null; - if (entry.value! < entry.min || entry.value! > entry.max) { - tip.setMsg(`Expected range: [${entry.min}, ${entry.max}], got ${entry.value}.`); - tip.show(); - return tip; - } - if (entry.validator && !entry.validator(entry.value)) { - tip.setMsg(`Invalid value: ${entry.value}. Did not pass this validator: ${entry.validator}.`); - tip.show(); - return tip; - } - return null; -} - -function ValidateCust() { - let tooltips: Array = []; - - for (let i of CustTable!) { - let tip = ValidateCustEntry(i); - if (tip) { - tooltips.push(tip); - } - } - - if (tooltips.length > 0) { - throw new Error("Invalid cust"); - } -} - function SaveCust(buffer) { let view = new DataView(buffer); let storage = {}; - storage["custRev"] = CUST_REV; - for (let i of CustTable!) { - let id = i.name; - i.value = Number((document.getElementById(id) as HTMLInputElement).value); - storage[i.name] = i.value; + for (let i of CustTable) { + i.update(); + if (!i.validate() || i.value === null) { + document.getElementById(i.id)!.focus(); + throw new Error(`Invalid ${i.name}`); + } + if (!i.offset) { + document.getElementById(i.id)!.focus(); + throw new Error(`Failed to get offset for ${i.name}`);; + } switch (i.size) { case 2: - view.setUint16(i.offset!, i.value, true); + view.setUint16(i.offset, i.value, true); break; case 4: - view.setUint32(i.offset!, i.value, true); + view.setUint32(i.offset, i.value, true); break; default: - document.getElementById(i.name)!.focus(); - throw new Error("Unknown size at " + i); + document.getElementById(i.id)!.focus(); + throw new Error(`Unknown size at ${i.name}`); } + storage[i.id] = i.value; } - ValidateCust(); + storage["custRev"] = CUST_REV; localStorage.setItem("last_saved", JSON.stringify(storage)); let a = document.createElement("a"); a.href = window.URL.createObjectURL(new Blob([buffer], { type: "application/octet-stream" })); @@ -394,61 +334,20 @@ function LoadLastSaved() { } function LoadDefault() { - for (let i of CustTable!) { - let id = i.name; - (document.getElementById(id) as HTMLInputElement).value = String(i.defval); + for (let i of CustTable) { + (document.getElementById(i.id) as HTMLInputElement).value = String(i.defval); } } function ClearHTMLForm() { - if (!CustTable) { - return; - } for (let i of CustTable) { - let id = i.name; - let input = document.getElementById(id) as HTMLInputElement; - input.parentElement?.parentElement?.remove(); + i.clearForm(); } } function UpdateHTMLForm() { - let dict = Object.assign({}, ...CustTable!.map((x: { name: any; }) => ({ [x.name]: x }))); - let form = document.getElementById("form")!; - for (let i of CustTable!) { - let id = i.name; - let input = document.getElementById(id) as HTMLInputElement; - if (!input) { - let grid = document.createElement("div"); - grid.classList.add("grid"); - - // Label and input box - input = document.createElement("input"); - input.min = dict[i.name].min; - input.max = dict[i.name].max; - input.id = id; - input.type = "number"; - input.step = dict[i.name].step; - let label = document.createElement("label"); - label.setAttribute("for", id); - label.innerHTML = id; - label.appendChild(input); - grid.appendChild(label); - - // Description in blockquote style - let desc = dict[i.name].desc!; - let block = document.createElement("blockquote"); - block.style["margin-top"] = "0"; - block.innerHTML = desc; - block.setAttribute("for", id); - grid.appendChild(block); - - grid.style["margin-top"] = "3rem"; - form.appendChild(grid); - - let tooltip = new ErrorToolTip(id); - tooltip.addChangeListener(); - } - input.value = dict[i.name].value; + for (let i of CustTable) { + i.createForm(); } let default_btn = document.getElementById("load_default")!; @@ -457,12 +356,15 @@ function UpdateHTMLForm() { LoadDefault(); }); + let last_btn = document.getElementById("load_saved")!; if (LastSaved()) { - let last_btn = document.getElementById("load_saved")!; + last_btn.style.removeProperty("display"); last_btn.removeAttribute("disabled"); last_btn.addEventListener('click', () => { LoadLastSaved(); }); + } else { + last_btn.style.setProperty("display", "none"); } let save_btn = document.getElementById("save")!; @@ -481,16 +383,10 @@ function ParseCust(magicOffset, buffer) { let view = new DataView(buffer); let offset = magicOffset + 4; let rev = view.getUint16(offset, true); - if (rev != 2 && rev != 3) { - throw new Error("Unsupported custRev, expected: 2 or 3, got " + rev); + if (rev != CUST_REV) { + throw new Error(`Unsupported custRev, expected: ${CUST_REV}, got ${rev}`); } - CUST_REV = rev; document.getElementById("cust_rev")!.innerHTML = `Cust V${CUST_REV} is loaded.`; - if (rev == 2) { - CustTable = CustTableV2; - } else { - CustTable = CustTableV3; - } offset += 2; for (let i of CustTable) { @@ -503,10 +399,11 @@ function ParseCust(magicOffset, buffer) { i.value = view.getUint32(offset, true); break; default: - document.getElementById(i.name)!.focus(); + document.getElementById(i.id)!.focus(); throw new Error("Unknown size at " + i); } offset += i.size; + i.validate(); } } @@ -521,7 +418,6 @@ fileInput.addEventListener('change', (event) => { let offset = FindMagicOffset(buffer); ClearHTMLForm(); ParseCust(offset, buffer); - ValidateCust(); UpdateHTMLForm(); } catch (e) { console.log(e); @@ -536,7 +432,9 @@ fileInput.addEventListener('change', (event) => { type ReleaseInfo = { OCSuiteVer: string; LoaderKipUrl: string; + LoaderKipTime: string; SdOutZipUrl: string; + SdOutZipTime: string; AMSVer: string; AMSUrl: string; }; @@ -568,7 +466,9 @@ async function fetchRelease(): Promise { let info: ReleaseInfo = { OCSuiteVer: latestVerFromSuite, LoaderKipUrl: loaderKip.browser_download_url, + LoaderKipTime: loaderKip.updated_at, SdOutZipUrl: sdOut.browser_download_url, + SdOutZipTime: sdOut.updated_at, AMSVer: correspondingVerFromAMS, AMSUrl: amsReleaseUrl }; @@ -589,13 +489,13 @@ async function updateDownloadUrls() { let info = await fetchRelease(); if (info) { - const loaderKipName = `loader.kip ${info.OCSuiteVer}`; + const loaderKipName = `loader.kip ${info.OCSuiteVer}
    ${info.LoaderKipTime}`; updateHref("loader_kip_btn", loaderKipName, info.LoaderKipUrl); - const sdOutName = `SdOut.zip ${info.OCSuiteVer}`; + const sdOutName = `SdOut.zip ${info.OCSuiteVer}
    ${info.SdOutZipTime}`; updateHref("sdout_zip_btn", sdOutName, info.SdOutZipUrl); - const amsName = `Atmosphere-NX ${info.AMSVer}`; + const amsName = `Atmosphere-NX ${info.AMSVer}`; updateHref("ams_btn", amsName, info.AMSUrl); } }