real freqs + ram load on bpmp
gpu freq is excluded due to smmu issue (carveout?)
This commit is contained in:
1 parent
8222fe6b0c
commit
d8c6a86bb0
20 files changed
+411
-368
No files matched your search
@@ -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 $@
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -18,6 +18,9 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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<u16>(temp2 >> 16));
|
||||
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() {
|
||||
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 */
|
||||
|
||||
@@ -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);
|
||||
@@ -89,7 +89,7 @@ typedef struct {
|
||||
HocClkBpmpSharedInfo bpmpInfo;
|
||||
|
||||
// Reserved for future use
|
||||
u8 reserved[0x358];
|
||||
u8 reserved[0x33C];
|
||||
} HocClkContext;
|
||||
|
||||
typedef struct
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
#include <pwm.h>
|
||||
#include <registers.h>
|
||||
#include <switch.h>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#include "../bpmp/bpmp.hpp"
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
#include <i2c.h>
|
||||
#include <max17050.h>
|
||||
#include <switch.h>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#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:
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <i2c.h>
|
||||
#include <max17050.h>
|
||||
#include <switch.h>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#include "../file/errors.hpp"
|
||||
|
||||
@@ -33,9 +33,9 @@
|
||||
#include <minIni.h>
|
||||
#include <numeric>
|
||||
#include <switch.h>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#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<u32>((static_cast<u64>(bpmp::GetSharedInfo()->freqMem) * 16) / 1000);
|
||||
default:
|
||||
ASSERT_ENUM_VALID(HocClkPartLoad, loadSource);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <i2c.h>
|
||||
#include <max17050.h>
|
||||
#include <switch.h>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#include "../hos/apm_ext.h"
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <max17050.h>
|
||||
#include <pwm.h>
|
||||
#include <switch.h>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#include "../file/config.hpp"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include <minIni.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include <i2c.h>
|
||||
#include <max17050.h>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#include "../hos/apm_ext.h"
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <i2c.h>
|
||||
#include <max17050.h>
|
||||
#include <switch.h>
|
||||
#include <t210.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#include "../file/config.hpp"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
Reference in new issue
Block a user