/* * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors * * 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 . * */ /* -------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * , , * wrote this file. As long as you retain this notice you can do whatever you * want with this stuff. If you meet any of us some day, and you think this * stuff is worth it, you can buy us a beer in return. - The sys-clk authors * -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include "board_misc.hpp" namespace board { Thread gpuThread; LEvent threadexit; Thread cpuCore0Thread; Thread cpuCore1Thread; Thread cpuCore2Thread; u32 gpuLoad; u32 _fd; u64 idletick0 = 0; u64 idletick1 = 0; u64 idletick2 = 0; constexpr u64 CpuTimeOutNs = 500'000'000; constexpr double Systemtickfrequency = 19200000.0 * (static_cast(CpuTimeOutNs) / 1'000'000'000.0); void GpuLoadThread(void *nvCheckPtr) { Result nvCheck = *static_cast(nvCheckPtr); constexpr u32 GpuSamples = 8; u32 gpu_load_array[GpuSamples] = {}; size_t i = 0; constexpr u32 NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD = 0x80044715; if (R_SUCCEEDED(nvCheck)) do { u32 temp; if (R_SUCCEEDED(nvIoctl(_fd, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &temp))) { gpu_load_array[i++ % gpu_samples_average] = temp; gpuLoad = std::accumulate(&gpu_load_array[0], &gpu_load_array[gpu_samples_average], 0) / gpu_samples_average; } svcSleepThread(16'666'000); // wait a bit (this is the perfect amount of time to keep the reading accurate) } while(true); } void CheckCore(void *idletickPtr) { u64* idletick = static_cast(idletickPtr); while(true) { u64 idletickA; u64 idletickB; svcGetInfo(&idletickB, InfoType_IdleTickCount, INVALID_HANDLE, -1); svcWaitForAddress(&threadexit, ArbitrationType_WaitIfEqual, 0, CpuTimeOutNs); svcGetInfo(&idletickA, InfoType_IdleTickCount, INVALID_HANDLE, -1); *idletick = idletickA - idletickB; } } void StartLoad() { _fd = fd; threadCreate(&gpuThread, GpuLoadThread, &nvCheck, NULL, 0x1000, 0x3F, -2); threadStart(&gpuThread) leventClear(&threadexit); threadCreate(&cpuCore0Thread, CheckCore, &idletick0, NULL, 0x1000, 0x10, 0); threadCreate(&cpuCore1Thread, CheckCore, &idletick1, NULL, 0x1000, 0x10, 1); threadCreate(&cpuCore2Thread, CheckCore, &idletick2, NULL, 0x1000, 0x10, 2); threadStart(&cpuCore0Thread); threadStart(&cpuCore1Thread); threadStart(&cpuCore2Thread); } u32 GetMaxCpuLoad() { float cpuUsage0 = std::clamp(((Systemtickfrequency - idletick0) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); float cpuUsage1 = std::clamp(((Systemtickfrequency - idletick1) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); float cpuUsage2 = std::clamp(((Systemtickfrequency - idletick2) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); return std::round(std::max({cpuUsage0, cpuUsage1, cpuUsage2})); } u32 GetPartLoad(SysClkPartLoad loadSource) { switch(loadSource) { case SysClkPartLoad_EMC: return t210EmcLoadAll(); case SysClkPartLoad_EMCCpu: return t210EmcLoadCpu(); case HocClkPartLoad_GPU: return gpuLoad; case HocClkPartLoad_CPUMax: return GetMaxCpuLoad(); case HocClkPartLoad_BAT: BatteryChargeInfo info; batteryInfoGetChargeInfo(&info); return info.RawBatteryCharge; case HocClkPartLoad_FAN: return GetFanLevel(); default: ASSERT_ENUM_VALID(SysClkPartLoad, loadSource); } return 0; } void ExitLoad() { threadClose(&gpuThread); threadClose(&cpuCore0Thread); threadClose(&cpuCore1Thread); threadClose(&cpuCore2Thread); } }