BPMP ABORT!
This commit is contained in:
1 parent
9428309ac3
commit
ab60e55303
87 files changed
+553
-3213
No files matched your search
@@ -34,7 +34,6 @@
|
||||
#include <switch.h>
|
||||
#include <tmp451.h>
|
||||
|
||||
#include "../bpmp/bpmp.hpp"
|
||||
#include "../display/display_refresh_rate.hpp"
|
||||
#include "../file/file_utils.hpp"
|
||||
#include "../hos/apm_ext.h"
|
||||
@@ -53,13 +52,7 @@
|
||||
#include "../soc/dram_mrr.hpp"
|
||||
namespace board {
|
||||
|
||||
u64 clkVirtAddr, dsiVirtAddr, apbVirtAddr, fuseVirtAddr, sysVirtAddr, actmonVirtAddr, gpuVirtAddr, tmrVirtAddr;
|
||||
consteval inline u32 PackCode(u32 r, u32 g, u32 b) {
|
||||
return ((r & 0xF) << 8) | ((g & 0xF) << 4) | ((b & 0xF) << 0);
|
||||
}
|
||||
const u32 GpuPanic = PackCode(0xF, 0x7, 0x0);
|
||||
const u32 CpuPanic = PackCode(0xF, 0x0, 0x0);
|
||||
const u32 RamPanic = PackCode(0x0, 0xF, 0xF);
|
||||
u64 clkVirtAddr, dsiVirtAddr, apbVirtAddr, fuseVirtAddr;
|
||||
|
||||
HocClkSocType gSocType;
|
||||
u8 gDramID;
|
||||
@@ -160,16 +153,6 @@ namespace board {
|
||||
rc = QueryMemoryMapping(&fuseVirtAddr, 0x7000F000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (fuse)");
|
||||
|
||||
rc = QueryMemoryMapping(&sysVirtAddr, 0x6000C000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (sys)");
|
||||
actmonVirtAddr = sysVirtAddr + 0x800;
|
||||
|
||||
rc = QueryMemoryMapping(&gpuVirtAddr, 0x57000000, 0x1000000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (gpu)");
|
||||
|
||||
rc = QueryMemoryMapping(&tmrVirtAddr, 0x60005000, 0x1000);
|
||||
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (tmr)");
|
||||
|
||||
FetchHardwareInfos();
|
||||
|
||||
Result nvCheck = 1;
|
||||
@@ -190,12 +173,6 @@ namespace board {
|
||||
|
||||
tsensor::InitializeSoctherm(); // SOCTHERM must be init before AOTAG
|
||||
|
||||
Result bpmpfwRc = bpmp::StartBpmfwExecution();
|
||||
if (R_FAILED(bpmpfwRc)) {
|
||||
file::utils::LogLine("[bpmp] StartBpmfwExecution failed: 0x%x", bpmpfwRc);
|
||||
}
|
||||
bpmp::StartSleepMonitorThread();
|
||||
|
||||
// PMC exosphere check
|
||||
SecmonArgs args = {};
|
||||
args.X[0] = 0xF0000002;
|
||||
@@ -224,7 +201,6 @@ namespace board {
|
||||
}
|
||||
|
||||
void Exit() {
|
||||
bpmp::StopSleepMonitorThread();
|
||||
|
||||
if (HOSSVC_HAS_CLKRST) {
|
||||
clkrstExit();
|
||||
@@ -254,30 +230,6 @@ namespace board {
|
||||
nvExit();
|
||||
}
|
||||
|
||||
#define MMIO32(addr) (*reinterpret_cast<volatile u32 *>(addr))
|
||||
NX_NORETURN void Panic(u32 c) {
|
||||
SmcReadWriteRegister(0x7000EC40 /* PMC_BASE + APBDEV_PMC_SCRATCH200 */, ~0, c);
|
||||
|
||||
/* Write timer magic */
|
||||
MMIO32(tmrVirtAddr + 0x18C) = 0xC45A;
|
||||
|
||||
/* Disable counters */
|
||||
MMIO32(tmrVirtAddr + 0x188) = 0x2;
|
||||
|
||||
/* Start periodic timer */
|
||||
MMIO32(tmrVirtAddr + 0x80) = 0xC0000000;
|
||||
|
||||
/* Set the reboot source to the timer */
|
||||
MMIO32(tmrVirtAddr + 0x180) = 0x8019;
|
||||
|
||||
/* Enable counter */
|
||||
MMIO32(tmrVirtAddr + 0x188) = 0x1;
|
||||
|
||||
/* Hang. */
|
||||
for(;;)
|
||||
;
|
||||
}
|
||||
|
||||
HocClkSocType GetSocType() {
|
||||
return gSocType;
|
||||
}
|
||||
|
||||
@@ -42,15 +42,12 @@
|
||||
#define HOSSVC_HAS_TC (hosversionAtLeast(5, 0, 0))
|
||||
|
||||
namespace board {
|
||||
extern u64 clkVirtAddr, dsiVirtAddr, apbVirtAddr, fuseVirtAddr, sysVirtAddr, actmonVirtAddr, gpuVirtAddr;
|
||||
extern u64 clkVirtAddr, dsiVirtAddr, apbVirtAddr, fuseVirtAddr;
|
||||
extern HocClkSocType gSocType;
|
||||
extern u8 gDramID;
|
||||
extern HocClkConsoleType gConsoleType;
|
||||
extern FuseData fuseData;
|
||||
extern u8 speedoBracket;
|
||||
extern const u32 GpuPanic;
|
||||
extern const u32 CpuPanic;
|
||||
extern const u32 RamPanic;
|
||||
|
||||
void Initialize();
|
||||
void Exit();
|
||||
@@ -62,6 +59,5 @@ namespace board {
|
||||
bool IsDram8GB();
|
||||
void SetDisplayRefreshDockedState(bool docked);
|
||||
FuseData *GetFuseData();
|
||||
NX_NORETURN void Panic(u32 c);
|
||||
|
||||
} // namespace board
|
||||
@@ -28,15 +28,16 @@
|
||||
#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"
|
||||
#include "../hos/apm_ext.h"
|
||||
#include "../i2c/i2cDrv.h"
|
||||
#include "../soc/gm20b.hpp"
|
||||
#include "../soc/pllmb.hpp"
|
||||
#include "board.hpp"
|
||||
#include "board_name.hpp"
|
||||
#include <ipc_server.h>
|
||||
@@ -183,30 +184,13 @@ namespace board {
|
||||
u32 hz = 0;
|
||||
switch (module) {
|
||||
case HocClkModule_CPU:
|
||||
return bpmp::GetSharedInfo()->freqCpu * 1000;
|
||||
case HocClkModule_GPU: {
|
||||
constexpr u32 ClkOutEnbX = 0x280;
|
||||
constexpr u32 RstDevicesX = 0x28C;
|
||||
constexpr u32 GpuBit = (1u << 24);
|
||||
|
||||
const bool gpuEnabled = (*reinterpret_cast<volatile u32 *>(clkVirtAddr + ClkOutEnbX) & GpuBit) &&
|
||||
!(*reinterpret_cast<volatile u32 *>(clkVirtAddr + RstDevicesX) & GpuBit);
|
||||
if (!gpuEnabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const u32 coeff = *reinterpret_cast<volatile u32 *>(gpuVirtAddr + 0x137004);
|
||||
const u32 divm = coeff & 0xFF;
|
||||
const u32 divn = (coeff >> 8) & 0xFF;
|
||||
const u32 divp = (coeff >> 16) & 0x3F;
|
||||
if (divm == 0 || divp == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return static_cast<u32>((static_cast<u64>(38400000) * divn) / (divm * divp) / 2);
|
||||
}
|
||||
return t210ClkCpuFreq();
|
||||
case HocClkModule_GPU:
|
||||
return t210ClkGpuFreq();
|
||||
case HocClkModule_MEM:
|
||||
return bpmp::GetSharedInfo()->freqMemPll * 1000;
|
||||
return file::config::GetConfigValue(HocClkConfigValue_MemoryFrequencyMeasurementMode) == MemoryFrequencyMeasurementMode_PLL
|
||||
? pllmb::getRamClockRatePLLMB()
|
||||
: t210ClkMemFreq();
|
||||
case HocClkModule_Display:
|
||||
return GetDisplayRate(hz);
|
||||
default:
|
||||
|
||||
@@ -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 bpmp::GetSharedInfo()->emcLoadAll;
|
||||
return t210EmcLoadAll();
|
||||
case HocClkPartLoad_EMCCpu:
|
||||
return bpmp::GetSharedInfo()->emcLoadCpu;
|
||||
return t210EmcLoadCpu();
|
||||
case HocClkPartLoad_GPU:
|
||||
return gpuLoad;
|
||||
case HocClkPartLoad_CPUMax:
|
||||
@@ -130,13 +130,13 @@ namespace board {
|
||||
case HocClkPartLoad_FAN:
|
||||
return GetFanLevel();
|
||||
case HocClkPartLoad_RamBWAll:
|
||||
return bpmp::GetSharedInfo()->emcBwAll;
|
||||
return t210EmcBwAll();
|
||||
case HocClkPartLoad_RamBWCpu:
|
||||
return bpmp::GetSharedInfo()->emcBwCpu;
|
||||
return t210EmcBwCpu();
|
||||
case HocClkPartLoad_RamBWGpu:
|
||||
return bpmp::GetSharedInfo()->emcBwGpu;
|
||||
return t210EmcBwGpu();
|
||||
case HocClkPartLoad_RamBWPeak:
|
||||
return static_cast<u32>((static_cast<u64>(bpmp::GetSharedInfo()->freqMem) * 16) / 1000);
|
||||
return t210EmcBwPeak();
|
||||
default:
|
||||
ASSERT_ENUM_VALID(HocClkPartLoad, loadSource);
|
||||
}
|
||||
|
||||
@@ -1,406 +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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <switch.h>
|
||||
|
||||
#include "../board/board.hpp"
|
||||
#include "../file/config.hpp"
|
||||
#include "../file/file_utils.hpp"
|
||||
#include "../mapping/mem_map.hpp"
|
||||
#include "../tsensor/soctherm.hpp"
|
||||
#include "bpmp.hpp"
|
||||
|
||||
#include "bpmpfw_bin.h"
|
||||
|
||||
namespace bpmp {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u64 FlowCtlrPhysBase = 0x60007000;
|
||||
constexpr u64 EvpPhysBase = 0x6000F000;
|
||||
|
||||
constexpr u32 ClkRstRstDevLSet = 0x300;
|
||||
constexpr u32 ClkRstRstDevLClr = 0x304;
|
||||
constexpr u32 RstDevLCopRstBit = (1u << 1); /* SWR_COP_RST */
|
||||
|
||||
constexpr u32 ApbMiscSlaveSecurityReg0 = 0xC00;
|
||||
constexpr u32 ApbMiscSlaveSecurityReg1 = 0xC04;
|
||||
constexpr u32 ApbMiscSlaveSecurityReg2 = 0xC08;
|
||||
|
||||
/* Relative to actmonVirtAddr */
|
||||
constexpr u32 ActmonGlbPeriodCtrl = 0x004;
|
||||
constexpr u32 ActmonCopCtrl = 0xC0;
|
||||
constexpr u32 ActmonCopUpperWmark = 0xC4;
|
||||
constexpr u32 ActmonCopIntrStatus = 0xE4;
|
||||
|
||||
constexpr u32 ActmonGlbPeriodCtrlUsec = (1u << 8);
|
||||
constexpr u32 ActmonCopCtrlStockArmed = (1u << 31) | (1u << 30) | (1u << 18);
|
||||
|
||||
/* MC SMMU handling for the BPMP. */
|
||||
constexpr u64 McPhysBase = 0x70019000;
|
||||
|
||||
constexpr u32 McSmmuTlbConfig = 0x014;
|
||||
constexpr u32 McSmmuPtbAsid = 0x01C;
|
||||
constexpr u32 McSmmuPtbData = 0x020;
|
||||
constexpr u32 McSmmuTlbFlush = 0x030;
|
||||
constexpr u32 McSmmuPtcFlush = 0x034;
|
||||
constexpr u32 McSmmuAvpcAsid = 0x23C;
|
||||
constexpr u32 McSmmuPpcs1Asid = 0x298;
|
||||
|
||||
/* Page table is backed by the security engine's MMIO aperture. */
|
||||
constexpr u64 BpmpSmmuPdeBase = 0x70012000;
|
||||
constexpr u32 BpmpSmmuAsid = 1;
|
||||
|
||||
/* MC_SMMU_PTB_ASID */
|
||||
constexpr u32 SmmuPtbAsidCurrentAsid = BpmpSmmuAsid; /* [0:7] */
|
||||
|
||||
/* MC_SMMU_PTB_DATA */
|
||||
constexpr u32 SmmuPtbDataPdeMask = 0x7FFFFF; /* [0:22] */
|
||||
|
||||
/* MC_SMMU_AVPC_ASID / MC_SMMU_PPCS1_ASID */
|
||||
constexpr u32 SmmuClientSmmuEnableBit = (1u << 31);
|
||||
constexpr u32 SmmuAvpcAsidValue = (SmmuClientSmmuEnableBit | BpmpSmmuAsid);
|
||||
constexpr u32 SmmuPpcs1AsidValue = (SmmuClientSmmuEnableBit | BpmpSmmuAsid);
|
||||
|
||||
constexpr u32 EvpCopResetVector = 0x200;
|
||||
constexpr u32 EvpCopUndefVector = 0x204;
|
||||
constexpr u32 EvpCopSwiVector = 0x208;
|
||||
constexpr u32 EvpCopPrefetchAbortVector = 0x20C;
|
||||
constexpr u32 EvpCopDataAbortVector = 0x210;
|
||||
constexpr u32 EvpCopRsvdVector = 0x214;
|
||||
constexpr u32 EvpCopIrqVector = 0x218;
|
||||
constexpr u32 EvpCopFiqVector = 0x21C;
|
||||
|
||||
constexpr u32 StockBpmpExceptionVector = 0x7D000000;
|
||||
|
||||
constexpr u32 FlowCtlrHaltCopEvents = 0x004;
|
||||
constexpr u32 FlowModeNone = 0x00000000;
|
||||
constexpr u32 FlowModeStop = (2u << 29);
|
||||
|
||||
constexpr u64 FwIramPhysBase = 0x40004000;
|
||||
constexpr u32 FwStagingSize = 0x8000;
|
||||
constexpr u64 WorkRamPhysBase = FwIramPhysBase + FwStagingSize; // matches bpmpfw's WorkRamStart
|
||||
|
||||
/* secmon will return error unless we apply thiss workaround */
|
||||
alignas(4096) u8 s_fwStageBuf[FwStagingSize];
|
||||
|
||||
HocClkBpmpSharedInfo s_sharedInfo = {};
|
||||
|
||||
inline volatile u32 &Mmio(u64 base_va, u32 offset) {
|
||||
return *reinterpret_cast<volatile u32 *>(base_va + offset);
|
||||
}
|
||||
|
||||
// Late in the boot chain, otherwise a fatal will occur from invalid boot state
|
||||
constexpr u32 pscDependencies[] = { PscPmModuleId_Olsc };
|
||||
constexpr PscPmModuleId PscModuleId = (PscPmModuleId)705;
|
||||
|
||||
PscPmModule s_pscModule;
|
||||
bool s_pscPrepared = false;
|
||||
Thread s_pscThread;
|
||||
bool s_pscExit = false;
|
||||
|
||||
std::atomic_bool s_isAwake = true;
|
||||
|
||||
|
||||
/* Must be called to prevent secmon/erpt panic on wake */
|
||||
void RequestBpmpExitAndWait() {
|
||||
if (!IsPatchedExosphere()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 cmd = HocClkBpmpCmd_RequestShutdown;
|
||||
Result rc = SmcCopyToIram(WorkRamPhysBase + offsetof(HocClkBpmpSharedInfo, cmd), &cmd, sizeof(cmd));
|
||||
if (R_FAILED(rc)) {
|
||||
file::utils::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;
|
||||
}
|
||||
}
|
||||
|
||||
file::utils::LogLine("[bpmp] RequestBpmpExitAndWait: BPMP timeout (cannot sleep)");
|
||||
board::Panic(board::CpuPanic);
|
||||
}
|
||||
|
||||
void EnableBpmpSmmu() {
|
||||
/* Point the ASID page table at the security engine aperture. */
|
||||
const u32 pde_base = static_cast<u32>(BpmpSmmuPdeBase / 0x1000);
|
||||
|
||||
/* Select the ASID and configure its page table entry. */
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuPtbAsid, 0xFFFFFFFF, SmmuPtbAsidCurrentAsid);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuPtbData, 0xFFFFFFFF, pde_base & SmmuPtbDataPdeMask);
|
||||
|
||||
/* Route BPMP (AVPC) and PPCS1 through the SMMU. */
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuAvpcAsid, 0xFFFFFFFF, SmmuAvpcAsidValue);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuPpcs1Asid, 0xFFFFFFFF, SmmuPpcs1AsidValue);
|
||||
|
||||
/* Flush the page table cache. */
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuPtcFlush, 0xFFFFFFFF, 0);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuTlbConfig, 0, 0);
|
||||
|
||||
/* Flush the translation lookaside buffer. */
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuTlbFlush, 0xFFFFFFFF, 0);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuTlbConfig, 0, 0);
|
||||
}
|
||||
|
||||
void DisableBpmpSmmu() {
|
||||
/* Clear the ASID page table. */
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuPtbAsid, 0xFFFFFFFF, SmmuPtbAsidCurrentAsid);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuPtbData, 0xFFFFFFFF, 0);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuAvpcAsid, 0xFFFFFFFF, 0);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuPpcs1Asid, 0xFFFFFFFF, 0);
|
||||
|
||||
/* Flush the page table cache. */
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuPtcFlush, 0xFFFFFFFF, 0);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuTlbConfig, 0, 0);
|
||||
|
||||
/* Flush the translation lookaside buffer. */
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuTlbFlush, 0xFFFFFFFF, 0);
|
||||
SmcReadWriteRegister(McPhysBase + McSmmuTlbConfig, 0, 0);
|
||||
}
|
||||
|
||||
bool WaitForBpmpReady(int maxAttempts = 50, u64 pollIntervalNs = 20'000'000) {
|
||||
HocClkBpmpSharedInfo info = {};
|
||||
for (int i = 0; i < maxAttempts; i++) {
|
||||
Result rc = SmcCopyFromIram(&info, WorkRamPhysBase, sizeof(info));
|
||||
if (R_SUCCEEDED(rc) && info.magic == HOCCLK_BPMP_MAGIC) {
|
||||
return true;
|
||||
}
|
||||
svcSleepThread(pollIntervalNs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SendBpmpCommand(HocClkBpmpCmd cmd, u32 arg1 = 0, u32 arg2 = 0, u32 arg3 = 0, u32 arg4 = 0) {
|
||||
if (!WaitForBpmpReady()) {
|
||||
file::utils::LogLine("[bpmp] SendBpmpCommand: BPMP never signaled ready (cmd=%u)", static_cast<u32>(cmd));
|
||||
return;
|
||||
}
|
||||
|
||||
struct {
|
||||
u32 cmd;
|
||||
u32 cmdArg1;
|
||||
u32 cmdArg2;
|
||||
u32 cmdArg3;
|
||||
u32 cmdArg4;
|
||||
} req = { static_cast<u32>(cmd), arg1, arg2, arg3, arg4 };
|
||||
static_assert(offsetof(HocClkBpmpSharedInfo, cmdArg1) == offsetof(HocClkBpmpSharedInfo, cmd) + 1 * sizeof(u32));
|
||||
static_assert(offsetof(HocClkBpmpSharedInfo, cmdArg4) == offsetof(HocClkBpmpSharedInfo, cmd) + 4 * sizeof(u32));
|
||||
|
||||
const Result rc = SmcCopyToIram(WorkRamPhysBase + offsetof(HocClkBpmpSharedInfo, cmd), &req, sizeof(req));
|
||||
if (R_FAILED(rc)) {
|
||||
file::utils::LogLine("[bpmp] SendBpmpCommand: SmcCopyToIram failed: 0x%x (cmd=%u)", rc, static_cast<u32>(cmd));
|
||||
}
|
||||
}
|
||||
|
||||
void RestoreStockState() {
|
||||
RequestBpmpExitAndWait();
|
||||
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopResetVector, 0xFFFFFFFF, StockBpmpExceptionVector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopUndefVector, 0xFFFFFFFF, StockBpmpExceptionVector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopSwiVector, 0xFFFFFFFF, StockBpmpExceptionVector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopPrefetchAbortVector, 0xFFFFFFFF, StockBpmpExceptionVector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopDataAbortVector, 0xFFFFFFFF, StockBpmpExceptionVector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopRsvdVector, 0xFFFFFFFF, StockBpmpExceptionVector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopIrqVector, 0xFFFFFFFF, StockBpmpExceptionVector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopFiqVector, 0xFFFFFFFF, StockBpmpExceptionVector);
|
||||
|
||||
SmcReadWriteRegister(FlowCtlrPhysBase + FlowCtlrHaltCopEvents, 0xFFFFFFFF, FlowModeStop);
|
||||
|
||||
Mmio(board::actmonVirtAddr, ActmonGlbPeriodCtrl) = ActmonGlbPeriodCtrlUsec;
|
||||
Mmio(board::actmonVirtAddr, ActmonCopUpperWmark) = 0;
|
||||
Mmio(board::actmonVirtAddr, ActmonCopIntrStatus) = Mmio(board::actmonVirtAddr, ActmonCopIntrStatus);
|
||||
Mmio(board::actmonVirtAddr, ActmonCopCtrl) = ActmonCopCtrlStockArmed;
|
||||
|
||||
EnableBpmpSmmu();
|
||||
}
|
||||
|
||||
void PscThreadFunc(void *) {
|
||||
while (!s_pscExit) {
|
||||
Result rc = eventWait(&s_pscModule.event, 1'000'000'000ULL);
|
||||
if (R_FAILED(rc)) {
|
||||
continue; // Timeout
|
||||
}
|
||||
|
||||
PscPmState state;
|
||||
u32 flags;
|
||||
rc = pscPmModuleGetRequest(&s_pscModule, &state, &flags);
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
if (state == PscPmState_Awake) {
|
||||
svcSleepThread(500'000'000); // 500ms
|
||||
|
||||
/* I have no clue why this is needed as it wasn't needed before ¯\(ツ)/¯ */
|
||||
tsensor::InitializeSoctherm();
|
||||
|
||||
Result wrc = StartBpmfwExecution();
|
||||
if (R_FAILED(wrc)) {
|
||||
file::utils::LogLine("[bpmp] restart after wake failed: 0x%x", wrc);
|
||||
}
|
||||
|
||||
s_isAwake = true;
|
||||
} else if (state == PscPmState_ReadySleep || state == PscPmState_ReadyShutdown) {
|
||||
RestoreStockState();
|
||||
s_isAwake = false;
|
||||
}
|
||||
|
||||
pscPmModuleAcknowledge(&s_pscModule, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Result StartBpmfwExecution() {
|
||||
if (!IsPatchedExosphere()) {
|
||||
file::utils::LogLine("[bpmp] Cannot start without patched exosphere");
|
||||
return (Result)1;
|
||||
}
|
||||
|
||||
const size_t fw_size = bpmpfw_bin_size;
|
||||
if (fw_size == 0 || fw_size > FwStagingSize) {
|
||||
file::utils::LogLine("[bpmp] StartBpmfwExecution: bpmpfw of %zu bytes is invalid (should not happen)!", fw_size);
|
||||
return (Result)1;
|
||||
}
|
||||
|
||||
std::memcpy(s_fwStageBuf, bpmpfw_bin, fw_size);
|
||||
|
||||
file::utils::LogLine("[bpmp] StartBpmfwExecution: loading %zu bytes at 0x%llx",
|
||||
fw_size, static_cast<unsigned long long>(FwIramPhysBase));
|
||||
|
||||
/* Disable the SMMU so DRAM can be accessed */
|
||||
DisableBpmpSmmu();
|
||||
|
||||
/* Set BPMP reset */
|
||||
Mmio(board::clkVirtAddr, ClkRstRstDevLSet) = RstDevLCopRstBit;
|
||||
|
||||
/* Grant BPMP access to all a bunch of registers that are useful */
|
||||
Mmio(board::apbVirtAddr, ApbMiscSlaveSecurityReg0) = 0;
|
||||
Mmio(board::apbVirtAddr, ApbMiscSlaveSecurityReg1) = 0;
|
||||
Mmio(board::apbVirtAddr, ApbMiscSlaveSecurityReg2) = 0;
|
||||
|
||||
/* Set up exeption vectors for bpmp */
|
||||
const u32 fw_reset_vector = static_cast<u32>(FwIramPhysBase + 0x0);
|
||||
const u32 fw_panic_vector = static_cast<u32>(FwIramPhysBase + 0x4);
|
||||
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopResetVector, 0xFFFFFFFF, fw_reset_vector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopUndefVector, 0xFFFFFFFF, fw_panic_vector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopSwiVector, 0xFFFFFFFF, fw_panic_vector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopPrefetchAbortVector, 0xFFFFFFFF, fw_panic_vector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopDataAbortVector, 0xFFFFFFFF, fw_panic_vector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopRsvdVector, 0xFFFFFFFF, fw_panic_vector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopIrqVector, 0xFFFFFFFF, fw_panic_vector);
|
||||
SmcReadWriteRegister(EvpPhysBase + EvpCopFiqVector, 0xFFFFFFFF, fw_panic_vector);
|
||||
|
||||
/* Disable actmon monitoring to avoid hang on FW >4.0.0 */
|
||||
Mmio(board::actmonVirtAddr, ActmonCopCtrl) = 0;
|
||||
Mmio(board::actmonVirtAddr, ActmonCopIntrStatus) = Mmio(board::actmonVirtAddr, ActmonCopIntrStatus);
|
||||
|
||||
/* Load the BPMPFW into iram per page. TODO: determine if this is nessesary */
|
||||
constexpr size_t PageSize = 4096;
|
||||
for (size_t ofs = 0; ofs < fw_size; ofs += PageSize) {
|
||||
const size_t chunk = (fw_size - ofs) < PageSize ? (fw_size - ofs) : PageSize;
|
||||
|
||||
Result copy_rc = SmcCopyToIram(FwIramPhysBase + ofs, s_fwStageBuf + ofs, static_cast<u32>(chunk));
|
||||
if (R_FAILED(copy_rc)) {
|
||||
file::utils::LogLine("[bpmp] SmcCopyToIram failed at +0x%zx: 0x%x", ofs, copy_rc);
|
||||
return copy_rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear BPMP reset. */
|
||||
Mmio(board::clkVirtAddr, ClkRstRstDevLClr) = RstDevLCopRstBit;
|
||||
|
||||
/* Clear the halt written by bootloader. */
|
||||
SmcReadWriteRegister(FlowCtlrPhysBase + FlowCtlrHaltCopEvents, 0xFFFFFFFF, FlowModeNone);
|
||||
|
||||
SendBpmpCommand(HocClkBpmpCmd_SetUartEnabled, file::utils::IsUartEnabled() ? 1u : 0u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StartSleepMonitorThread() {
|
||||
if (!IsPatchedExosphere()) {
|
||||
file::utils::LogLine("[bpmp] Cant start BPMP without exosphere patch");
|
||||
return;
|
||||
}
|
||||
|
||||
Result rc = pscmInitialize();
|
||||
if (R_FAILED(rc)) {
|
||||
file::utils::LogLine("[bpmp] pscmInitialize failed: 0x%x", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = pscmGetPmModule(&s_pscModule, PscModuleId, pscDependencies, sizeof(pscDependencies) / sizeof(u32), true);
|
||||
if (R_FAILED(rc)) {
|
||||
file::utils::LogLine("[bpmp] pscmGetPmModule failed: 0x%x", rc);
|
||||
pscmExit();
|
||||
return;
|
||||
}
|
||||
s_pscPrepared = true;
|
||||
|
||||
rc = threadCreate(&s_pscThread, PscThreadFunc, nullptr, NULL, 0x1000, 0x10, 3);
|
||||
if (R_FAILED(rc)) {
|
||||
file::utils::LogLine("[bpmp] failed to create psc thread: 0x%x", rc);
|
||||
return;
|
||||
}
|
||||
threadStart(&s_pscThread);
|
||||
}
|
||||
|
||||
void StopSleepMonitorThread() {
|
||||
if (!s_pscPrepared) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_pscExit = true;
|
||||
eventFire(&s_pscModule.event);
|
||||
threadWaitForExit(&s_pscThread);
|
||||
threadClose(&s_pscThread);
|
||||
|
||||
pscPmModuleFinalize(&s_pscModule);
|
||||
pscPmModuleClose(&s_pscModule);
|
||||
pscmExit();
|
||||
s_pscPrepared = false;
|
||||
}
|
||||
|
||||
const HocClkBpmpSharedInfo *GetSharedInfo() {
|
||||
if (!IsPatchedExosphere()) {
|
||||
s_sharedInfo = {};
|
||||
return &s_sharedInfo;
|
||||
}
|
||||
|
||||
SmcCopyFromIram(&s_sharedInfo, WorkRamPhysBase, sizeof(s_sharedInfo));
|
||||
return &s_sharedInfo;
|
||||
}
|
||||
|
||||
bool IsAwake() {
|
||||
return s_isAwake;
|
||||
}
|
||||
|
||||
} // namespace bpmp
|
||||
@@ -66,35 +66,3 @@ Result SmcCopyToIram(uintptr_t dest, const void *src, u32 size) {
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool IsPatchedExosphere() {
|
||||
static bool checked = false;
|
||||
static bool patched = false;
|
||||
|
||||
if (!checked) {
|
||||
constexpr u64 EvpPhysBase = 0x6000F000;
|
||||
constexpr u64 EvpCopResetVector = 0x200;
|
||||
|
||||
SecmonArgs args = {};
|
||||
args.X[0] = 0xF0000002;
|
||||
args.X[1] = EvpPhysBase + EvpCopResetVector;
|
||||
args.X[2] = 0;
|
||||
args.X[3] = 0;
|
||||
svcCallSecureMonitor(&args);
|
||||
|
||||
patched = (args.X[0] == 0);
|
||||
checked = true;
|
||||
}
|
||||
|
||||
return patched;
|
||||
}
|
||||
|
||||
u32 SmcReadWriteRegister(u64 phys_addr, u32 mask, u32 value) {
|
||||
SecmonArgs args = {};
|
||||
args.X[0] = 0xF0000002;
|
||||
args.X[1] = phys_addr;
|
||||
args.X[2] = mask;
|
||||
args.X[3] = value;
|
||||
svcCallSecureMonitor(&args);
|
||||
return (u32)args.X[1];
|
||||
}
|
||||
@@ -22,5 +22,3 @@ Result QueryMemoryMapping(u64 *virtaddr, u64 physaddr, u64 size);
|
||||
Result MapAddress(u64 &va, const u64 &physAddr, const char *name);
|
||||
Result SmcCopyFromIram(void *dest, uintptr_t src, u32 size);
|
||||
Result SmcCopyToIram(uintptr_t dest, const void *src, u32 size);
|
||||
bool IsPatchedExosphere();
|
||||
u32 SmcReadWriteRegister(u64 phys_addr, u32 mask, u32 value);
|
||||
@@ -30,7 +30,6 @@
|
||||
#include <i2c.h>
|
||||
|
||||
#include "../board/board.hpp"
|
||||
#include "../bpmp/bpmp.hpp"
|
||||
#include "../display/aula.hpp"
|
||||
#include "../display/display_refresh_rate.hpp"
|
||||
#include "../file/config.hpp"
|
||||
@@ -630,8 +629,6 @@ namespace mgr {
|
||||
bool RefreshContext() {
|
||||
bool hasChanged = false;
|
||||
|
||||
gContext.bpmpInfo = *bpmp::GetSharedInfo();
|
||||
|
||||
std::uint32_t mode = 0;
|
||||
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
@@ -882,10 +879,6 @@ namespace mgr {
|
||||
}
|
||||
|
||||
void WaitForNextTick() {
|
||||
if (!bpmp::IsAwake()) {
|
||||
svcSleepThread(250 * 1000000ULL); // 250ms
|
||||
return;
|
||||
}
|
||||
|
||||
if (board::GetHz(HocClkModule_MEM) > 665000000)
|
||||
svcSleepThread(file::config::GetConfigValue(HocClkConfigValue_PollingIntervalMs) * 1000000ULL);
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer
|
||||
*
|
||||
* Copyright (c) KazushiMe
|
||||
*
|
||||
* 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 "pllmb.hpp"
|
||||
|
||||
namespace pllmb {
|
||||
#define GET_BITS(VAL, HIGH, LOW) ((VAL & ((1UL << (HIGH + 1UL)) - 1UL)) >> LOW)
|
||||
#define GET_BIT(VAL, BIT) GET_BITS(VAL, BIT, BIT)
|
||||
|
||||
static inline volatile u32 ®(uintptr_t addr) {
|
||||
return *reinterpret_cast<volatile u32 *>(addr);
|
||||
}
|
||||
|
||||
// From jetson nano kernel
|
||||
typedef enum {
|
||||
/* divider = 2 */
|
||||
CLK_PLLX = 5,
|
||||
CLK_PLLM = 2,
|
||||
CLK_PLLMB = 37,
|
||||
/* PLLX & PLLG are backup PLLs for CPU & GPU */
|
||||
/* divider = 1 */
|
||||
CLK_CCLK_G = 18, // A57 CPU cluster
|
||||
CLK_EMC = 36,
|
||||
} PTO_ID; // PLL Test Output Register ID
|
||||
|
||||
/* See if GM20B clock GPC PLL regs are accessible. */
|
||||
|
||||
#define PLLX_MISC0 0xE4
|
||||
#define PLLM_MISC2 0x9C
|
||||
|
||||
double ptoGetMHz(PTO_ID pto_id, u32 divider = 1, u32 presel_reg = 0, u32 presel_mask = 0) {
|
||||
u32 pre_val, val, presel_val;
|
||||
|
||||
if (presel_reg) {
|
||||
val = REG(board::clkVirtAddr + presel_reg);
|
||||
usleep(10);
|
||||
presel_val = val & presel_mask;
|
||||
val &= ~presel_mask;
|
||||
val |= presel_mask;
|
||||
REG(board::clkVirtAddr + presel_reg) = val;
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
constexpr u32 cycle_count = 16;
|
||||
pre_val = REG(board::clkVirtAddr + 0x60);
|
||||
val = BIT(23) | BIT(13) | (cycle_count - 1);
|
||||
val |= pto_id << 14;
|
||||
|
||||
REG(board::clkVirtAddr + 0x60) = val;
|
||||
usleep(10);
|
||||
REG(board::clkVirtAddr + 0x60) = val | BIT(10);
|
||||
usleep(10);
|
||||
REG(board::clkVirtAddr + 0x60) = val;
|
||||
usleep(10);
|
||||
REG(board::clkVirtAddr + 0x60) = val | BIT(9);
|
||||
usleep(500);
|
||||
|
||||
while (REG(board::clkVirtAddr + 0x64) & BIT(31))
|
||||
;
|
||||
|
||||
val = REG(board::clkVirtAddr + 0x64);
|
||||
val &= 0xFFFFFF;
|
||||
val *= divider;
|
||||
|
||||
double rate_hz = (u64)val * 32768. / cycle_count;
|
||||
usleep(10);
|
||||
REG(board::clkVirtAddr + 0x60) = pre_val;
|
||||
usleep(10);
|
||||
|
||||
if (presel_reg) {
|
||||
val = REG(board::clkVirtAddr + presel_reg);
|
||||
usleep(10);
|
||||
val &= ~presel_mask;
|
||||
val |= presel_val;
|
||||
REG(board::clkVirtAddr + presel_reg) = val;
|
||||
usleep(10);
|
||||
}
|
||||
|
||||
return rate_hz;
|
||||
}
|
||||
|
||||
u64 getRamClockRatePLLMB() {
|
||||
// printf("\n"
|
||||
// "EMC: %6.1f MHz\n"
|
||||
// "CCLK_G: %6.1f MHz\n"
|
||||
// "PLLX: %6.1f MHz\n"
|
||||
// "PLLM: %6.1f MHz\n"
|
||||
// "PLLMB: %6.1f MHz\n",
|
||||
// ptoGetMHz(CLK_EMC),
|
||||
// ptoGetMHz(CLK_CCLK_G),
|
||||
// ptoGetMHz(CLK_PLLX, 2, PLLX_MISC0, BIT(22)),
|
||||
// ptoGetMHz(CLK_PLLM, 2, PLLM_MISC2, BIT(8)),
|
||||
// ptoGetMHz(CLK_PLLMB, 2, PLLM_MISC2, BIT(9))
|
||||
// );
|
||||
|
||||
u32 pllmb = ptoGetMHz(CLK_PLLMB, 2, PLLM_MISC2, BIT(9));
|
||||
u32 pllm = ptoGetMHz(CLK_PLLM, 2, PLLM_MISC2, BIT(8));
|
||||
return pllmb == 0 ? pllm : pllmb; // pllmb is zeroed out at times, fallback to pllm
|
||||
}
|
||||
} // namespace pllmb
|
||||
+10
-12
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) Souldbminer, Lightos and Horizon OC Contributors
|
||||
* Copyright (c) Souldbminer
|
||||
*
|
||||
* Copyright (c) KazushiMe
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@@ -16,18 +18,14 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <hocclk.h>
|
||||
#include <registers.h>
|
||||
#include <switch.h>
|
||||
|
||||
#include <hocclk/bpmp.h>
|
||||
#include "../board/board.hpp"
|
||||
|
||||
namespace bpmp {
|
||||
Result StartBpmfwExecution();
|
||||
namespace pllmb {
|
||||
|
||||
void StartSleepMonitorThread();
|
||||
void StopSleepMonitorThread();
|
||||
|
||||
const HocClkBpmpSharedInfo *GetSharedInfo();
|
||||
|
||||
// Always true on stock exosphere
|
||||
bool IsAwake();
|
||||
}
|
||||
u64 getRamClockRatePLLMB();
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
#include <notification.h>
|
||||
|
||||
#include "../bpmp/bpmp.hpp"
|
||||
#include "../file/file_utils.hpp"
|
||||
#include "../mapping/mem_map.hpp"
|
||||
#include "aotag.hpp"
|
||||
@@ -215,7 +214,24 @@ namespace tsensor {
|
||||
return -126;
|
||||
}
|
||||
|
||||
return bpmp::GetSharedInfo()->tempAO;
|
||||
u32 regval = 0, abs = 0, fraction = 0, valid = 0, sign = 0;
|
||||
s32 temp = 0;
|
||||
regval = ReadPmcReg(PMC_TSENSOR_STATUS1);
|
||||
valid = REG_GET(regval, STATUS1_TEMP_VALID);
|
||||
|
||||
if (!valid) {
|
||||
return -125;
|
||||
}
|
||||
|
||||
abs = REG_GET(regval, STATUS1_TEMP_ABS);
|
||||
fraction = REG_GET(regval, STATUS1_TEMP_FRAC);
|
||||
sign = REG_GET(regval, STATUS1_TEMP_SIGN);
|
||||
temp = (abs * 1000) + (fraction * 500);
|
||||
if (sign) {
|
||||
temp = (-1) * (temp);
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool IsInitialized() {
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <switch.h>
|
||||
|
||||
#include "../board/board.hpp"
|
||||
#include "../bpmp/bpmp.hpp"
|
||||
#include "../file/file_utils.hpp"
|
||||
#include "../mapping/mem_map.hpp"
|
||||
#include "soctherm.hpp"
|
||||
@@ -414,6 +413,15 @@ namespace tsensor {
|
||||
bool isMariko;
|
||||
} // namespace
|
||||
|
||||
bool IsDisabledThroughSleep() {
|
||||
return (ReadReg(carVa, CLK_RST_CONTROLLER_RST_DEVICES) & SWR_SOC_THERM_RST) ||
|
||||
!(ReadReg(carVa, CLK_RST_CONTROLLER_CLK_OUT_ENB) & CLK_ENB_SOC_THERM);
|
||||
}
|
||||
|
||||
bool IsSensorEnabled() {
|
||||
return ReadReg(socthermVa, TSENSOR_TSENSOR_CLKEN);
|
||||
}
|
||||
|
||||
void EnableSensor(const TSensor *sensor, u32 sensorIdx) {
|
||||
u32 val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT;
|
||||
WriteReg(socthermVa, sensor->base + SENSOR_CONFIG0, val);
|
||||
@@ -426,6 +434,21 @@ namespace tsensor {
|
||||
WriteReg(socthermVa, sensor->base + SENSOR_CONFIG2, calib[sensorIdx]);
|
||||
}
|
||||
|
||||
s32 TranslateTemp(u16 val) {
|
||||
s32 t;
|
||||
|
||||
t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000;
|
||||
if (val & READBACK_ADD_HALF) {
|
||||
t += 500;
|
||||
}
|
||||
|
||||
if (val & READBACK_NEGATE) {
|
||||
t *= -1;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
void StartSensors() {
|
||||
u32 pdiv, hotspot;
|
||||
|
||||
@@ -451,15 +474,23 @@ namespace tsensor {
|
||||
}
|
||||
|
||||
void ReadTSensors(TSensorTemps &temps) {
|
||||
const HocClkBpmpSharedInfo *bpmpInfo = bpmp::GetSharedInfo();
|
||||
if (bpmpInfo->magic != HOCCLK_BPMP_MAGIC) {
|
||||
if (IsDisabledThroughSleep()) {
|
||||
return;
|
||||
}
|
||||
|
||||
temps.cpu = bpmpInfo->tempCpu;
|
||||
temps.gpu = bpmpInfo->tempGpu;
|
||||
temps.pllx = bpmpInfo->tempPllx;
|
||||
temps.mem = (board::GetSocType() == HocClkSocType_Erista) ? bpmpInfo->tempMem : bpmpInfo->tempPllx;
|
||||
if (!IsSensorEnabled()) {
|
||||
StartSensors();
|
||||
}
|
||||
|
||||
temps.cpu = TranslateTemp(ReadReg(socthermVa, SENSOR_TEMP1) >> 16);
|
||||
temps.gpu = TranslateTemp(ReadReg(socthermVa, SENSOR_TEMP1) & SENSOR_TEMP1_GPU_TEMP_MASK);
|
||||
temps.pllx = TranslateTemp(ReadReg(socthermVa, SENSOR_TEMP2) & SENSOR_TEMP2_PLLX_TEMP_MASK);
|
||||
|
||||
if (board::GetSocType() == HocClkSocType_Erista) {
|
||||
temps.mem = TranslateTemp(ReadReg(socthermVa, SENSOR_TEMP2) >> 16);
|
||||
} else {
|
||||
temps.mem = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeSoctherm() {
|
||||
|
||||
Reference in new issue
Block a user