diff --git a/Source/sys-clk/common/include/registers.h b/Source/sys-clk/common/include/registers.h
index e782f1c6..d645e331 100644
--- a/Source/sys-clk/common/include/registers.h
+++ b/Source/sys-clk/common/include/registers.h
@@ -12,9 +12,8 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
- *
+ *
*/
-
#pragma once
@@ -310,6 +309,9 @@
#define EMC_PMACRO_CMD_CTRL_1_0 0x784
#define EMC_PMACRO_CMD_CTRL_2_0 0x788
+#define MC_REGISTER_BASE 0x70019000
+#define MC_REGISTER_REGION_SIZE 0x1000
+
#define MC_INTSTATUS_0 0x000
#define MC_INTMASK_0 0x004
#define MC_ERR_STATUS_0 0x008
@@ -489,4 +491,40 @@
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xBEC
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS_0 0xC00
#define MC_SECURITY_CARVEOUT2_BOM_0 0xC5C
-#define MC_SECURITY_CARVEOUT3_BOM_0 0xCAC
\ No newline at end of file
+#define MC_SECURITY_CARVEOUT3_BOM_0 0xCAC
+
+#define CLDVFS_REGION_BASE 0x70110000
+#define CLDVFS_REGION_SIZE 0x1000
+#define CL_DVFS_CTRL_0 0x0
+#define CL_DVFS_CONFIG_0 0x4
+#define CL_DVFS_PARAMS_0 0x8
+#define CL_DVFS_TUNE0_0 0xC
+#define CL_DVFS_TUNE1_0 0x10
+#define CL_DVFS_FREQ_REQ_0 0x14
+#define CL_DVFS_SCALE_RAMP_0 0x18
+#define CL_DVFS_DROOP_CTRL_0 0x1C
+#define CL_DVFS_OUTPUT_CFG_0 0x20
+#define CL_DVFS_OUTPUT_FORCE_0 0x24
+#define CL_DVFS_MONITOR_CTRL_0 0x28
+#define CL_DVFS_MONITOR_DATA_0 0x2C
+#define CL_DVFS_I2C_CFG_0 0x40
+#define CL_DVFS_I2C_VDD_REG_ADDR_0 0x44
+#define CL_DVFS_I2C_STS_0 0x48
+#define CL_DVFS_INTR_STS_0 0x5C
+#define CL_DVFS_INTR_EN_0 0x60
+#define DVFS_DFLL_THROTTLE_CTRL_0 0x64
+#define DVFS_DFLL_THROTTLE_LIGHT_0 0x68
+#define DVFS_DFLL_THROTTLE_MEDIUM_0 0x6C
+#define DVFS_DFLL_THROTTLE_HEAVY_0 0x70
+#define DVFS_CC4_HVC_0 0x74
+#define CL_DVFS_MONITOR_DATA_0 0x2C
+#define CL_DVFS_I2C_CFG_0 0x40
+#define CL_DVFS_I2C_VDD_REG_ADDR_0 0x44
+#define CL_DVFS_I2C_STS_0 0x48
+#define CL_DVFS_INTR_STS_0 0x5C
+#define CL_DVFS_INTR_EN_0 0x60
+#define DVFS_DFLL_THROTTLE_CTRL_0 0x64
+#define DVFS_DFLL_THROTTLE_LIGHT_0 0x68
+#define DVFS_DFLL_THROTTLE_MEDIUM_0 0x6C
+#define DVFS_DFLL_THROTTLE_HEAVY_0 0x70
+#define CL_DVFS_I2C_CLK_DIVISOR_REGISTER_0 0x16C
diff --git a/Source/sys-clk/common/include/sysclk/config.h b/Source/sys-clk/common/include/sysclk/config.h
index d9db7297..c0aba13e 100644
--- a/Source/sys-clk/common/include/sysclk/config.h
+++ b/Source/sys-clk/common/include/sysclk/config.h
@@ -58,7 +58,7 @@ typedef enum {
HorizonOCConfigValue_DVFSMode,
HorizonOCConfigValue_DVFSOffset,
-
+ HorizonOCConfigValue_LiveCpuUv,
HorizonOCConfigValue_EnableExperimentalSettings,
HorizonOCConfigValue_GPUScheduling,
@@ -235,6 +235,9 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
case HorizonOCConfigValue_GPUScheduling:
return pretty ? "GPU Scheduling" : "gpu_scheduling";
+ case HorizonOCConfigValue_LiveCpuUv:
+ return pretty ? "Live CPU Undervolt" : "live_cpu_uv";
+
case HorizonOCConfigValue_EnableExperimentalSettings:
return pretty ? "Enable Experimental Settings" : "enable_experimental_settings";
@@ -416,6 +419,7 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
case HorizonOCConfigValue_OverwriteRefreshRate:
case HorizonOCConfigValue_EnableUnsafeDisplayFreqs:
case HorizonOCConfigValue_GPUScheduling:
+ case HorizonOCConfigValue_LiveCpuUv:
return 0ULL;
case HocClkConfigValue_EristaMaxCpuClock:
return 1785ULL;
@@ -463,6 +467,7 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case HorizonOCConfigValue_EnableUnsafeDisplayFreqs:
case HocClkConfigValue_IsFirstLoad:
case HorizonOCConfigValue_EnableExperimentalSettings:
+ case HorizonOCConfigValue_LiveCpuUv:
return (input & 0x1) == input;
case KipConfigValue_custRev:
diff --git a/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp
index 7148ef84..67466b82 100644
--- a/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp
+++ b/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp
@@ -409,7 +409,8 @@ void MiscGui::listUI()
// };
if(this->configList->values[HorizonOCConfigValue_EnableExperimentalSettings]) {
this->listElement->addItem(new tsl::elm::CategoryHeader("Experimental"));
-
+
+ addConfigToggle(HorizonOCConfigValue_LiveCpuUv, nullptr);
std::vector gpuSchedValues = {
NamedValue("Do not override", GpuSchedulingMode_DoNotOverride),
NamedValue("Enabled", GpuSchedulingMode_Enabled, "96.5% limit"),
diff --git a/Source/sys-clk/sysmodule/perms.json b/Source/sys-clk/sysmodule/perms.json
index c4eb76b9..5b705463 100644
--- a/Source/sys-clk/sysmodule/perms.json
+++ b/Source/sys-clk/sysmodule/perms.json
@@ -57,6 +57,15 @@
"is_io": true
}
},
+ {
+ "type": "map",
+ "value": {
+ "address": "0x70110000",
+ "size": "0x1000",
+ "is_ro": false,
+ "is_io": true
+ }
+ },
{
"type": "syscalls",
"value": {
diff --git a/Source/sys-clk/sysmodule/src/board.cpp b/Source/sys-clk/sysmodule/src/board.cpp
index 57e7a1cc..4cc7fe45 100644
--- a/Source/sys-clk/sysmodule/src/board.cpp
+++ b/Source/sys-clk/sysmodule/src/board.cpp
@@ -105,6 +105,8 @@ u16 cpuSpeedo0, cpuSpeedo2, socSpeedo0; // CPU, GPU, SOC
u32 speedoBracket;
u16 cpuIDDQ, gpuIDDQ, socIDDQ;
u8 g_dramID = 0;
+u64 cldvfs, cldvfs_temp;
+u32 cachedEristaUvLowTune0 = 0, cachedEristaUvLowTune1 = 0, cachedMarikoUvHighTune0 = 0;
static const u32 ramBrackets[][22] = {
{ 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, },
@@ -290,6 +292,16 @@ void Board::Initialize()
}
FetchHardwareInfos();
+ rc = svcQueryMemoryMapping(&cldvfs, &cldvfs_temp, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE);
+ ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (cldvfs)");
+ if(Board::GetSocType() == SysClkSocType_Erista) {
+ cachedEristaUvLowTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0);
+ cachedEristaUvLowTune1 = *(u32*)(cldvfs + CL_DVFS_TUNE1_0);
+ } else {
+ Board::SetHz(SysClkModule_CPU, 1785000000);
+ cachedMarikoUvHighTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0);
+ Board::ResetToStockCpu();
+ }
}
void Board::fuseReadSpeedos() {
@@ -1134,9 +1146,6 @@ void Board::PcvHijackDvfs(u32 vmin) {
FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
}
-#define MC_REGISTER_BASE 0x70019000
-#define MC_REGISTER_REGION_SIZE 0x1000
-
bool Board::IsDram8GB() {
SecmonArgs args = {};
args.X[0] = 0xF0000002;
@@ -1173,4 +1182,125 @@ void Board::SetDisplayRefreshDockedState(bool docked) {
if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag) {
DisplayRefresh_SetDockedState(docked);
}
+}
+
+
+typedef struct EristaCpuUvEntry {
+ u32 tune0;
+ u32 tune1;
+} EristaCpuUvEntry;
+typedef struct MarikoCpuUvEntry {
+ u32 tune0_low;
+ u32 tune0_high;
+ u32 tune1_low;
+ u32 tune1_high;
+} MarikoCpuUvEntry;
+
+EristaCpuUvEntry eristaCpuUvTable[5] = {
+ {0xffff, 0x27007ff},
+ {0xefff, 0x27407ff},
+ {0xdfff, 0x27807ff},
+ {0xdfdf, 0x27a07ff},
+ {0xcfdf, 0x37007ff},
+};
+
+MarikoCpuUvEntry marikoCpuUvLow[12] = {
+ {0xffa0, 0xffff, 0x21107ff, 0},
+ {0x0, 0xffdf, 0x21107ff, 0x27207ff},
+ {0xffdf, 0xffdf, 0x21107ff, 0x27307ff},
+ {0xffff, 0xffdf, 0x21107ff, 0x27407ff},
+ {0x0, 0xffdf, 0x21607ff, 0x27707ff},
+ {0x0, 0xffdf, 0x21607ff, 0x27807ff},
+ {0x0, 0xdfff, 0x21607ff, 0x27b07ff},
+ {0xdfff, 0xdfff, 0x21707ff, 0x27b07ff},
+ {0xdfff, 0xdfff, 0x21707ff, 0x27c07ff},
+ {0xdfff, 0xdfff, 0x21707ff, 0x27d07ff},
+ {0xdfff, 0xdfff, 0x21707ff, 0x27e07ff},
+ {0xdfff, 0xdfff, 0x21707ff, 0x27f07ff},
+};
+
+MarikoCpuUvEntry marikoCpuUvHigh[12] = {
+ {0x0, 0xffff, 0, 0},
+ {0x0, 0xffdf, 0, 0x27207ff},
+ {0x0, 0xffdf, 0, 0x27307ff},
+ {0x0, 0xffdf, 0, 0x27407ff},
+ {0x0, 0xffdf, 0, 0x27707ff},
+ {0x0, 0xffdf, 0, 0x27807ff},
+ {0x0, 0xdfff, 0, 0x27b07ff},
+ {0x0, 0xdfff, 0, 0x27c07ff},
+ {0x0, 0xdfff, 0, 0x27d07ff},
+ {0x0, 0xdfff, 0, 0x27e07ff},
+ {0x0, 0xdfff, 0, 0x27f07ff},
+ {0x0, 0xdfff, 0, 0x27f07ff},
+};
+void Board::SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint) {
+
+ u32* tune0_ptr = (u32*)(cldvfs + CL_DVFS_TUNE0_0);
+ u32* tune1_ptr = (u32*)(cldvfs + CL_DVFS_TUNE1_0);
+ if(Board::GetSocType() == SysClkSocType_Mariko) {
+ if(Board::GetHz(SysClkModule_CPU) < tbreakPoint && (levelLow || levelHigh)) {
+ if(levelLow) {
+ *tune0_ptr = marikoCpuUvLow[levelLow-1].tune0_low;
+ *tune1_ptr = marikoCpuUvLow[levelLow-1].tune1_low;
+ }
+ return;
+ } else {
+ if(levelLow) {
+ *tune0_ptr = marikoCpuUvLow[levelLow-1].tune0_low;
+ *tune1_ptr = marikoCpuUvLow[levelLow-1].tune1_low;
+ }
+ if(levelHigh) {
+ *tune0_ptr = marikoCpuUvHigh[levelHigh-1].tune0_high;
+ *tune1_ptr = marikoCpuUvHigh[levelHigh-1].tune1_high;
+ }
+ return;
+ }
+ if(Board::GetHz(SysClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak
+ *tune0_ptr = 0xCFFF;
+ *tune1_ptr = 0xFF072201;
+ return;
+ } else if (Board::GetHz(SysClkModule_CPU) >= tbreakPoint || (!levelHigh)) {
+ *tune0_ptr = cachedMarikoUvHighTune0; // per console?
+ *tune1_ptr = 0xFFF7FF3F;
+ return;
+ }
+ } else {
+ if(Board::GetHz(SysClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak
+ *tune0_ptr = cachedEristaUvLowTune0; // I think each erista has a different tune0/tune1?
+ *tune1_ptr = cachedEristaUvLowTune1;
+ return;
+ } else {
+ if(levelLow) {
+ *tune0_ptr = eristaCpuUvTable[levelLow-1].tune0;
+ *tune1_ptr = eristaCpuUvTable[levelLow-1].tune1;
+ } else {
+ *tune0_ptr = 0x0;
+ *tune1_ptr = 0x0;
+ }
+ }
+ }
+}
+/*
+enum TableConfig: u32 {
+ DEFAULT_TABLE = 1,
+ TBREAK_1581 = 2,
+ TBREAK_1683 = 3,
+ EXTREME_TABLE = 4,
+};
+*/
+u32 Board::CalculateTbreak(u32 table) {
+ if(Board::GetSocType() == SysClkSocType_Erista)
+ return 1581000000;
+ else {
+ switch(table) {
+ case 1 ... 2:
+ case 4:
+ return 1581000000;
+ case 3:
+ return 1683000000;
+ default:
+ return 1581000000;
+ }
+ }
+
}
\ No newline at end of file
diff --git a/Source/sys-clk/sysmodule/src/board.h b/Source/sys-clk/sysmodule/src/board.h
index f8c040a3..4b7a28c3 100644
--- a/Source/sys-clk/sysmodule/src/board.h
+++ b/Source/sys-clk/sysmodule/src/board.h
@@ -66,6 +66,8 @@ class Board
static bool IsDram8GB();
static void SetGpuSchedulingMode(GpuSchedulingMode mode);
static void SetDisplayRefreshDockedState(bool docked);
+ static void SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint);
+ static u32 CalculateTbreak(u32 table);
protected:
static void FetchHardwareInfos();
static PcvModule GetPcvModule(SysClkModule sysclkModule);
diff --git a/Source/sys-clk/sysmodule/src/clock_manager.cpp b/Source/sys-clk/sysmodule/src/clock_manager.cpp
index cf5e4c89..d9be4b5a 100644
--- a/Source/sys-clk/sysmodule/src/clock_manager.cpp
+++ b/Source/sys-clk/sysmodule/src/clock_manager.cpp
@@ -550,9 +550,15 @@ void ClockManager::Tick()
this->context->voltages[HocClkVoltage_GPU] = vmin * 1000;
}
-
Board::SetHz((SysClkModule)module, nearestHz);
this->context->freqs[module] = nearestHz;
+ if(module == SysClkModule_CPU && this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv))
+ {
+ if(Board::GetSocType() == SysClkSocType_Erista)
+ Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
+ else
+ Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf)));
+ }
}
if(module == SysClkModule_MEM && Board::GetSocType() == SysClkSocType_Mariko && targetHz < oldHz && this->config->GetConfigValue(HorizonOCConfigValue_DVFSMode) == DVFSMode_Hijack) {
@@ -583,6 +589,14 @@ void ClockManager::Tick()
void ClockManager::ResetToStockClocks() {
Board::ResetToStockCpu();
+ if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv))
+ {
+ if(Board::GetSocType() == SysClkSocType_Erista)
+ Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
+ else
+ Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf)));
+ }
+
Board::ResetToStockGpu();
}
@@ -656,6 +670,13 @@ bool ClockManager::RefreshContext()
case SysClkModule_CPU:
if(!(apmExtIsBoostMode(mode) || (this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode) && apmExtIsBoostMode(mode))))
Board::ResetToStockCpu();
+ if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv)) {
+ if(Board::GetSocType() == SysClkSocType_Erista)
+ Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000);
+ else
+ Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf)));
+ }
+
break;
case SysClkModule_GPU:
Board::ResetToStockGpu();
diff --git a/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp b/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
index 95f7a010..cd3813f3 100644
Binary files a/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp and b/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp differ
diff --git a/dist/atmosphere/kips/hoc.kip b/dist/atmosphere/kips/hoc.kip
index 08205552..9a9745e8 100644
Binary files a/dist/atmosphere/kips/hoc.kip and b/dist/atmosphere/kips/hoc.kip differ
diff --git a/dist/switch/.overlays/Horizon-OC-Monitor.ovl b/dist/switch/.overlays/Horizon-OC-Monitor.ovl
index d48cb866..fb0767fb 100644
Binary files a/dist/switch/.overlays/Horizon-OC-Monitor.ovl and b/dist/switch/.overlays/Horizon-OC-Monitor.ovl differ
diff --git a/dist/switch/.overlays/horizon-oc-overlay.ovl b/dist/switch/.overlays/horizon-oc-overlay.ovl
index cda28f06..ffd93bbf 100644
Binary files a/dist/switch/.overlays/horizon-oc-overlay.ovl and b/dist/switch/.overlays/horizon-oc-overlay.ovl differ