diff --git a/Source/sys-clk/common/include/sysclk.h b/Source/sys-clk/common/include/sysclk.h index ffcd71d7..e9befcaf 100644 --- a/Source/sys-clk/common/include/sysclk.h +++ b/Source/sys-clk/common/include/sysclk.h @@ -21,6 +21,7 @@ extern "C" { #include "sysclk/apm.h" #include "sysclk/config.h" #include "sysclk/errors.h" +#include "sysclk/psm_ext.h" #ifdef __cplusplus } diff --git a/Source/sys-clk/common/include/sysclk/config.h b/Source/sys-clk/common/include/sysclk/config.h index 1accb40d..0753bccb 100644 --- a/Source/sys-clk/common/include/sysclk/config.h +++ b/Source/sys-clk/common/include/sysclk/config.h @@ -22,6 +22,8 @@ typedef enum { HocClkConfigValue_UncappedClocks, HocClkConfigValue_OverwriteBoostMode, HocClkConfigValue_SyncReverseNXMode, + HocClkConfigValue_DockedGovernor, + HocClkConfigValue_HandheldGovernor, SysClkConfigValue_EnumMax, } SysClkConfigValue; @@ -49,6 +51,10 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr return pretty ? "Overwrite Boost Mode" : "ow_boost"; case HocClkConfigValue_SyncReverseNXMode: return pretty ? "ReverseNX Sync" : "rnx_sync"; + case HocClkConfigValue_DockedGovernor: + return pretty ? "Docked Governor" : "governor_d"; + case HocClkConfigValue_HandheldGovernor: + return pretty ? "Handheld Governor" : "governor_hh"; default: return NULL; } diff --git a/Source/sys-clk/common/include/sysclk/psm_ext.h b/Source/sys-clk/common/include/sysclk/psm_ext.h new file mode 100644 index 00000000..7065efbc --- /dev/null +++ b/Source/sys-clk/common/include/sysclk/psm_ext.h @@ -0,0 +1,77 @@ +#pragma once + +#include + +typedef enum { + PsmPDC_NewPDO = 1, //Received new Power Data Object + PsmPDC_NoPD = 2, //No Power Delivery source is detected + PsmPDC_AcceptedRDO = 3 //Received and accepted Request Data Object +} PsmChargeInfoPDC; //BM92T series + +typedef enum { + PsmPowerRole_Sink = 1, + PsmPowerRole_Source = 2 +} PsmPowerRole; + +const char* PsmPowerRoleToStr(PsmPowerRole role); + +typedef enum { + PsmInfoChargerType_None = 0, + PsmInfoChargerType_PD = 1, + PsmInfoChargerType_TypeC_1500mA = 2, + PsmInfoChargerType_TypeC_3000mA = 3, + PsmInfoChargerType_DCP = 4, + PsmInfoChargerType_CDP = 5, + PsmInfoChargerType_SDP = 6, + PsmInfoChargerType_Apple_500mA = 7, + PsmInfoChargerType_Apple_1000mA = 8, + PsmInfoChargerType_Apple_2000mA = 9 +} PsmInfoChargerType; + +const char* PsmInfoChargerTypeToStr(PsmInfoChargerType type); + +typedef enum { + PsmFlags_NoHub = BIT(0), //If hub is disconnected + PsmFlags_Rail = BIT(8), //At least one Joy-con is charging from rail + PsmFlags_SPDSRC = BIT(12), //OTG + PsmFlags_ACC = BIT(16) //Accessory +} PsmChargeInfoFlags; + +typedef struct { + int32_t InputCurrentLimit; //Input (Sink) current limit in mA + int32_t VBUSCurrentLimit; //Output (Source/VBUS/OTG) current limit in mA + int32_t ChargeCurrentLimit; //Battery charging current limit in mA (512mA when Docked, 768mA when BatteryTemperature < 17.0 C) + int32_t ChargeVoltageLimit; //Battery charging voltage limit in mV (3952mV when BatteryTemperature >= 51.0 C) + int32_t unk_x10; //Possibly an emum, getting the same value as PowerRole in all tested cases + int32_t unk_x14; //Possibly flags + PsmChargeInfoPDC PDCState; //Power Delivery Controller State + int32_t BatteryTemperature; //Battery temperature in milli C + int32_t RawBatteryCharge; //Raw battery charged capacity per cent-mille (i.e. 100% = 100000 pcm) + int32_t VoltageAvg; //Voltage avg in mV (more in Notes) + int32_t BatteryAge; //Battery age (capacity full / capacity design) per cent-mille (i.e. 100% = 100000 pcm) + PsmPowerRole PowerRole; + PsmInfoChargerType ChargerType; + int32_t ChargerVoltageLimit; //Charger and external device voltage limit in mV + int32_t ChargerCurrentLimit; //Charger and external device current limit in mA + PsmChargeInfoFlags Flags; //Unknown flags +} PsmChargeInfo; + +typedef enum { + Psm_EnableBatteryCharging = 2, + Psm_DisableBatteryCharging = 3, + Psm_EnableFastBatteryCharging = 10, + Psm_DisableFastBatteryCharging = 11, + Psm_GetBatteryChargeInfoFields = 17, +} IPsmServerCmd; + +bool PsmIsChargerConnected(const PsmChargeInfo* info); +bool PsmIsCharging(const PsmChargeInfo* info); + +typedef enum { + PsmBatteryState_Discharging, + PsmBatteryState_ChargingPaused, + PsmBatteryState_FastCharging +} PsmBatteryState; + +PsmBatteryState PsmGetBatteryState(const PsmChargeInfo* info); +const char* PsmGetBatteryStateIcon(const PsmChargeInfo* info); \ No newline at end of file diff --git a/Source/sys-clk/common/src/psm_ext.c b/Source/sys-clk/common/src/psm_ext.c new file mode 100644 index 00000000..c7c8a6dd --- /dev/null +++ b/Source/sys-clk/common/src/psm_ext.c @@ -0,0 +1,50 @@ +#include + +const char* PsmPowerRoleToStr(PsmPowerRole role) { + switch (role) { + case PsmPowerRole_Sink: return "Sink"; + case PsmPowerRole_Source: return "Source"; + default: return "Unknown"; + } +} + +const char* PsmInfoChargerTypeToStr(PsmInfoChargerType type) { + switch (type) { + case PsmInfoChargerType_None: return "None"; + case PsmInfoChargerType_PD: return "USB-C PD"; + case PsmInfoChargerType_TypeC_1500mA: + case PsmInfoChargerType_TypeC_3000mA: return "USB-C"; + case PsmInfoChargerType_DCP: return "USB DCP"; + case PsmInfoChargerType_CDP: return "USB CDP"; + case PsmInfoChargerType_SDP: return "USB SDP"; + case PsmInfoChargerType_Apple_500mA: + case PsmInfoChargerType_Apple_1000mA: + case PsmInfoChargerType_Apple_2000mA: return "Apple"; + default: return "Unknown"; + } +} + +bool PsmIsChargerConnected(const PsmChargeInfo* info) { + return info->ChargerType != PsmInfoChargerType_None; +} + +bool PsmIsCharging(const PsmChargeInfo* info) { + return PsmIsChargerConnected(info) && ((info->unk_x14 >> 8) & 1); +} + +PsmBatteryState PsmGetBatteryState(const PsmChargeInfo* info) { + if (!PsmIsChargerConnected(info)) + return PsmBatteryState_Discharging; + if (!PsmIsCharging(info)) + return PsmBatteryState_ChargingPaused; + return PsmBatteryState_FastCharging; +} + +const char* PsmGetBatteryStateIcon(const PsmChargeInfo* info) { + switch (PsmGetBatteryState(info)) { + case PsmBatteryState_Discharging: return "\u25c0"; // ◀ + case PsmBatteryState_ChargingPaused:return "| |"; + case PsmBatteryState_FastCharging: return "\u25b6"; // ▶ + default: return "?"; + } +} diff --git a/Source/sys-clk/manager/Makefile b/Source/sys-clk/manager/Makefile index bfd99a64..bcf2b20d 100644 --- a/Source/sys-clk/manager/Makefile +++ b/Source/sys-clk/manager/Makefile @@ -37,14 +37,14 @@ include $(DEVKITPRO)/libnx/switch_rules # of a homebrew executable (.nro). This is intended to be used for sysmodules. # NACP building is skipped as well. #--------------------------------------------------------------------------------- -TARGET := sys-clk-manager +TARGET := horizon-oc-manager BUILD := build.nx SOURCES := src ../common/src ../common/src/client RESOURCES := resources DATA := data INCLUDES := ../common/include APP_TITLE := Horizon OC Manager -APP_AUTHOR := Souldbminer, meha, b0rd2dEAth and RetroNX Team +APP_AUTHOR := Souldbminer and RetroNX Team ROMFS := $(BUILD)/romfs BOREALIS_PATH := lib/borealis diff --git a/Source/sys-clk/overlay/Makefile b/Source/sys-clk/overlay/Makefile index 0cc33c74..346cc0d5 100644 --- a/Source/sys-clk/overlay/Makefile +++ b/Source/sys-clk/overlay/Makefile @@ -17,7 +17,7 @@ include $(DEVKITPRO)/libnx/switch_rules # INCLUDES is a list of directories containing header files # EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". #--------------------------------------------------------------------------------- -TARGET := sys-clk-overlay +TARGET := horizon-oc-overlay BUILD := build OUTDIR := out RESOURCES := res diff --git a/Source/sys-clk/sysmodule/src/clock_manager.cpp b/Source/sys-clk/sysmodule/src/clock_manager.cpp index 7f8b9e77..250293a9 100644 --- a/Source/sys-clk/sysmodule/src/clock_manager.cpp +++ b/Source/sys-clk/sysmodule/src/clock_manager.cpp @@ -16,16 +16,16 @@ #include "errors.h" #include "ipc_service.h" -ClockManager* ClockManager::instance = NULL; +ClockManager *ClockManager::instance = NULL; -ClockManager* ClockManager::GetInstance() +ClockManager *ClockManager::GetInstance() { return instance; } void ClockManager::Exit() { - if(instance) + if (instance) { delete instance; } @@ -33,7 +33,7 @@ void ClockManager::Exit() void ClockManager::Initialize() { - if(!instance) + if (!instance) { instance = new ClockManager(); } @@ -60,7 +60,6 @@ ClockManager::ClockManager() this->lastCsvWriteNs = 0; this->rnxSync = new ReverseNXSync; - } ClockManager::~ClockManager() @@ -119,13 +118,13 @@ std::uint32_t ClockManager::GetMaxAllowedHz(SysClkModule module, SysClkProfile p } else { - if(module == SysClkModule_GPU) + if (module == SysClkModule_GPU) { - if(profile < SysClkProfile_HandheldCharging) + if (profile < SysClkProfile_HandheldCharging) { return Board::GetSocType() == SysClkSocType_Mariko ? 614400000 : 460800000; } - else if(profile <= SysClkProfile_HandheldChargingUSB) + else if (profile <= SysClkProfile_HandheldChargingUSB) { return 768000000; } @@ -211,31 +210,46 @@ void ClockManager::Tick() for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) { targetHz = this->context->overrideFreqs[module]; - - if (!targetHz) - { - targetHz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile); - } - - 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 && !apmExtIsBoostMode(this->context->perfConfId) && this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode)) +// if (!this->config->GetConfigValue(HocClkConfigValue_DockedGovernor) || !this->config->GetConfigValue(HocClkConfigValue_HandheldGovernor)) +// { + if (!targetHz) { - FileUtils::LogLine( - "[mgr] %s clock set : %u.%u MHz (target = %u.%u MHz)", - Board::GetModuleName((SysClkModule)module, true), - nearestHz / 1000000, nearestHz / 100000 - nearestHz / 1000000 * 10, - targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10); - - Board::SetHz((SysClkModule)module, nearestHz); - this->context->freqs[module] = nearestHz; - } else { - Board::ResetToStockCpu(); - Board::ResetToStockGpu(); + targetHz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile); } + + 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 && !apmExtIsBoostMode(this->context->perfConfId) && this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode)) + { + FileUtils::LogLine( + "[mgr] %s clock set : %u.%u MHz (target = %u.%u MHz)", + Board::GetModuleName((SysClkModule)module, true), + nearestHz / 1000000, nearestHz / 100000 - nearestHz / 1000000 * 10, + targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10); + + Board::SetHz((SysClkModule)module, nearestHz); + this->context->freqs[module] = nearestHz; + } + else + { + Board::ResetToStockCpu(); + Board::ResetToStockGpu(); + } + // } + // } else { + // #define GOVERNOR_LOAD_THRESHOLD 80 + // if(apmExtIsBoostMode(this->context->perfConfId)) { + // Board::ResetToStockCpu(); // GOVERNOR: Reset to stock clocks if boost mode (dont use governor when boosted) + // Board::ResetToStockGpu(); + // } else { + // // Actually run the CPU governor + // if(t210EmcLoadCpu() > GOVERNOR_LOAD_THRESHOLD) { + // realHz = targetHz / 1000000 + // } + // } } } } @@ -265,7 +279,6 @@ bool ClockManager::RefreshContext() this->context->applicationId = applicationId; hasChanged = true; this->rnxSync->Reset(applicationId); - } SysClkProfile profile = Board::GetProfile(); @@ -378,6 +391,7 @@ bool ClockManager::RefreshContext() return hasChanged; } -void ClockManager::SetRNXRTMode(ReverseNXMode mode) { +void ClockManager::SetRNXRTMode(ReverseNXMode mode) +{ this->rnxSync->SetRTMode(mode); } \ No newline at end of file diff --git a/Source/sys-clk/sysmodule/src/clock_manager.h b/Source/sys-clk/sysmodule/src/clock_manager.h index a56e6112..125c3a1e 100644 --- a/Source/sys-clk/sysmodule/src/clock_manager.h +++ b/Source/sys-clk/sysmodule/src/clock_manager.h @@ -39,6 +39,10 @@ class ClockManager void Tick(); void WaitForNextTick(); void SetRNXRTMode(ReverseNXMode mode); + struct { + std::uint32_t count; + std::uint32_t list[SYSCLK_FREQ_LIST_MAX]; + } freqTable[SysClkModule_EnumMax]; protected: bool IsAssignableHz(SysClkModule module, std::uint32_t hz); @@ -52,10 +56,6 @@ class ClockManager std::atomic_bool running; LockableMutex contextMutex; - struct { - std::uint32_t count; - std::uint32_t list[SYSCLK_FREQ_LIST_MAX]; - } freqTable[SysClkModule_EnumMax]; Config* config; SysClkContext* context; std::uint64_t lastTempLogNs; diff --git a/Source/sys-clk/sysmodule/src/fancontrol.c b/Source/sys-clk/sysmodule/src/fancontrol.c new file mode 100644 index 00000000..145a490e --- /dev/null +++ b/Source/sys-clk/sysmodule/src/fancontrol.c @@ -0,0 +1,250 @@ +#include "fancontrol.h" +#include "tmp451.h" + +//Fan curve table +const TemperaturePoint defaultTable[] = +{ + { .temperature_c = 25.0, .fanLevel_f = 0.10 }, + { .temperature_c = 30.0, .fanLevel_f = 0.20 }, + { .temperature_c = 35.0, .fanLevel_f = 0.30 }, + { .temperature_c = 40.0, .fanLevel_f = 0.40 }, + { .temperature_c = 45.0, .fanLevel_f = 0.50 }, + { .temperature_c = 50.0, .fanLevel_f = 0.60 }, + { .temperature_c = 55.0, .fanLevel_f = 0.70 }, + { .temperature_c = 60.0, .fanLevel_f = 0.80 }, + { .temperature_c = 65.0, .fanLevel_f = 0.90 }, + { .temperature_c = 70.0, .fanLevel_f = 1.00 } +}; + + +TemperaturePoint *fanControllerTable; + +//Fan +Thread FanControllerThread; +bool fanControllerThreadExit = false; + +//Log +char logPath[PATH_MAX]; + +//Power management +Event powerStateChangeEvent; +bool isPowerStateInitialized = false; + +void CreateDir(char *dir) +{ + char dirPath[PATH_MAX]; + + for(int i = 0; i < PATH_MAX; i++) + { + if(*(dir + i) == '/' && access(dirPath, F_OK) == -1) + { + mkdir(dirPath, 0777); + } + dirPath[i] = *(dir + i); + } +} + +void InitLog() +{ + if(access(LOG_DIR, F_OK) == -1) + CreateDir(LOG_DIR); + + if(access(LOG_FILE, F_OK) != -1) + remove(LOG_FILE); +} + +void WriteLog(char *buffer) +{ + FILE *log = fopen(LOG_FILE, "a"); + if(log != NULL) + { + fprintf(log, "%s\n", buffer); + } + fclose(log); +} + +void WriteConfigFile(TemperaturePoint *table) +{ + if(table == NULL) + { + table = malloc(sizeof(defaultTable)); + memcpy(table, defaultTable, sizeof(defaultTable)); + } + + if(access(CONFIG_DIR, F_OK) == -1) + CreateDir(CONFIG_DIR); + + FILE *config = fopen(CONFIG_FILE, "w"); + fwrite(table, TABLE_SIZE, 1, config); + fclose(config); +} + + + +void ReadConfigFile(TemperaturePoint **table_out) +{ + InitLog(); + + *table_out = malloc(sizeof(defaultTable)); + memcpy(*table_out, defaultTable, sizeof(defaultTable)); + + if(access(CONFIG_DIR, F_OK) == -1) + { + CreateDir(CONFIG_DIR); + WriteConfigFile(NULL); + } + else + { + if(access(CONFIG_FILE, F_OK) == -1) + { + WriteConfigFile(NULL); + } + else + { + FILE *config = fopen(CONFIG_FILE, "r"); + fread(*table_out, TABLE_SIZE, 1, config); + fclose(config); + } + } +} + +bool IsSystemAwake() +{ + // Check if system is in sleep mode by checking applet state + // appletGetOperationMode returns the current operation mode + AppletOperationMode opMode = appletGetOperationMode(); + + // If in handheld or console mode, system is awake + // If in any other mode (like sleep), we consider it asleep + return (opMode == AppletOperationMode_Handheld || opMode == AppletOperationMode_Console); +} + +void InitFanController(TemperaturePoint *table) +{ + fanControllerTable = table; + + if(R_FAILED(threadCreate(&FanControllerThread, FanControllerThreadFunction, NULL, NULL, 0x4000, 0x3F, -2))) + { + WriteLog("Error creating FanControllerThread"); + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + } +} + +void FanControllerThreadFunction(void*) +{ + FanController fc; + float fanLevelSet_f = 0; + float temperatureC_f = 0; + u64 awakeSleepTime = 250000000ULL; // 0.25 second when awake (250ms - responsive) + u64 sleepSleepTime = 5000000000ULL; // 5 seconds when in sleep + int sleepCheckCounter = 0; + + Result rs = fanOpenController(&fc, 0x3D000001); + if(R_FAILED(rs)) + { + WriteLog("Error opening fanController"); + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + } + + while(!fanControllerThreadExit) + { + // Check if system is awake every 20 iterations (~5 seconds) to reduce overhead + sleepCheckCounter++; + if(sleepCheckCounter >= 20) + { + bool isAwake = IsSystemAwake(); + sleepCheckCounter = 0; + + // If system is asleep, use longer sleep interval + if(!isAwake) + { + svcSleepThread(sleepSleepTime); + continue; + } + } + + rs = Tmp451GetSocTemp(&temperatureC_f); + if(R_FAILED(rs)) + { + WriteLog("tsSessionGetTemperature error"); + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + } + + if(temperatureC_f >= 0 && temperatureC_f <= fanControllerTable->temperature_c) + { + float m = 0; + float q = 0; + + m = fanControllerTable->fanLevel_f / fanControllerTable->temperature_c; + q = 0 - m; + + fanLevelSet_f = (m * temperatureC_f) + q; + + }else if(temperatureC_f >= (fanControllerTable + 9)->temperature_c) + { + fanLevelSet_f = (fanControllerTable + 9)->fanLevel_f; + }else + { + for(int i = 0; i < (TABLE_SIZE/sizeof(TemperaturePoint)) - 1; i++) + { + if(temperatureC_f >= (fanControllerTable + i)->temperature_c && temperatureC_f <= (fanControllerTable + i + 1)->temperature_c) + { + float m = 0; + float q = 0; + + m = ((fanControllerTable + i + 1)->fanLevel_f - (fanControllerTable + i)->fanLevel_f ) / ((fanControllerTable + i + 1)->temperature_c - (fanControllerTable + i)->temperature_c); + q = (fanControllerTable + i)->fanLevel_f - (m * (fanControllerTable + i)->temperature_c); + + fanLevelSet_f = (m * temperatureC_f) + q; + break; + } + } + } + + // Always update fan speed for immediate response + rs = fanControllerSetRotationSpeedLevel(&fc, fanLevelSet_f); + if(R_FAILED(rs)) + { + WriteLog("fanControllerSetRotationSpeedLevel error"); + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + } + + // Use responsive sleep time when awake (250ms) + svcSleepThread(awakeSleepTime); + } + + fanControllerClose(&fc); +} + +void StartFanControllerThread() +{ + if(R_FAILED(threadStart(&FanControllerThread))) + { + WriteLog("Error starting FanControllerThread"); + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + } +} + +void CloseFanControllerThread() +{ + Result rs; + fanControllerThreadExit = true; + rs = threadWaitForExit(&FanControllerThread); + if(R_FAILED(rs)) + { + WriteLog("Error waiting fanControllerThread"); + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + } + threadClose(&FanControllerThread); + fanControllerThreadExit = false; + free(fanControllerTable); +} + +void WaitFanController() +{ + if(R_FAILED(threadWaitForExit(&FanControllerThread))) + { + WriteLog("Error waiting fanControllerThread"); + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + } +} \ No newline at end of file diff --git a/Source/sys-clk/sysmodule/src/fancontrol.h b/Source/sys-clk/sysmodule/src/fancontrol.h new file mode 100644 index 00000000..f1f0c67e --- /dev/null +++ b/Source/sys-clk/sysmodule/src/fancontrol.h @@ -0,0 +1,41 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_DIR "./config/hoc-clk/" +#define LOG_FILE "./config/hoc-clk/fan_log.txt" +#define CONFIG_DIR "./config/hoc-clk/" +#define CONFIG_FILE "./config/hoc-clk/config.dat" +#define TABLE_SIZE sizeof(TemperaturePoint) * 10 + + +typedef struct +{ + int temperature_c; + float fanLevel_f; +} TemperaturePoint; + +void WriteConfigFile(TemperaturePoint *table); +void ReadConfigFile(TemperaturePoint **table_out); + +void InitFanController(TemperaturePoint *table); +void FanControllerThreadFunction(void*); +void StartFanControllerThread(); +void CloseFanControllerThread(); +void WaitFanController(); +void WriteLog(char *buffer); + +#ifdef __cplusplus +} +#endif diff --git a/Source/sys-clk/sysmodule/src/i2c.h b/Source/sys-clk/sysmodule/src/i2c.h new file mode 100644 index 00000000..653e7e1e --- /dev/null +++ b/Source/sys-clk/sysmodule/src/i2c.h @@ -0,0 +1,82 @@ +#ifndef I2C_H +#define I2C_H + +#include + +Result I2cReadRegHandler16(u8 reg, I2cDevice dev, u16 *out) +{ + struct readReg { + u8 send; + u8 sendLength; + u8 sendData; + u8 receive; + u8 receiveLength; + }; + + I2cSession _session; + + Result res = i2cOpenSession(&_session, dev); + if (res) + return res; + + u16 val; + + struct readReg readRegister = { + .send = 0 | (I2cTransactionOption_Start << 6), + .sendLength = sizeof(reg), + .sendData = reg, + .receive = 1 | (I2cTransactionOption_All << 6), + .receiveLength = sizeof(val), + }; + + res = i2csessionExecuteCommandList(&_session, &val, sizeof(val), &readRegister, sizeof(readRegister)); + if (res) + { + i2csessionClose(&_session); + return res; + } + + *out = val; + i2csessionClose(&_session); + return 0; +} + +Result I2cReadRegHandler8(u8 reg, I2cDevice dev, u8 *out) +{ + struct readReg { + u8 send; + u8 sendLength; + u8 sendData; + u8 receive; + u8 receiveLength; + }; + + I2cSession _session; + + Result res = i2cOpenSession(&_session, dev); + if (res) + return res; + + u8 val; + + struct readReg readRegister = { + .send = 0 | (I2cTransactionOption_Start << 6), + .sendLength = sizeof(reg), + .sendData = reg, + .receive = 1 | (I2cTransactionOption_All << 6), + .receiveLength = sizeof(val), + }; + + res = i2csessionExecuteCommandList(&_session, &val, sizeof(val), &readRegister, sizeof(readRegister)); + if (res) + { + i2csessionClose(&_session); + return res; + } + + *out = val; + i2csessionClose(&_session); + return 0; +} + +#endif \ No newline at end of file diff --git a/Source/sys-clk/sysmodule/src/integrations.h b/Source/sys-clk/sysmodule/src/integrations.h index 0222d216..19678641 100644 --- a/Source/sys-clk/sysmodule/src/integrations.h +++ b/Source/sys-clk/sysmodule/src/integrations.h @@ -9,6 +9,8 @@ #include "errors.h" #include "file_utils.h" +#include "clock_manager.h" + class ReverseNXSync { public: ReverseNXSync (); @@ -30,4 +32,4 @@ protected: ReverseNXMode GetToolModeFromPatch(const char* patch_path); ReverseNXMode RecheckToolMode(); -}; +}; \ No newline at end of file diff --git a/Source/sys-clk/sysmodule/src/main.cpp b/Source/sys-clk/sysmodule/src/main.cpp index 96a026a0..7c1c6ed5 100644 --- a/Source/sys-clk/sysmodule/src/main.cpp +++ b/Source/sys-clk/sysmodule/src/main.cpp @@ -20,7 +20,7 @@ #include "process_management.h" #include "clock_manager.h" #include "ipc_service.h" - +#include "fancontrol.h" #define INNER_HEAP_SIZE 0x30000 extern "C" @@ -63,12 +63,24 @@ extern "C" hosversionSet(MAKEHOSVERSION(fw.major, fw.minor, fw.micro)); setsysExit(); } + + rc = fanInitialize(); + if (R_FAILED(rc)) + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + + rc = i2cInitialize(); + if (R_FAILED(rc)) + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); } void __appExit(void) { - smExit(); - } + CloseFanControllerThread(); + fanExit(); + i2cExit(); + fsExit(); + fsdevUnmountAll(); + } } int main(int argc, char** argv) @@ -95,6 +107,10 @@ int main(int argc, char** argv) clockMgr->SetRunning(true); clockMgr->GetConfig()->SetEnabled(true); ipcSrv->SetRunning(true); + TemperaturePoint *table; + ReadConfigFile(&table); + InitFanController(table); + StartFanControllerThread(); while (clockMgr->Running()) { @@ -121,5 +137,6 @@ int main(int argc, char** argv) FileUtils::LogLine("Exit"); svcSleepThread(1000000ULL); FileUtils::Exit(); + return 0; } diff --git a/Source/sys-clk/sysmodule/src/tmp451.h b/Source/sys-clk/sysmodule/src/tmp451.h new file mode 100644 index 00000000..3d71aaee --- /dev/null +++ b/Source/sys-clk/sysmodule/src/tmp451.h @@ -0,0 +1,98 @@ +/* + * SOC/PCB Temperature driver for Nintendo Switch's TI TMP451 + * + * Copyright (c) 2018 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * Modified by: MasaGratoR + */ + +//#include +#include "i2c.h" + +//#define TMP451_I2C_ADDR 0x4C + +#define TMP451_PCB_TEMP_REG 0x00 +#define TMP451_SOC_TEMP_REG 0x01 + +/* +#define TMP451_CONFIG_REG 0x09 +#define TMP451_CNV_RATE_REG 0x0A +*/ + +#define TMP451_SOC_TEMP_DEC_REG 0x10 +#define TMP451_PCB_TEMP_DEC_REG 0x15 + +/* +#define TMP451_SOC_TMP_OFH_REG 0x11 +#define TMP451_SOC_TMP_OFL_REG 0x12 +*/ + +// If input is false, the return value is packed. MSByte is the integer in oC +// and the LSByte is the decimal point truncated to 2 decimal places. +// Otherwise it's an integer oC. +/* +u16 tmp451_get_soc_temp(bool integer); +u16 tmp451_get_pcb_temp(bool integer); +void tmp451_init(); +void tmp451_end(); +*/ + +Result Tmp451ReadReg(u8 reg, u8 *out) +{ + u8 data = 0; + Result res = I2cReadRegHandler8(reg, I2cDevice_Tmp451, &data); + + if (R_FAILED(res)) + { + return res; + } + + *out = data; + return res; +} + +Result Tmp451GetSocTemp(float* temperature) { + u8 integer = 0; + u8 decimals = 0; + + Result rc = Tmp451ReadReg(TMP451_SOC_TEMP_REG, &integer); + if (R_FAILED(rc)) + return rc; + rc = Tmp451ReadReg(TMP451_SOC_TEMP_DEC_REG, &decimals); + if (R_FAILED(rc)) + return rc; + + decimals = ((u16)(decimals >> 4) * 625) / 100; + *temperature = (float)(integer) + ((float)(decimals) / 100); + return rc; +} + +Result Tmp451GetPcbTemp(float* temperature) { + u8 integer = 0; + u8 decimals = 0; + + Result rc = Tmp451ReadReg(TMP451_PCB_TEMP_REG, &integer); + if (R_FAILED(rc)) + return rc; + rc = Tmp451ReadReg(TMP451_PCB_TEMP_DEC_REG, &decimals); + if (R_FAILED(rc)) + return rc; + + decimals = ((u16)(decimals >> 4) * 625) / 100; + *temperature = (float)(integer) + ((float)(decimals) / 100); + return rc; +}