real freqs + ram load on bpmp

gpu freq is excluded due to smmu issue (carveout?)
This commit is contained in:
souldbminersmwc committed 2026-08-26 11:56:56 -04:00
1 parent 8222fe6b0c
commit d8c6a86bb0
20 files changed
+411 -368

No files matched your search

+3 -2
View File
@@ -28,7 +28,8 @@ CXXFLAGS := $(ARCH) -O2 -g -ffreestanding -nostdlib -fno-exceptions -fno-rtti \
-fomit-frame-pointer -std=gnu++20 -Wall -Wextra $(CUSTOMDEFINES) -fomit-frame-pointer -std=gnu++20 -Wall -Wextra $(CUSTOMDEFINES)
ASFLAGS := $(ARCH) -g ASFLAGS := $(ARCH) -g
LDFLAGS := $(ARCH) -nostartfiles -nodefaultlibs -Wl,--nmagic,--gc-sections \ LDFLAGS := $(ARCH) -nostartfiles -nodefaultlibs -Wl,--nmagic,--gc-sections \
-T $(SOURCEDIR)/../bpmpfw.ld -lgcc -T $(SOURCEDIR)/../bpmpfw.ld
LDLIBS := -lgcc
BIN_TARGET := $(OUTPUTDIR)/$(TARGET).bin BIN_TARGET := $(OUTPUTDIR)/$(TARGET).bin
@@ -41,7 +42,7 @@ $(BIN_TARGET): $(BUILDDIR)/$(TARGET).elf
$(OBJCOPY) -S -O binary $< $@ $(OBJCOPY) -S -O binary $< $@
$(BUILDDIR)/$(TARGET).elf: $(OBJS) $(BUILDDIR)/$(TARGET).elf: $(OBJS)
@$(CXX) $(LDFLAGS) $(OBJS) -o $@ @$(CXX) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
$(BUILDDIR)/%.o: $(SOURCEDIR)/%.cpp | $(BUILDDIR) $(BUILDDIR)/%.o: $(SOURCEDIR)/%.cpp | $(BUILDDIR)
@$(CXX) $(CXXFLAGS) $(INCLUDE) -c $< -o $@ @$(CXX) $(CXXFLAGS) $(INCLUDE) -c $< -o $@
Binary file not shown.
+99
View File
@@ -0,0 +1,99 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "freq.hpp"
#include "regs.hpp"
#include <hocclk/bpmp.h>
namespace actmon {
constexpr u32 ActmonBase = 0x6000C800;
constexpr u32 ActmonDevBase = ActmonBase + 0x80;
constexpr u32 ActmonDevSize = 0x40;
constexpr u32 ActmonGlbStatus = 0x0;
constexpr u32 ActmonGlbPeriodCtrl = 0x4;
constexpr u32 ActmonMccpuMonAct = (1u << 8);
constexpr u32 ActmonMcallMonAct = (1u << 9);
constexpr u32 ActmonDevCtrlKVal3 = (3u << 10);
constexpr u32 ActmonDevCtrlEnbPeriodic = (1u << 18);
constexpr u32 ActmonDevCtrlEnb = (1u << 31);
constexpr u32 ActmonPeriodMs = 20;
enum ActmonDev : u32 {
DevCpu = 0,
DevBpmp,
DevAhb,
DevApb,
DevCpuFreq,
DevMcAll,
DevMcCpu,
};
constexpr u32 DevCtrl = 0x00;
constexpr u32 DevInitAvg = 0x0C;
constexpr u32 DevCountWeight = 0x18;
constexpr u32 DevAvgCount = 0x20;
inline u32 DevReg(ActmonDev dev, u32 off) {
return ActmonDevBase + (static_cast<u32>(dev) * ActmonDevSize) + off;
}
inline void EnableDev(ActmonDev dev, u32 freqKhz, u32 weight) {
MMIO32(DevReg(dev, DevInitAvg)) = freqKhz * ActmonPeriodMs / 2;
MMIO32(DevReg(dev, DevCountWeight)) = weight;
MMIO32(DevReg(dev, DevCtrl)) = ActmonDevCtrlEnb | ActmonDevCtrlEnbPeriodic | ActmonDevCtrlKVal3;
}
inline void Init() {
const u32 emcFreqKhz = static_cast<u32>(freq::MeasurePtoFreqHz(freq::ClkPtoEmc, 1) / 1000);
if (!(MMIO32(ActmonBase + ActmonGlbStatus) & ActmonMcallMonAct)) {
MMIO32(ActmonBase + ActmonGlbPeriodCtrl) = (ActmonPeriodMs - 1) & 0xFF;
EnableDev(DevMcAll, emcFreqKhz, 256 * 4);
}
if (!(MMIO32(ActmonBase + ActmonGlbStatus) & ActmonMccpuMonAct)) {
EnableDev(DevMcCpu, emcFreqKhz, 256 * 4);
}
}
inline void Update(HocClkBpmpSharedInfo &info) {
const u32 emcFreqKhz = info.freqMem;
if (emcFreqKhz == 0) {
return;
}
const u32 avgAll = MMIO32(DevReg(DevMcAll, DevAvgCount));
const u32 avgCpu = MMIO32(DevReg(DevMcCpu, DevAvgCount));
// Get 1000 -> 100.0.
info.emcLoadAll = static_cast<u32>((static_cast<u64>(avgAll) * 10 * 100) / (emcFreqKhz * ActmonPeriodMs));
info.emcLoadCpu = static_cast<u32>((static_cast<u64>(avgCpu) * 10 * 100) / (emcFreqKhz * ActmonPeriodMs));
info.emcBwAll = static_cast<u32>((static_cast<u64>(emcFreqKhz) * 16 * info.emcLoadAll) / 1000000);
info.emcBwCpu = static_cast<u32>((static_cast<u64>(emcFreqKhz) * 16 * info.emcLoadCpu) / 1000000);
// Not 100% accurate but should be enough.
info.emcBwGpu = (info.emcBwAll > info.emcBwCpu) ? (info.emcBwAll - info.emcBwCpu) : 0;
}
} // namespace actmon
+152
View File
@@ -0,0 +1,152 @@
/*
* Copyright (c) 2019-2025 CTCaer
* 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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "regs.hpp"
// NOTE: MMU won't allow access to gpu address space (smmu carveout issue?)
namespace bpmpMmu {
constexpr u32 CacheBase = 0x50040000;
constexpr u32 CacheConfig = 0x000;
constexpr u32 CfgEnableCache = (1u << 0);
constexpr u32 CfgForceWriteThrough = (1u << 3);
constexpr u32 CfgMmuTagModeShift = 8;
constexpr u32 TagModeParallel = 0;
constexpr u32 CfgTagChkAbrtOnErr = (1u << 14);
constexpr u32 MaintReq = 0x028;
constexpr u32 MaintReqWayBitmapAll = (0xFu << 8);
constexpr u32 IntClear = 0x044;
constexpr u32 IntRawEvent = 0x048;
constexpr u32 IntMaintDone = (1u << 0);
constexpr u32 MmuFallbackEntry = 0x0A0;
constexpr u32 MmuShadowCopyMask = 0x0A4;
constexpr u32 MmuCfg = 0x0AC;
constexpr u32 MmuCfgSeqEn = (1u << 1);
constexpr u32 MmuCfgTlbEn = (1u << 2);
constexpr u32 MmuCfgAbortStoreLast = (1u << 4);
constexpr u32 MmuCmd = 0x0B0;
constexpr u32 MmuCmdInit = 1;
constexpr u32 MmuCmdCopyShadow = 2;
constexpr u32 ShadowEntryBase = CacheBase + 0x400;
constexpr u32 MmuEnCached = (1u << 0);
constexpr u32 MmuEnExec = (1u << 1);
constexpr u32 MmuEnRead = (1u << 2);
constexpr u32 MmuEnWrite = (1u << 3);
constexpr u32 MaintCleanInvalidWay = 19;
constexpr u32 MaintInvalidWay = 18;
constexpr u32 CacheLineSize = 0x20;
constexpr u32 DramStart = 0x80000000;
constexpr u32 IramBase = 0x40000000;
constexpr u32 IramEnd = 0x4003FFFF;
struct MmuEntry {
u32 startAddr;
u32 endAddr;
u32 attr;
};
constexpr MmuEntry Entries[] = {
{ DramStart, 0xFFFFFFFF, MmuEnRead | MmuEnWrite | MmuEnExec | MmuEnCached },
{ IramBase, IramEnd, MmuEnRead | MmuEnWrite | MmuEnExec | MmuEnCached },
};
inline u32 AlignUp(u32 x, u32 a) {
return (x + a - 1) & ~(a - 1);
}
inline u32 AlignDown(u32 x, u32 a) {
return x & ~(a - 1);
}
inline void Maintenance(u32 op, bool force) {
if (!force && !(MMIO32(CacheBase + CacheConfig) & CfgEnableCache)) {
return;
}
MMIO32(CacheBase + IntClear) = IntMaintDone;
// Blocking operation.
MMIO32(CacheBase + MaintReq) = MaintReqWayBitmapAll | op;
while (!(MMIO32(CacheBase + IntRawEvent) & IntMaintDone))
;
MMIO32(CacheBase + IntClear) = MMIO32(CacheBase + IntRawEvent);
}
inline void SetEntry(u32 idx, const MmuEntry &entry) {
MMIO32(ShadowEntryBase + sizeof(u32) * 4 * idx + 0x0) = AlignUp(entry.startAddr, CacheLineSize);
MMIO32(ShadowEntryBase + sizeof(u32) * 4 * idx + 0x4) = AlignDown(entry.endAddr, CacheLineSize);
MMIO32(ShadowEntryBase + sizeof(u32) * 4 * idx + 0x8) = entry.attr;
MMIO32(CacheBase + MmuShadowCopyMask) |= (1u << idx);
}
inline void Enable() {
if (MMIO32(CacheBase + CacheConfig) & CfgEnableCache) {
return;
}
// Init BPMP MMU.
MMIO32(CacheBase + MmuCmd) = MmuCmdInit;
MMIO32(CacheBase + MmuFallbackEntry) = MmuEnRead | MmuEnWrite | MmuEnExec; // RWX for non-defined regions.
MMIO32(CacheBase + MmuCfg) = MmuCfgSeqEn | MmuCfgTlbEn | MmuCfgAbortStoreLast;
// Init BPMP MMU entries.
MMIO32(CacheBase + MmuShadowCopyMask) = 0;
for (u32 idx = 0; idx < sizeof(Entries) / sizeof(Entries[0]); idx++) {
SetEntry(idx, Entries[idx]);
}
MMIO32(CacheBase + MmuCmd) = MmuCmdCopyShadow;
// Invalidate cache.
Maintenance(MaintInvalidWay, true);
// Enable cache.
MMIO32(CacheBase + CacheConfig) = CfgEnableCache | CfgForceWriteThrough |
(TagModeParallel << CfgMmuTagModeShift) | CfgTagChkAbrtOnErr;
// HW bug. Invalidate cache again.
Maintenance(MaintInvalidWay, false);
}
inline void Disable() {
if (!(MMIO32(CacheBase + CacheConfig) & CfgEnableCache)) {
return;
}
// Clean and invalidate cache.
Maintenance(MaintCleanInvalidWay, false);
// Disable cache.
MMIO32(CacheBase + CacheConfig) = 0;
}
} // namespace bpmpMmu
+79
View File
@@ -0,0 +1,79 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "regs.hpp"
#include <hocclk/bpmp.h>
namespace freq {
constexpr u32 CarBase = 0x60006000;
constexpr u32 CarPtoClkCntCntl = 0x60;
constexpr u32 CarPtoClkCntStatus = 0x64;
constexpr u32 PtoCntEn = (1u << 9);
constexpr u32 PtoCntRst = (1u << 10);
constexpr u32 PtoClkEnable = (1u << 13);
constexpr u32 PtoSrcSelShift = 14;
constexpr u32 PtoDivSelDiv1 = (1u << 23);
constexpr u32 PtoClkCntBusy = (1u << 31);
constexpr u32 PtoClkCntMask = 0xFFFFFF;
constexpr u32 ClkPtoCclkGDiv2 = 0x13;
constexpr u32 ClkPtoEmc = 0x24;
inline u64 MeasurePtoFreqHz(u32 srcId, u32 multiplier) {
constexpr u32 win = 16;
constexpr u32 osc = 32768;
const u32 val = (srcId << PtoSrcSelShift) | PtoDivSelDiv1 | PtoClkEnable | (win - 1);
MMIO32(CarBase + CarPtoClkCntCntl) = val;
(void)MMIO32(CarBase + CarPtoClkCntCntl);
usleep(2);
MMIO32(CarBase + CarPtoClkCntCntl) = val | PtoCntRst;
(void)MMIO32(CarBase + CarPtoClkCntCntl);
usleep(2);
MMIO32(CarBase + CarPtoClkCntCntl) = val;
(void)MMIO32(CarBase + CarPtoClkCntCntl);
usleep(2);
MMIO32(CarBase + CarPtoClkCntCntl) = val | PtoCntEn;
(void)MMIO32(CarBase + CarPtoClkCntCntl);
usleep((1000000u * win / osc) + 12 + 2); // 502us
while (MMIO32(CarBase + CarPtoClkCntStatus) & PtoClkCntBusy)
;
const u32 cnt = MMIO32(CarBase + CarPtoClkCntStatus) & PtoClkCntMask;
MMIO32(CarBase + CarPtoClkCntCntl) = 0;
(void)MMIO32(CarBase + CarPtoClkCntCntl);
usleep(2);
return (static_cast<u64>(cnt) * multiplier * osc) / win;
}
inline void Update(HocClkBpmpSharedInfo &info) {
info.freqMem = static_cast<u32>(MeasurePtoFreqHz(ClkPtoEmc, 1) / 1000);
info.freqCpu = static_cast<u32>(MeasurePtoFreqHz(ClkPtoCclkGDiv2, 2) / 1000);
}
} // namespace freq
+23 -1
View File
@@ -18,6 +18,9 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "actmon.hpp"
#include "bpmp_mmu.hpp"
#include "freq.hpp"
#include "libc_platform.hpp" #include "libc_platform.hpp"
#include "regs.hpp" #include "regs.hpp"
#include "sensors.hpp" #include "sensors.hpp"
@@ -34,6 +37,10 @@ namespace {
void MainLoop(HocClkBpmpSharedInfo &info) { void MainLoop(HocClkBpmpSharedInfo &info) {
for (;;) { for (;;) {
if (info.exitRequested) {
break;
}
const u32 temp1 = MMIO32(SocthermBase + SocthermSensorTemp1); const u32 temp1 = MMIO32(SocthermBase + SocthermSensorTemp1);
const u32 temp2 = MMIO32(SocthermBase + SocthermSensorTemp2); const u32 temp2 = MMIO32(SocthermBase + SocthermSensorTemp2);
@@ -42,11 +49,23 @@ void MainLoop(HocClkBpmpSharedInfo &info) {
info.tempMem = TranslateSocthermTemp(static_cast<u16>(temp2 >> 16)); info.tempMem = TranslateSocthermTemp(static_cast<u16>(temp2 >> 16));
info.tempPllx = TranslateSocthermTemp(static_cast<u16>(temp2)); info.tempPllx = TranslateSocthermTemp(static_cast<u16>(temp2));
msleep(500); freq::Update(info);
actmon::Update(info);
msleep(250);
} }
/* Deinit MMU otherwise sleep will fatal. */
bpmpMmu::Disable();
info.magic = 0x0;
for (;;)
;
} }
extern "C" void main() { extern "C" void main() {
bpmpMmu::Enable();
InitializeLibc(); InitializeLibc();
printf("[hoc-bpmpfw]: Starting bpmpfw\n"); printf("[hoc-bpmpfw]: Starting bpmpfw\n");
@@ -54,6 +73,9 @@ extern "C" void main() {
HocClkBpmpSharedInfo &info = SharedInfo(); HocClkBpmpSharedInfo &info = SharedInfo();
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
info.status = 0; info.status = 0;
actmon::Init();
info.magic = HOCCLK_BPMP_MAGIC; // written last info.magic = HOCCLK_BPMP_MAGIC; // written last
/* If the main loop exits then it is a panic or invalid state */ /* If the main loop exits then it is a panic or invalid state */
+10 -2
View File
@@ -33,7 +33,15 @@ typedef struct {
s32 tempGpu; s32 tempGpu;
s32 tempMem; s32 tempMem;
s32 tempPllx; s32 tempPllx;
u32 reserved[2]; u32 freqCpu; // kHz
u32 freqMem; // kHz
u32 emcLoadAll;
u32 emcLoadCpu;
u32 emcBwAll;
u32 emcBwCpu;
u32 emcBwGpu;
u32 exitRequested; // Request BPMP-FW to shutdown for sleep mode
u32 reserved[1];
} HocClkBpmpSharedInfo; } HocClkBpmpSharedInfo;
static_assert(sizeof(HocClkBpmpSharedInfo) == 0x20); static_assert(sizeof(HocClkBpmpSharedInfo) == 0x3C);
@@ -89,7 +89,7 @@ typedef struct {
HocClkBpmpSharedInfo bpmpInfo; HocClkBpmpSharedInfo bpmpInfo;
// Reserved for future use // Reserved for future use
u8 reserved[0x358]; u8 reserved[0x33C];
} HocClkContext; } HocClkContext;
typedef struct typedef struct
@@ -32,7 +32,6 @@
#include <pwm.h> #include <pwm.h>
#include <registers.h> #include <registers.h>
#include <switch.h> #include <switch.h>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include "../bpmp/bpmp.hpp" #include "../bpmp/bpmp.hpp"
@@ -28,9 +28,9 @@
#include <i2c.h> #include <i2c.h>
#include <max17050.h> #include <max17050.h>
#include <switch.h> #include <switch.h>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include "../bpmp/bpmp.hpp"
#include "../display/display_refresh_rate.hpp" #include "../display/display_refresh_rate.hpp"
#include "../file/config.hpp" #include "../file/config.hpp"
#include "../file/errors.hpp" #include "../file/errors.hpp"
@@ -179,13 +179,13 @@ namespace board {
u32 hz = 0; u32 hz = 0;
switch (module) { switch (module) {
case HocClkModule_CPU: case HocClkModule_CPU:
return t210ClkCpuFreq(); return bpmp::GetSharedInfo()->freqCpu * 1000;
case HocClkModule_GPU: case HocClkModule_GPU:
return t210ClkGpuFreq(); return bpmp::GetSharedInfo()->freqGpu * 1000;
case HocClkModule_MEM: case HocClkModule_MEM:
return config::GetConfigValue(HocClkConfigValue_MemoryFrequencyMeasurementMode) == MemoryFrequencyMeasurementMode_PLL return config::GetConfigValue(HocClkConfigValue_MemoryFrequencyMeasurementMode) == MemoryFrequencyMeasurementMode_PLL
? pllmb::getRamClockRatePLLMB() ? pllmb::getRamClockRatePLLMB()
: t210ClkMemFreq(); : bpmp::GetSharedInfo()->freqMem * 1000;
case HocClkModule_Display: case HocClkModule_Display:
return GetDisplayRate(hz); return GetDisplayRate(hz);
default: default:
@@ -29,7 +29,6 @@
#include <i2c.h> #include <i2c.h>
#include <max17050.h> #include <max17050.h>
#include <switch.h> #include <switch.h>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include "../file/errors.hpp" #include "../file/errors.hpp"
@@ -33,9 +33,9 @@
#include <minIni.h> #include <minIni.h>
#include <numeric> #include <numeric>
#include <switch.h> #include <switch.h>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include "../bpmp/bpmp.hpp"
#include "../hos/apm_ext.h" #include "../hos/apm_ext.h"
#include "board.hpp" #include "board.hpp"
#include "board_misc.hpp" #include "board_misc.hpp"
@@ -116,9 +116,9 @@ namespace board {
u32 GetPartLoad(HocClkPartLoad loadSource) { u32 GetPartLoad(HocClkPartLoad loadSource) {
switch (loadSource) { switch (loadSource) {
case HocClkPartLoad_EMC: case HocClkPartLoad_EMC:
return t210EmcLoadAll(); return bpmp::GetSharedInfo()->emcLoadAll;
case HocClkPartLoad_EMCCpu: case HocClkPartLoad_EMCCpu:
return t210EmcLoadCpu(); return bpmp::GetSharedInfo()->emcLoadCpu;
case HocClkPartLoad_GPU: case HocClkPartLoad_GPU:
return gpuLoad; return gpuLoad;
case HocClkPartLoad_CPUMax: case HocClkPartLoad_CPUMax:
@@ -130,13 +130,13 @@ namespace board {
case HocClkPartLoad_FAN: case HocClkPartLoad_FAN:
return GetFanLevel(); return GetFanLevel();
case HocClkPartLoad_RamBWAll: case HocClkPartLoad_RamBWAll:
return t210EmcBwAll(); return bpmp::GetSharedInfo()->emcBwAll;
case HocClkPartLoad_RamBWCpu: case HocClkPartLoad_RamBWCpu:
return t210EmcBwCpu(); return bpmp::GetSharedInfo()->emcBwCpu;
case HocClkPartLoad_RamBWGpu: case HocClkPartLoad_RamBWGpu:
return t210EmcBwGpu(); return bpmp::GetSharedInfo()->emcBwGpu;
case HocClkPartLoad_RamBWPeak: case HocClkPartLoad_RamBWPeak:
return t210EmcBwPeak(); return static_cast<u32>((static_cast<u64>(bpmp::GetSharedInfo()->freqMem) * 16) / 1000);
default: default:
ASSERT_ENUM_VALID(HocClkPartLoad, loadSource); ASSERT_ENUM_VALID(HocClkPartLoad, loadSource);
} }
@@ -28,7 +28,6 @@
#include <i2c.h> #include <i2c.h>
#include <max17050.h> #include <max17050.h>
#include <switch.h> #include <switch.h>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include "../hos/apm_ext.h" #include "../hos/apm_ext.h"
@@ -31,7 +31,6 @@
#include <max17050.h> #include <max17050.h>
#include <pwm.h> #include <pwm.h>
#include <switch.h> #include <switch.h>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include "../file/config.hpp" #include "../file/config.hpp"
@@ -16,6 +16,7 @@
*/ */
#include <atomic> #include <atomic>
#include <cstddef>
#include <cstring> #include <cstring>
#include <switch.h> #include <switch.h>
@@ -91,7 +92,39 @@ namespace bpmp {
std::atomic_bool s_isAwake = true; std::atomic_bool s_isAwake = true;
/* Must be called to prevent secmon/erpt panic on wake */
void RequestBpmpExitAndWait() {
if (!IsPatchedExosphere()) {
return;
}
const u32 one = 1;
Result rc = SmcCopyToIram(WorkRamPhysBase + offsetof(HocClkBpmpSharedInfo, exitRequested), &one, sizeof(one));
if (R_FAILED(rc)) {
fileUtils::LogLine("[bpmp] RequestBpmpExitAndWait: SmcCopyToIram failed: 0x%x", rc);
return;
}
constexpr int MaxAttempts = 20;
constexpr u64 PollIntervalNs = 20'000'000; // 20ms
HocClkBpmpSharedInfo info = {};
for (int i = 0; i < MaxAttempts; i++) {
svcSleepThread(PollIntervalNs);
rc = SmcCopyFromIram(&info, WorkRamPhysBase, sizeof(info));
if (R_SUCCEEDED(rc) && info.magic != HOCCLK_BPMP_MAGIC) {
return;
}
}
fileUtils::LogLine("[bpmp] RequestBpmpExitAndWait: BPMP timeout (cannot sleep)");
}
void RestoreStockState() { void RestoreStockState() {
RequestBpmpExitAndWait();
SmcReadWriteRegister(EvpPhysBase + EvpCopResetVector, 0xFFFFFFFF, StockBpmpExceptionVector); SmcReadWriteRegister(EvpPhysBase + EvpCopResetVector, 0xFFFFFFFF, StockBpmpExceptionVector);
SmcReadWriteRegister(EvpPhysBase + EvpCopUndefVector, 0xFFFFFFFF, StockBpmpExceptionVector); SmcReadWriteRegister(EvpPhysBase + EvpCopUndefVector, 0xFFFFFFFF, StockBpmpExceptionVector);
SmcReadWriteRegister(EvpPhysBase + EvpCopSwiVector, 0xFFFFFFFF, StockBpmpExceptionVector); SmcReadWriteRegister(EvpPhysBase + EvpCopSwiVector, 0xFFFFFFFF, StockBpmpExceptionVector);
@@ -34,7 +34,6 @@
#include <minIni.h> #include <minIni.h>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include <unistd.h> #include <unistd.h>
@@ -26,7 +26,6 @@
#include <i2c.h> #include <i2c.h>
#include <max17050.h> #include <max17050.h>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include "../hos/apm_ext.h" #include "../hos/apm_ext.h"
@@ -28,7 +28,6 @@
#include <i2c.h> #include <i2c.h>
#include <max17050.h> #include <max17050.h>
#include <switch.h> #include <switch.h>
#include <t210.h>
#include <tmp451.h> #include <tmp451.h>
#include "../file/config.hpp" #include "../file/config.hpp"
-297
View File
@@ -1,297 +0,0 @@
/*
* Copyright (c) 2020-2023 CTCaer
* Copyright (c) 2023 p-sam
* Copyright (c) 2023 Lineon
* Copyright (c) 2026 Souldbminer
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "t210.h"
#define WAIT_NS 1000000000UL
#define usleep(x) svcSleepThread(1000UL * x)
#define GPU_TRIM_SYS_GPCPLL_COEFF 0x4
#define GPU_TRIM_SYS_GPCPLL(x) (*(volatile u32 *)(g_gpu_base + 0x137000ul + (x)))
#define CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL 0x60
#define CLK_RST_CONTROLLER_PTO_CLK_CNT_STATUS 0x64
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X 0x280
#define CLK_RST_CONTROLLER_RST_DEVICES_X 0x28C
/*! PTO_CLK_CNT */
#define PTO_REF_CLK_WIN_CFG_MASK 0xF
#define PTO_REF_CLK_WIN_CFG_16P 0xF
#define PTO_CNT_EN BIT(9)
#define PTO_CNT_RST BIT(10)
#define PTO_CLK_ENABLE BIT(13)
#define PTO_SRC_SEL_SHIFT 14
#define PTO_SRC_SEL_MASK 0x1FF
#define PTO_DIV_SEL_MASK (3 << 23)
#define PTO_DIV_SEL_GATED (0 << 23)
#define PTO_DIV_SEL_DIV1 (1 << 23)
#define PTO_DIV_SEL_DIV2_RISING (2 << 23)
#define PTO_DIV_SEL_DIV2_FALLING (3 << 23)
#define PTO_DIV_SEL_CPU_EARLY (0 << 23)
#define PTO_DIV_SEL_CPU_LATE (1 << 23)
#define PTO_CLK_CNT_BUSY BIT(31)
#define PTO_CLK_CNT 0xFFFFFF
#define CLK_PTO_CCLK_G_DIV2 0x13
#define CLK_PTO_EMC 0x24
#define CLOCK(x) (*(volatile u32 *)(g_clk_base + (x)))
/* Actmon Global registers */
#define ACTMON_GLB_STATUS 0x0
#define ACTMON_MCCPU_MON_ACT BIT(8)
#define ACTMON_MCALL_MON_ACT BIT(9)
#define ACTMON_CPU_FREQ_MON_ACT BIT(10)
#define ACTMON_BPMP_MON_ACT BIT(14)
#define ACTMON_CPU_MON_ACT BIT(15)
#define ACTMON_GLB_PERIOD_CTRL 0x4
#define ACTMON_GLB_PERIOD_USEC BIT(8)
#define ACTMON_GLB_PERIOD_SAMPLE(n) (((n) - 1) & 0xFF)
/* Actmon Device Registers */
#define ACTMON_DEV_SIZE 0x40
/* Actmon CTRL */
#define ACTMON_DEV_CTRL_K_VAL(k) (((k) & 7) << 10)
#define ACTMON_DEV_CTRL_ENB_PERIODIC BIT(18)
#define ACTMON_DEV_CTRL_ENB BIT(31)
#define ACTMON_PERIOD_MS 20
#define DEV_COUNT_WEIGHT 1024
#define ACTMON_BASE (g_act_base + 0x800)
#define ACTMON_DEV_BASE (ACTMON_BASE + 0x80)
#define ACTMON(x) (*(volatile u32 *)(ACTMON_BASE + (x)))
typedef enum _actmon_dev_t {
ACTMON_DEV_CPU,
ACTMON_DEV_BPMP,
ACTMON_DEV_AHB,
ACTMON_DEV_APB,
ACTMON_DEV_CPU_FREQ,
ACTMON_DEV_MC_ALL,
ACTMON_DEV_MC_CPU,
ACTMON_DEV_NUM,
} actmon_dev_t;
typedef struct _actmon_dev_reg_t {
vu32 ctrl;
vu32 upper_wnark;
vu32 lower_wmark;
vu32 init_avg;
vu32 avg_upper_wmark;
vu32 avg_lower_wmark;
vu32 count_weight;
vu32 count;
vu32 avg_count;
vu32 intr_status;
vu32 ctrl2;
vu32 rsvd[5];
} actmon_dev_reg_t;
static uintptr_t g_clk_base = 0;
static uintptr_t g_gpu_base = 0;
static uintptr_t g_act_base = 0;
static u64 g_update_ticks = 0;
static u32 g_cpu_freq = 0;
static u32 g_gpu_freq = 0;
static u32 g_mem_freq = 0;
static u32 g_emc_lall = 0;
static u32 g_emc_lcpu = 0;
static u32 g_emc_bw_all = 0;
static u32 g_emc_bw_cpu = 0;
static u32 g_emc_bw_gpu = 0;
static u32 _clock_get_dev_freq(u32 id, u32 multiplier) {
const u32 pto_win = 16;
const u32 pto_osc = 32768;
u32 val = ((id & PTO_SRC_SEL_MASK) << PTO_SRC_SEL_SHIFT) | PTO_DIV_SEL_DIV1 | PTO_CLK_ENABLE | (pto_win - 1);
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep(2);
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val | PTO_CNT_RST;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep(2);
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep(2);
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val | PTO_CNT_EN;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep((1000000ULL * pto_win / pto_osc) + 12 + 2); // 502 us.
while (CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_STATUS) & PTO_CLK_CNT_BUSY)
;
u32 cnt = CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_STATUS) & PTO_CLK_CNT;
CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = 0;
(void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL);
usleep(2);
u32 freq_khz = (u64)cnt * multiplier * pto_osc / pto_win;
return freq_khz;
}
static void _actmon_dev_enable(actmon_dev_t dev, u32 freq, u32 weight) {
actmon_dev_reg_t *regs = (actmon_dev_reg_t *)(ACTMON_DEV_BASE + (dev * ACTMON_DEV_SIZE));
regs->init_avg = (u32)freq * ACTMON_PERIOD_MS / 2;
regs->count_weight = weight;
regs->ctrl = ACTMON_DEV_CTRL_ENB | ACTMON_DEV_CTRL_ENB_PERIODIC | ACTMON_DEV_CTRL_K_VAL(3); // 8 samples average.
}
static u32 _actmon_dev_get_count_avg(actmon_dev_t dev) {
actmon_dev_reg_t *regs = (actmon_dev_reg_t *)(ACTMON_DEV_BASE + (dev * ACTMON_DEV_SIZE));
return regs->avg_count;
}
static inline Result _svcQueryMemoryMappingFallback(u64 *virtaddr, u64 physaddr, u64 size) {
if (hosversionAtLeast(10, 0, 0)) {
u64 out_size;
return svcQueryMemoryMapping(virtaddr, &out_size, physaddr, size);
} else {
return svcLegacyQueryIoMapping(virtaddr, physaddr, size);
}
}
static void _clock_update_freqs(void) {
u64 ticks = armGetSystemTick();
if (armTicksToNs(ticks - g_update_ticks) <= WAIT_NS) {
return;
}
g_update_ticks = ticks;
if (!g_clk_base) {
_svcQueryMemoryMappingFallback(&g_clk_base, 0x60006000ul, 0x1000);
}
if (!g_clk_base) {
return;
}
g_mem_freq = _clock_get_dev_freq(CLK_PTO_EMC, 1);
g_cpu_freq = _clock_get_dev_freq(CLK_PTO_CCLK_G_DIV2, 2);
if (!g_gpu_base) {
_svcQueryMemoryMappingFallback(&g_gpu_base, 0x57000000ul, 0x1000000);
}
if (!g_gpu_base) {
return;
}
bool gpu_enabled = (CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_X) & BIT(24)) && !(CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_X) & BIT(24));
if (!gpu_enabled) {
return;
}
if (!g_act_base) {
_svcQueryMemoryMappingFallback(&g_act_base, 0x6000C000ul, 0x1000);
}
if (!g_act_base) {
return;
}
const u32 osc = 38400000;
u32 coeff = GPU_TRIM_SYS_GPCPLL(GPU_TRIM_SYS_GPCPLL_COEFF);
u32 divm = coeff & 0xFF;
u32 divn = (coeff >> 8) & 0xFF;
u32 divp = (coeff >> 16) & 0x3F;
g_gpu_freq = osc * divn / (divm * divp) / 2;
u32 emc_freq = g_mem_freq / 1000;
// Check if actmon is disabled
if (!(ACTMON(ACTMON_GLB_STATUS) & ACTMON_MCALL_MON_ACT)) {
ACTMON(ACTMON_GLB_PERIOD_CTRL) = ACTMON_GLB_PERIOD_SAMPLE(ACTMON_PERIOD_MS);
_actmon_dev_enable(ACTMON_DEV_MC_ALL, emc_freq, 256 * 4);
}
// Check if actmon is disabled
if (!(ACTMON(ACTMON_GLB_STATUS) & ACTMON_MCCPU_MON_ACT))
_actmon_dev_enable(ACTMON_DEV_MC_CPU, emc_freq, 256 * 4);
// Get 1000 -> 100.0.
g_emc_lall = (u64)_actmon_dev_get_count_avg(ACTMON_DEV_MC_ALL) * 10 * 100 / (emc_freq * ACTMON_PERIOD_MS);
g_emc_lcpu = (u64)_actmon_dev_get_count_avg(ACTMON_DEV_MC_CPU) * 10 * 100 / (emc_freq * ACTMON_PERIOD_MS);
// TODO: use PLL instead of actmon
// Avoid floating point arithematic
g_emc_bw_all = (u64)emc_freq * 16 * g_emc_lall / 1000000;
g_emc_bw_cpu = (u64)emc_freq * 16 * g_emc_lcpu / 1000000;
// Not 100% accurate but should be enough
g_emc_bw_gpu = g_emc_bw_all - g_emc_bw_cpu;
}
u32 t210ClkCpuFreq(void) {
_clock_update_freqs();
return g_cpu_freq;
}
u32 t210ClkMemFreq(void) {
_clock_update_freqs();
return g_mem_freq;
}
u32 t210ClkGpuFreq(void) {
_clock_update_freqs();
return g_gpu_freq;
}
u32 t210EmcLoadAll() {
_clock_update_freqs();
return g_emc_lall;
}
u32 t210EmcLoadCpu() {
_clock_update_freqs();
return g_emc_lcpu;
}
u32 t210EmcBwAll() {
_clock_update_freqs();
return g_emc_bw_all;
}
u32 t210EmcBwCpu() {
_clock_update_freqs();
return g_emc_bw_cpu;
}
u32 t210EmcBwGpu() {
_clock_update_freqs();
return g_emc_bw_gpu;
}
u32 t210EmcBwPeak() {
_clock_update_freqs();
return ((u64)g_mem_freq * 16) / 1000000;
}
-47
View File
@@ -1,47 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* 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
* --------------------------------------------------------------------------
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <switch.h>
u32 t210ClkCpuFreq(void);
u32 t210ClkMemFreq(void);
u32 t210ClkGpuFreq(void);
u32 t210EmcLoadAll(void);
u32 t210EmcLoadCpu(void);
u32 t210EmcBwAll(void);
u32 t210EmcBwCpu(void);
u32 t210EmcBwGpu(void);
u32 t210EmcBwPeak(void);
#ifdef __cplusplus
}
#endif