diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 1fb0768f..81cd2c20 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -38,7 +38,7 @@ volatile CustomizeTable C = { .commonEmcMemVolt = 1175000, // LPDDR4X JEDEC Specification .eristaEmcMaxClock = 1600000, // Maximum HB-MGCH ram rating -.marikoEmcMaxClock = 2133000, // Hynix NME and Samsung AM-MGCJ Rating (others are 4766MT, 2133MHz) +.marikoEmcMaxClock = 2133000, .marikoEmcVddqVolt = 600000, .emcDvbShift = 0, diff --git a/Source/sys-clk/common/include/sysclk/config.h b/Source/sys-clk/common/include/sysclk/config.h index 8a120ffc..2749d28b 100644 --- a/Source/sys-clk/common/include/sysclk/config.h +++ b/Source/sys-clk/common/include/sysclk/config.h @@ -62,6 +62,7 @@ typedef enum { HocClkConfigValue_KipEditing, HocClkConfigValue_KipFileName, + HocClkConfigValue_HighRamFreqVmin, KipConfigValue_custRev, KipConfigValue_mtcConf, @@ -231,6 +232,9 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr case HocClkConfigValue_KipFileName: return pretty ? "KIP File Name" : "kip_file_name"; + case HocClkConfigValue_HighRamFreqVmin: + return pretty ? "High RAM Vmin" : "high_ram_vmin"; + // KIP config values case KipConfigValue_custRev: return pretty ? "Custom Revision" : "kip_cust_rev"; @@ -420,6 +424,8 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val) return 8600ULL; case HocClkConfigValue_LiteTDPLimit: return 6400ULL; + case HocClkConfigValue_HighRamFreqVmin: + return 610ULL; default: return 0ULL; } @@ -543,6 +549,7 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in case KipConfigValue_g_volt_e_998400: case KipConfigValue_g_volt_e_1036800: case KipConfigValue_g_volt_e_1075200: + case HocClkConfigValue_HighRamFreqVmin: return true; default: 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 4e792a81..98520482 100644 --- a/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp +++ b/Source/sys-clk/overlay/src/ui/gui/misc_gui.cpp @@ -336,7 +336,16 @@ void MiscGui::listUI() addFreqButton(HocClkConfigValue_EristaMaxGpuClock, nullptr, SysClkModule_GPU, gpu_freq_label_e); // addFreqButton(HocClkConfigValue_EristaMaxMemClock, nullptr, SysClkModule_MEM, emc_freq_label_e); } - + addConfigButton( + HocClkConfigValue_HighRamFreqVmin, + "RAM HF Vmin", + ValueRange(480, 960, 5, "mV", 1), + "Voltage", + &thresholdsDisabled, + {}, + {}, + false + ); this->listElement->addItem(new tsl::elm::CategoryHeader("KIP Editing")); addConfigToggle(HocClkConfigValue_KipEditing, nullptr); diff --git a/Source/sys-clk/sysmodule/src/clock_manager.cpp b/Source/sys-clk/sysmodule/src/clock_manager.cpp index e1b0d463..7636c71e 100644 --- a/Source/sys-clk/sysmodule/src/clock_manager.cpp +++ b/Source/sys-clk/sysmodule/src/clock_manager.cpp @@ -33,6 +33,9 @@ #include "errors.h" #include "ipc_service.h" #include "kip.h" +#include "i2c_reg.h" + + #define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0)) ClockManager *ClockManager::instance = NULL; @@ -247,8 +250,12 @@ u32 findIndexMHz(u32 arr[], u32 size, u32 value) { } void ClockManager::Tick() -{ +{ std::scoped_lock lock{this->contextMutex}; + if((Board::GetHz(SysClkModule_MEM) / 1000000) > 1600) + I2c_BuckConverter_SetMvOut(Board::GetSocType == SysClkSocType_Mariko ? &I2c_Mariko_GPU : &I2c_Erista_GPU, this->config->GetConfigValue(HocClkConfigValue_HighRamFreqVmin)); + else + I2c_BuckConverter_SetMvOut(Board::GetSocType == SysClkSocType_Mariko ? &I2c_Mariko_GPU : &I2c_Erista_GPU, this->config->GetConfigValue(KipConfigValue_marikoGpuVmin)); std::uint32_t mode = 0; AppletOperationMode opMode = appletGetOperationMode(); Result rc = apmExtGetCurrentPerformanceConfiguration(&mode); @@ -349,7 +356,6 @@ void ClockManager::Tick() if (targetHz) { - maxHz = this->GetMaxAllowedHz((SysClkModule)module, this->context->profile); nearestHz = this->GetNearestHz((SysClkModule)module, targetHz, maxHz); if (nearestHz != this->context->freqs[module] && this->context->enabled) { diff --git a/Source/sys-clk/sysmodule/src/clock_manager.h b/Source/sys-clk/sysmodule/src/clock_manager.h index c9fab4e9..02c8cbcd 100644 --- a/Source/sys-clk/sysmodule/src/clock_manager.h +++ b/Source/sys-clk/sysmodule/src/clock_manager.h @@ -35,7 +35,7 @@ #include "board.h" #include #include "integrations.h" - +#include "i2c_reg.h" class ReverseNXSync; class ClockManager diff --git a/Source/sys-clk/sysmodule/src/i2c_reg.cpp b/Source/sys-clk/sysmodule/src/i2c_reg.cpp new file mode 100644 index 00000000..8ed9f4db --- /dev/null +++ b/Source/sys-clk/sysmodule/src/i2c_reg.cpp @@ -0,0 +1,186 @@ +#include "i2c_reg.h" + +Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) { + // ams::fatal::srv::StopSoundTask::StopSound() + // I2C Bus Communication Reference: https://www.ti.com/lit/an/slva704/slva704.pdf + struct { + u8 reg; + u8 val; + } __attribute__((packed)) cmd; + + I2cSession _session; + Result res = i2cOpenSession(&_session, dev); + if (res) + return res; + + cmd.reg = reg; + cmd.val = val; + res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + i2csessionClose(&_session); + return res; +} + +Result I2cRead_OutU8(I2cDevice dev, u8 reg, u8 *out) { + struct { u8 reg; } __attribute__((packed)) cmd; + struct { u8 val; } __attribute__((packed)) rec; + + I2cSession _session; + Result res = i2cOpenSession(&_session, dev); + if (res) + return res; + + cmd.reg = reg; + res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + if (res) { + i2csessionClose(&_session); + return res; + } + + res = i2csessionReceiveAuto(&_session, &rec, sizeof(rec), I2cTransactionOption_All); + i2csessionClose(&_session); + if (res) { + return res; + } + + *out = rec.val; + return 0; +} + +Result I2cRead_OutU16(I2cDevice dev, u8 reg, u16 *out) { + struct { u8 reg; } __attribute__((packed)) cmd; + struct { u16 val; } __attribute__((packed)) rec; + + I2cSession _session; + Result res = i2cOpenSession(&_session, dev); + if (res) + return res; + + cmd.reg = reg; + res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + if (res) { + i2csessionClose(&_session); + return res; + } + + res = i2csessionReceiveAuto(&_session, &rec, sizeof(rec), I2cTransactionOption_All); + i2csessionClose(&_session); + if (res) { + return res; + } + + *out = rec.val; + return 0; +} + +float I2c_Max17050_GetBatteryCurrent() { + u16 val; + Result res = I2cRead_OutU16(I2cDevice_Max17050, MAX17050_CURRENT_REG, &val); + if (res) + return 0.f; + + const float SenseResistor = 5.; // in uOhm + const float CGain = 1.99993; + return (s16)val * (1.5625 / (SenseResistor * CGain)); +} + +u32 I2c_BuckConverter_MultiplierToMvOut(const I2c_BuckConverter_Domain* domain, u8 multiplier) { + return (domain->uv_min + domain->uv_step * multiplier) / 1000; +} + +u8 I2c_BuckConverter_MvOutToMultiplier(const I2c_BuckConverter_Domain* domain, u32 mvolt) { + u32 uvolt = mvolt * 1000; + if (uvolt < domain->uv_min) + uvolt = domain->uv_min; + if (uvolt > domain->uv_max) + uvolt = domain->uv_max; + + return (uvolt - domain->uv_min) / domain->uv_step; +} + +u32 I2c_BuckConverter_GetMvOut(const I2c_BuckConverter_Domain* domain) { + u8 val; + // 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; + } + return I2c_BuckConverter_MultiplierToMvOut(domain, val & domain->volt_mask); +} + +Result I2c_BuckConverter_SetMvOut(const I2c_BuckConverter_Domain* domain, u32 mvolt) { + u8 val; + Result res = I2cRead_OutU8(domain->device, domain->reg, &val); + if (R_FAILED(res)) + return res; + + u8 multiplier = I2c_BuckConverter_MvOutToMultiplier(domain, mvolt); + val &= ~domain->volt_mask; + val |= multiplier & domain->volt_mask; + + res = I2cSet_U8(domain->device, domain->reg, val); + if (R_FAILED(res)) + return res; + + // 5ms Ramp delay + svcSleepThread(5E6); + u8 new_val; + res = I2cRead_OutU8(domain->device, domain->reg, &new_val); + if (R_FAILED(res)) + return res; + if (new_val != val) + return -1; + + return 0; +} + +u8 I2c_Bq24193_Convert_mA_Raw(u32 ma) { + // Adjustment is required + u8 raw = 0; + + if (ma > MA_RANGE_MAX) // capping + ma = MA_RANGE_MAX; + + bool pct20 = ma <= (MA_RANGE_MIN - 64); + if (pct20) { + ma = ma * 5; + raw |= 0x1; + } + + ma -= ma % 100; // round to 100 + ma -= (MA_RANGE_MIN - 64); // ceiling + raw |= (ma >> 6) << 2; + + return raw; +}; + +u32 I2c_Bq24193_Convert_Raw_mA(u8 raw) { + // No adjustment is allowed + u32 ma = (((raw >> 2)) << 6) + MA_RANGE_MIN; + + bool pct20 = raw & 1; + if (pct20) + ma = ma * 20 / 100; + + return ma; +}; + +Result I2c_Bq24193_GetFastChargeCurrentLimit(u32 *ma) { + u8 raw; + Result res = I2cRead_OutU8(I2cDevice_Bq24193, BQ24193_CHARGE_CURRENT_CONTROL_REG, &raw); + if (res) + return res; + + *ma = I2c_Bq24193_Convert_Raw_mA(raw); + return 0; +} + +Result I2c_Bq24193_SetFastChargeCurrentLimit(u32 ma) { + u8 raw = I2c_Bq24193_Convert_mA_Raw(ma); + return I2cSet_U8(I2cDevice_Bq24193, BQ24193_CHARGE_CURRENT_CONTROL_REG, raw); +} \ No newline at end of file diff --git a/Source/sys-clk/sysmodule/src/i2c_reg.h b/Source/sys-clk/sysmodule/src/i2c_reg.h new file mode 100644 index 00000000..1a85dd36 --- /dev/null +++ b/Source/sys-clk/sysmodule/src/i2c_reg.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +// To use i2c service, sm and i2c should be intialized via smInitialize() and i2cInitialize(). + +Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val); + +Result I2cRead_OutU8(I2cDevice dev, u8 reg, u8 *out); +Result I2cRead_OutU16(I2cDevice dev, u8 reg, u16 *out); + +// Max17050 fuel gauge +float I2c_Max17050_GetBatteryCurrent(); + +const u8 MAX17050_CURRENT_REG = 0x0A; + +// Buck Converter +typedef enum I2c_BuckConverter_Reg { + 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), used for Mariko VDDQ +} I2c_BuckConverter_Reg; + +typedef struct I2c_BuckConverter_Domain { + I2cDevice device; + I2c_BuckConverter_Reg reg; + u8 volt_mask; + u32 uv_step; + u32 uv_min; + u32 uv_max; + 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_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); + +// Bq24193 Battery management +u32 I2c_Bq24193_Convert_Raw_mA(u8 raw); +u8 I2c_Bq24193_Convert_mA_Raw(u32 ma); + +Result I2c_Bq24193_GetFastChargeCurrentLimit(u32 *ma); +Result I2c_Bq24193_SetFastChargeCurrentLimit(u32 ma); + +const u32 MA_RANGE_MIN = 512; +const u32 MA_RANGE_MAX = 4544; + +const u8 BQ24193_CHARGE_CURRENT_CONTROL_REG = 0x2; \ No newline at end of file