diff --git a/Source/hoc-clk/bpmpfw/Makefile b/Source/hoc-clk/bpmpfw/Makefile
index 4cd0257a..ddfe3882 100644
--- a/Source/hoc-clk/bpmpfw/Makefile
+++ b/Source/hoc-clk/bpmpfw/Makefile
@@ -28,7 +28,8 @@ CXXFLAGS := $(ARCH) -O2 -g -ffreestanding -nostdlib -fno-exceptions -fno-rtti \
-fomit-frame-pointer -std=gnu++20 -Wall -Wextra $(CUSTOMDEFINES)
ASFLAGS := $(ARCH) -g
LDFLAGS := $(ARCH) -nostartfiles -nodefaultlibs -Wl,--nmagic,--gc-sections \
- -T $(SOURCEDIR)/../bpmpfw.ld -lgcc
+ -T $(SOURCEDIR)/../bpmpfw.ld
+LDLIBS := -lgcc
BIN_TARGET := $(OUTPUTDIR)/$(TARGET).bin
@@ -41,7 +42,7 @@ $(BIN_TARGET): $(BUILDDIR)/$(TARGET).elf
$(OBJCOPY) -S -O binary $< $@
$(BUILDDIR)/$(TARGET).elf: $(OBJS)
- @$(CXX) $(LDFLAGS) $(OBJS) -o $@
+ @$(CXX) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
$(BUILDDIR)/%.o: $(SOURCEDIR)/%.cpp | $(BUILDDIR)
@$(CXX) $(CXXFLAGS) $(INCLUDE) -c $< -o $@
diff --git a/Source/hoc-clk/bpmpfw/out/bpmpfw.bin b/Source/hoc-clk/bpmpfw/out/bpmpfw.bin
index 61766ba1..4681bab6 100644
Binary files a/Source/hoc-clk/bpmpfw/out/bpmpfw.bin and b/Source/hoc-clk/bpmpfw/out/bpmpfw.bin differ
diff --git a/Source/hoc-clk/bpmpfw/source/actmon.hpp b/Source/hoc-clk/bpmpfw/source/actmon.hpp
new file mode 100644
index 00000000..9a607c4e
--- /dev/null
+++ b/Source/hoc-clk/bpmpfw/source/actmon.hpp
@@ -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 .
+ *
+ */
+
+#pragma once
+#include "freq.hpp"
+#include "regs.hpp"
+
+#include
+
+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(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(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((static_cast(avgAll) * 10 * 100) / (emcFreqKhz * ActmonPeriodMs));
+ info.emcLoadCpu = static_cast((static_cast(avgCpu) * 10 * 100) / (emcFreqKhz * ActmonPeriodMs));
+
+ info.emcBwAll = static_cast((static_cast(emcFreqKhz) * 16 * info.emcLoadAll) / 1000000);
+ info.emcBwCpu = static_cast((static_cast(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
diff --git a/Source/hoc-clk/bpmpfw/source/bpmp_mmu.hpp b/Source/hoc-clk/bpmpfw/source/bpmp_mmu.hpp
new file mode 100644
index 00000000..ca106eb8
--- /dev/null
+++ b/Source/hoc-clk/bpmpfw/source/bpmp_mmu.hpp
@@ -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 .
+ *
+ */
+
+#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
diff --git a/Source/hoc-clk/bpmpfw/source/freq.hpp b/Source/hoc-clk/bpmpfw/source/freq.hpp
new file mode 100644
index 00000000..eea8e9a4
--- /dev/null
+++ b/Source/hoc-clk/bpmpfw/source/freq.hpp
@@ -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 .
+ *
+ */
+
+#pragma once
+#include "regs.hpp"
+
+#include
+
+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(cnt) * multiplier * osc) / win;
+ }
+
+ inline void Update(HocClkBpmpSharedInfo &info) {
+ info.freqMem = static_cast(MeasurePtoFreqHz(ClkPtoEmc, 1) / 1000);
+ info.freqCpu = static_cast(MeasurePtoFreqHz(ClkPtoCclkGDiv2, 2) / 1000);
+ }
+
+} // namespace freq
diff --git a/Source/hoc-clk/bpmpfw/source/main.cpp b/Source/hoc-clk/bpmpfw/source/main.cpp
index 2da95075..43e74416 100644
--- a/Source/hoc-clk/bpmpfw/source/main.cpp
+++ b/Source/hoc-clk/bpmpfw/source/main.cpp
@@ -18,6 +18,9 @@
#include
#include
+#include "actmon.hpp"
+#include "bpmp_mmu.hpp"
+#include "freq.hpp"
#include "libc_platform.hpp"
#include "regs.hpp"
#include "sensors.hpp"
@@ -34,6 +37,10 @@ namespace {
void MainLoop(HocClkBpmpSharedInfo &info) {
for (;;) {
+ if (info.exitRequested) {
+ break;
+ }
+
const u32 temp1 = MMIO32(SocthermBase + SocthermSensorTemp1);
const u32 temp2 = MMIO32(SocthermBase + SocthermSensorTemp2);
@@ -42,11 +49,23 @@ void MainLoop(HocClkBpmpSharedInfo &info) {
info.tempMem = TranslateSocthermTemp(static_cast(temp2 >> 16));
info.tempPllx = TranslateSocthermTemp(static_cast(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() {
+ bpmpMmu::Enable();
+
InitializeLibc();
printf("[hoc-bpmpfw]: Starting bpmpfw\n");
@@ -54,6 +73,9 @@ extern "C" void main() {
HocClkBpmpSharedInfo &info = SharedInfo();
memset(&info, 0, sizeof(info));
info.status = 0;
+
+ actmon::Init();
+
info.magic = HOCCLK_BPMP_MAGIC; // written last
/* If the main loop exits then it is a panic or invalid state */
diff --git a/Source/hoc-clk/common/include/hocclk/bpmp.h b/Source/hoc-clk/common/include/hocclk/bpmp.h
index d2b5f862..c064f835 100644
--- a/Source/hoc-clk/common/include/hocclk/bpmp.h
+++ b/Source/hoc-clk/common/include/hocclk/bpmp.h
@@ -33,7 +33,15 @@ typedef struct {
s32 tempGpu;
s32 tempMem;
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;
-static_assert(sizeof(HocClkBpmpSharedInfo) == 0x20);
+static_assert(sizeof(HocClkBpmpSharedInfo) == 0x3C);
diff --git a/Source/hoc-clk/common/include/hocclk/clock_manager.h b/Source/hoc-clk/common/include/hocclk/clock_manager.h
index f2338354..766412cb 100644
--- a/Source/hoc-clk/common/include/hocclk/clock_manager.h
+++ b/Source/hoc-clk/common/include/hocclk/clock_manager.h
@@ -89,7 +89,7 @@ typedef struct {
HocClkBpmpSharedInfo bpmpInfo;
// Reserved for future use
- u8 reserved[0x358];
+ u8 reserved[0x33C];
} HocClkContext;
typedef struct
diff --git a/Source/hoc-clk/sysmodule/src/board/board.cpp b/Source/hoc-clk/sysmodule/src/board/board.cpp
index da191e6d..049ec75a 100644
--- a/Source/hoc-clk/sysmodule/src/board/board.cpp
+++ b/Source/hoc-clk/sysmodule/src/board/board.cpp
@@ -32,7 +32,6 @@
#include
#include
#include
-#include
#include
#include "../bpmp/bpmp.hpp"
diff --git a/Source/hoc-clk/sysmodule/src/board/board_freq.cpp b/Source/hoc-clk/sysmodule/src/board/board_freq.cpp
index 8386d8ef..81128498 100644
--- a/Source/hoc-clk/sysmodule/src/board/board_freq.cpp
+++ b/Source/hoc-clk/sysmodule/src/board/board_freq.cpp
@@ -28,9 +28,9 @@
#include
#include
#include
-#include
#include
+#include "../bpmp/bpmp.hpp"
#include "../display/display_refresh_rate.hpp"
#include "../file/config.hpp"
#include "../file/errors.hpp"
@@ -179,13 +179,13 @@ namespace board {
u32 hz = 0;
switch (module) {
case HocClkModule_CPU:
- return t210ClkCpuFreq();
+ return bpmp::GetSharedInfo()->freqCpu * 1000;
case HocClkModule_GPU:
- return t210ClkGpuFreq();
+ return bpmp::GetSharedInfo()->freqGpu * 1000;
case HocClkModule_MEM:
return config::GetConfigValue(HocClkConfigValue_MemoryFrequencyMeasurementMode) == MemoryFrequencyMeasurementMode_PLL
? pllmb::getRamClockRatePLLMB()
- : t210ClkMemFreq();
+ : bpmp::GetSharedInfo()->freqMem * 1000;
case HocClkModule_Display:
return GetDisplayRate(hz);
default:
diff --git a/Source/hoc-clk/sysmodule/src/board/board_freq.hpp b/Source/hoc-clk/sysmodule/src/board/board_freq.hpp
index fa774cbe..b0a91b6f 100644
--- a/Source/hoc-clk/sysmodule/src/board/board_freq.hpp
+++ b/Source/hoc-clk/sysmodule/src/board/board_freq.hpp
@@ -29,7 +29,6 @@
#include
#include
#include
-#include
#include
#include "../file/errors.hpp"
diff --git a/Source/hoc-clk/sysmodule/src/board/board_load.cpp b/Source/hoc-clk/sysmodule/src/board/board_load.cpp
index bf814453..669f46d2 100644
--- a/Source/hoc-clk/sysmodule/src/board/board_load.cpp
+++ b/Source/hoc-clk/sysmodule/src/board/board_load.cpp
@@ -33,9 +33,9 @@
#include
#include
#include
-#include
#include
+#include "../bpmp/bpmp.hpp"
#include "../hos/apm_ext.h"
#include "board.hpp"
#include "board_misc.hpp"
@@ -116,9 +116,9 @@ namespace board {
u32 GetPartLoad(HocClkPartLoad loadSource) {
switch (loadSource) {
case HocClkPartLoad_EMC:
- return t210EmcLoadAll();
+ return bpmp::GetSharedInfo()->emcLoadAll;
case HocClkPartLoad_EMCCpu:
- return t210EmcLoadCpu();
+ return bpmp::GetSharedInfo()->emcLoadCpu;
case HocClkPartLoad_GPU:
return gpuLoad;
case HocClkPartLoad_CPUMax:
@@ -130,13 +130,13 @@ namespace board {
case HocClkPartLoad_FAN:
return GetFanLevel();
case HocClkPartLoad_RamBWAll:
- return t210EmcBwAll();
+ return bpmp::GetSharedInfo()->emcBwAll;
case HocClkPartLoad_RamBWCpu:
- return t210EmcBwCpu();
+ return bpmp::GetSharedInfo()->emcBwCpu;
case HocClkPartLoad_RamBWGpu:
- return t210EmcBwGpu();
+ return bpmp::GetSharedInfo()->emcBwGpu;
case HocClkPartLoad_RamBWPeak:
- return t210EmcBwPeak();
+ return static_cast((static_cast(bpmp::GetSharedInfo()->freqMem) * 16) / 1000);
default:
ASSERT_ENUM_VALID(HocClkPartLoad, loadSource);
}
diff --git a/Source/hoc-clk/sysmodule/src/board/board_profile.cpp b/Source/hoc-clk/sysmodule/src/board/board_profile.cpp
index 6d7da57e..eadeba8a 100644
--- a/Source/hoc-clk/sysmodule/src/board/board_profile.cpp
+++ b/Source/hoc-clk/sysmodule/src/board/board_profile.cpp
@@ -28,7 +28,6 @@
#include
#include
#include
-#include
#include
#include "../hos/apm_ext.h"
diff --git a/Source/hoc-clk/sysmodule/src/board/board_sensor.cpp b/Source/hoc-clk/sysmodule/src/board/board_sensor.cpp
index b451075a..1a274050 100644
--- a/Source/hoc-clk/sysmodule/src/board/board_sensor.cpp
+++ b/Source/hoc-clk/sysmodule/src/board/board_sensor.cpp
@@ -31,7 +31,6 @@
#include
#include
#include
-#include
#include
#include "../file/config.hpp"
diff --git a/Source/hoc-clk/sysmodule/src/bpmp/bpmp.cpp b/Source/hoc-clk/sysmodule/src/bpmp/bpmp.cpp
index bf1824cf..7c626409 100644
--- a/Source/hoc-clk/sysmodule/src/bpmp/bpmp.cpp
+++ b/Source/hoc-clk/sysmodule/src/bpmp/bpmp.cpp
@@ -16,6 +16,7 @@
*/
#include
+#include
#include
#include
@@ -91,7 +92,39 @@ namespace bpmp {
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() {
+ RequestBpmpExitAndWait();
+
SmcReadWriteRegister(EvpPhysBase + EvpCopResetVector, 0xFFFFFFFF, StockBpmpExceptionVector);
SmcReadWriteRegister(EvpPhysBase + EvpCopUndefVector, 0xFFFFFFFF, StockBpmpExceptionVector);
SmcReadWriteRegister(EvpPhysBase + EvpCopSwiVector, 0xFFFFFFFF, StockBpmpExceptionVector);
diff --git a/Source/hoc-clk/sysmodule/src/file/config.cpp b/Source/hoc-clk/sysmodule/src/file/config.cpp
index d911cebb..018571a5 100644
--- a/Source/hoc-clk/sysmodule/src/file/config.cpp
+++ b/Source/hoc-clk/sysmodule/src/file/config.cpp
@@ -34,7 +34,6 @@
#include
#include
#include
-#include
#include
#include
diff --git a/Source/hoc-clk/sysmodule/src/file/file_utils.cpp b/Source/hoc-clk/sysmodule/src/file/file_utils.cpp
index 107a2b73..169eb87b 100644
--- a/Source/hoc-clk/sysmodule/src/file/file_utils.cpp
+++ b/Source/hoc-clk/sysmodule/src/file/file_utils.cpp
@@ -26,7 +26,6 @@
#include
#include
-#include
#include
#include "../hos/apm_ext.h"
diff --git a/Source/hoc-clk/sysmodule/src/ipc/ipc_service.cpp b/Source/hoc-clk/sysmodule/src/ipc/ipc_service.cpp
index f6230f69..f1e01d9b 100644
--- a/Source/hoc-clk/sysmodule/src/ipc/ipc_service.cpp
+++ b/Source/hoc-clk/sysmodule/src/ipc/ipc_service.cpp
@@ -28,7 +28,6 @@
#include
#include
#include
-#include
#include
#include "../file/config.hpp"
diff --git a/Source/hoc-clk/sysmodule/src/soc/t210.c b/Source/hoc-clk/sysmodule/src/soc/t210.c
deleted file mode 100644
index d787976f..00000000
--- a/Source/hoc-clk/sysmodule/src/soc/t210.c
+++ /dev/null
@@ -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 .
- */
-
-#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;
-}
\ No newline at end of file
diff --git a/Source/hoc-clk/sysmodule/src/soc/t210.h b/Source/hoc-clk/sysmodule/src/soc/t210.h
deleted file mode 100644
index 2ad618f6..00000000
--- a/Source/hoc-clk/sysmodule/src/soc/t210.h
+++ /dev/null
@@ -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 .
- *
- */
-
-/* --------------------------------------------------------------------------
- * "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
- * --------------------------------------------------------------------------
- */
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include
-
-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