BPMP ABORT!

This commit is contained in:
souldbminersmwc authored and Lightos1 committed 2026-09-23 23:25:51 +02:00
1 parent 9428309ac3
commit ab60e55303
87 files changed
+553 -3213

No files matched your search

+1 -49
View File
@@ -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;
}
+1 -5
View File
@@ -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);
}
-406
View File
@@ -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);
+116
View File
@@ -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 &REG(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
@@ -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();
}
+297
View File
@@ -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;
}
+47
View File
@@ -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
+18 -2
View File
@@ -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() {