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
View File
@@ -8,4 +8,3 @@ build
Source/hekate
dist/*
!dist/.gitignore
bpmpfw.bin
@@ -1,9 +0,0 @@
160c160
< /* Validate that the bpmp is appropriately halted. */
---
> /* Horizon-OC: Halt BPMP here as hoc-clk may be running it, and if it's running there will be a panic. */
161a162,165
> reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
> FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED));
>
> /* Validate that the bpmp is appropriately halted. */
@@ -900,32 +900,6 @@ namespace ams::secmon {
reg::ReadWrite(PMC + APBDEV_PMC_SECURE_SCRATCH39, REG_BITS_VALUE(0, 27, WarmbootCarveoutConfig));
}
void EnableBpmpSmmu() {
/* Define the ASID contents. */
constexpr int BpmpAsid = 1;
constexpr uintptr_t BpmpAsidPde = MemoryRegionPhysicalDeviceSecurityEngine.GetAddress();
/* Configure the ASID. */
reg::Write(MC + MC_SMMU_PTB_ASID, MC_REG_BITS_VALUE(SMMU_PTB_ASID_CURRENT_ASID, BpmpAsid));
reg::Write(MC + MC_SMMU_PTB_DATA, MC_REG_BITS_VALUE(SMMU_PTB_DATA_ASID_PDE_BASE, BpmpAsidPde / 4_KB),
MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_NONSECURE, DISABLE),
MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_WRITABLE, DISABLE),
MC_REG_BITS_ENUM (SMMU_PTB_DATA_ASID_READABLE, DISABLE));
/* Configure the BPMP and PPCS1 to use the asid. */
reg::Write(MC + MC_SMMU_AVPC_ASID, MC_REG_BITS_ENUM(SMMU_AVPC_ASID_AVPC_SMMU_ENABLE, ENABLE), MC_REG_BITS_VALUE(SMMU_AVPC_ASID_AVPC_ASID, BpmpAsid));
reg::Write(MC + MC_SMMU_PPCS1_ASID, MC_REG_BITS_ENUM(SMMU_PPCS1_ASID_PPCS1_SMMU_ENABLE, ENABLE), MC_REG_BITS_VALUE(SMMU_PPCS1_ASID_PPCS1_ASID, BpmpAsid));
/* Flush the entire page table cache, and read TLB_CONFIG to ensure the flush takes. */
reg::Write(MC + MC_SMMU_PTC_FLUSH_0, 0);
reg::Read (MC + MC_SMMU_TLB_CONFIG);
/* Flush the entire translation lookaside buffer, and read TLB_CONFIG to ensure the flush takes. */
reg::Write(MC + MC_SMMU_TLB_FLUSH, 0);
reg::Read (MC + MC_SMMU_TLB_CONFIG);
}
void ValidateResetExpected() {
/* We're coming out of reset, so check that we expected to come out of reset. */
if (!IsResetExpected()) {
@@ -935,11 +909,6 @@ namespace ams::secmon {
SetResetExpected(false);
}
void ActmonInterruptHandler() {
SetError(pkg1::ErrorInfo_ActivityMonitorInterrupt);
AMS_ABORT("actmon observed bpmp wakeup");
}
void ExitChargerHiZMode() {
/* Setup I2c-1. */
pinmux::SetupI2c1();
@@ -1182,39 +1151,6 @@ namespace ams::secmon {
FinalizeCarveoutSecureScratchRegisters();
pmc::LockSecureRegister(pmc::SecureRegister_Carveout);
/* Clear all the BPMP exception vectors to a fixed value. */
constexpr u32 BpmpExceptionVector = 0x7D000000;
reg::Write(EVP + EVP_COP_RESET_VECTOR, BpmpExceptionVector);
reg::Write(EVP + EVP_COP_UNDEF_VECTOR, BpmpExceptionVector);
reg::Write(EVP + EVP_COP_SWI_VECTOR, BpmpExceptionVector);
reg::Write(EVP + EVP_COP_PREFETCH_ABORT_VECTOR, BpmpExceptionVector);
reg::Write(EVP + EVP_COP_DATA_ABORT_VECTOR, BpmpExceptionVector);
reg::Write(EVP + EVP_COP_RSVD_VECTOR, BpmpExceptionVector);
reg::Write(EVP + EVP_COP_IRQ_VECTOR, BpmpExceptionVector);
reg::Write(EVP + EVP_COP_FIQ_VECTOR, BpmpExceptionVector);
/* Disable arbitration for the bpmp. */
reg::ReadWrite(SYSTEM + AHB_ARBITRATION_DISABLE, AHB_REG_BITS_ENUM(ARBITRATION_DISABLE_COP, DISABLE));
/* Turn on the SMMU for the BPMP. */
EnableBpmpSmmu();
/* Wait until the flow controller reports that the BPMP is halted. */
while (!reg::HasValue(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP))) {
util::WaitMicroSeconds(1);
}
/* Enable clock to the activity monitor. */
clkrst::EnableActmonClock();
/* If JTAG is disabled, disable JTAG. */
if (!secmon::IsJtagEnabled()) {
reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_JTAG, DISABLED));
/* Turn on the activity monitor to prevent booting up the bpmp. */
actmon::StartMonitoringBpmp(ActmonInterruptHandler);
}
}
void SetupPmcAndMcSecure() {
@@ -157,12 +157,8 @@ namespace ams::secmon::smc {
/* Validate that all other cores are off. */
AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0)));
/* Horizon-OC: Halt BPMP here as hoc-clk may be running it, and if it's running there will be a panic. */
const bool jtag = IsJtagEnabled();
reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED));
/* Validate that the bpmp is appropriately halted. */
const bool jtag = IsJtagEnabled();
AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, jtag, ENABLED, DISABLED)));
-68
View File
@@ -1,68 +0,0 @@
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/base_rules
TARGET := bpmpfw
BUILDDIR := build
OUTPUTDIR := out
SOURCEDIR := source
LIBCDIR := $(SOURCEDIR)/libc
CPP_SOURCES := $(wildcard $(SOURCEDIR)/*.cpp)
C_SOURCES := $(wildcard $(LIBCDIR)/src/*.c)
S_SOURCES := $(wildcard $(SOURCEDIR)/*.s)
OBJS := $(patsubst $(SOURCEDIR)/%.cpp,$(BUILDDIR)/%.o,$(CPP_SOURCES)) \
$(patsubst $(LIBCDIR)/src/%.c,$(BUILDDIR)/libc/%.o,$(C_SOURCES)) \
$(patsubst $(SOURCEDIR)/%.s,$(BUILDDIR)/%.o,$(S_SOURCES))
ARCH := -march=armv4t -mtune=arm7tdmi -mthumb-interwork
COMMONDIR := ../common/include
INCLUDE := -I$(SOURCEDIR) -I$(LIBCDIR)/include -I$(LIBCDIR)/include/fslc -I$(COMMONDIR)
CFLAGS := $(ARCH) -O2 -g -ffreestanding -nostdlib -ffunction-sections -fdata-sections \
-fomit-frame-pointer -std=gnu11 -Wall -Wextra $(CUSTOMDEFINES)
CXXFLAGS := $(ARCH) -O2 -g -ffreestanding -nostdlib -fno-exceptions -fno-rtti \
-fno-unwind-tables -fno-threadsafe-statics -ffunction-sections -fdata-sections \
-fomit-frame-pointer -std=gnu++20 -Wall -Wextra $(CUSTOMDEFINES)
ASFLAGS := $(ARCH) -g
LDFLAGS := $(ARCH) -nostartfiles -nodefaultlibs -Wl,--nmagic,--gc-sections \
-T $(SOURCEDIR)/../bpmpfw.ld
LDLIBS := -lgcc
BIN_TARGET := $(OUTPUTDIR)/$(TARGET).bin
.PHONY: all clean
all: $(BIN_TARGET)
$(BIN_TARGET): $(BUILDDIR)/$(TARGET).elf
@mkdir -p $(OUTPUTDIR)
@echo "bpmpfw.bin"
@$(OBJCOPY) -S -O binary $< $@
$(BUILDDIR)/$(TARGET).elf: $(OBJS)
@echo "linking $(TARGET).elf"
@$(CXX) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@
$(BUILDDIR)/%.o: $(SOURCEDIR)/%.cpp | $(BUILDDIR)
@echo "$(notdir $<)"
@$(CXX) $(CXXFLAGS) $(INCLUDE) -c $< -o $@
$(BUILDDIR)/libc/%.o: $(LIBCDIR)/src/%.c | $(BUILDDIR)/libc
@echo "$(notdir $<)"
@$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
$(BUILDDIR)/%.o: $(SOURCEDIR)/%.s | $(BUILDDIR)
@echo "$(notdir $<)"
@$(CXX) $(ASFLAGS) $(INCLUDE) -c $< -o $@
$(BUILDDIR):
@mkdir -p $(BUILDDIR)
$(BUILDDIR)/libc: | $(BUILDDIR)
@mkdir -p $(BUILDDIR)/libc
clean:
@rm -rf $(BUILDDIR) $(OUTPUTDIR)
-151
View File
@@ -1,151 +0,0 @@
OUTPUT_ARCH(arm)
ENTRY(reset)
MEMORY
{
bpmpfw : ORIGIN = 0x40004000, LENGTH = 32K
}
SECTIONS
{
PROVIDE(__start__ = ORIGIN(bpmpfw));
. = __start__;
__code_start = . ;
.vectors :
{
KEEP (*(.vectors .vectors.*))
. = ALIGN(8);
} >bpmpfw
.text :
{
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(.text .stub .text.* .gnu.linkonce.t.*)
. = ALIGN(8);
} >bpmpfw
.init :
{
KEEP( *(.init) )
. = ALIGN(8);
} >bpmpfw
.plt :
{
*(.plt)
*(.iplt)
. = ALIGN(8);
} >bpmpfw
.fini :
{
KEEP( *(.fini) )
. = ALIGN(8);
} >bpmpfw
. = ALIGN(8);
__rodata_start = . ;
.rodata :
{
*(.rodata .rodata.* .gnu.linkonce.r.*)
. = ALIGN(8);
} >bpmpfw
.eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >bpmpfw
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >bpmpfw
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >bpmpfw
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >bpmpfw
.hash : { *(.hash) } >bpmpfw
. = ALIGN(8);
__data_start = . ;
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >bpmpfw
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >bpmpfw
.gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >bpmpfw
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >bpmpfw
.preinit_array ALIGN(8) :
{
PROVIDE (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
} >bpmpfw
.init_array ALIGN(8) :
{
PROVIDE (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE (__init_array_end = .);
} >bpmpfw
.fini_array ALIGN(8) :
{
PROVIDE (__fini_array_start = .);
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE (__fini_array_end = .);
} >bpmpfw
.ctors ALIGN(8) :
{
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
} >bpmpfw
.dtors ALIGN(8) :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
} >bpmpfw
__got_start__ = .;
.got : { *(.got) *(.igot) } >bpmpfw
.got.plt : { *(.got.plt) *(.igot.plt) } >bpmpfw
__got_end__ = .;
.data ALIGN(8) :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
} >bpmpfw
__bss_start__ = .;
.bss ALIGN(8) :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(16);
} >bpmpfw
__bss_end__ = .;
__end__ = ABSOLUTE(.) ;
__stack_top = ORIGIN(bpmpfw) + LENGTH(bpmpfw) - 0x10;
/DISCARD/ : { *(.group .comment .note .interp .ARM.attributes) }
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.debug_info 0 : { *(.debug_info) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
}
-65
View File
@@ -1,65 +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 "actmon.hpp"
namespace actmon {
u32 DevReg(ActmonDev dev, u32 off) {
return ActmonDevBase + (static_cast<u32>(dev) * ActmonDevSize) + off;
}
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;
}
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);
}
}
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
-64
View File
@@ -1,64 +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/>.
*
*/
#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;
u32 DevReg(ActmonDev dev, u32 off);
void EnableDev(ActmonDev dev, u32 freqKhz, u32 weight);
void Init();
void Update(HocClkBpmpSharedInfo &info);
} // namespace actmon
-43
View File
@@ -1,43 +0,0 @@
/*
* Copyright (c) 2014 - 2017, NVIDIA CORPORATION. All rights reserved.
* 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 "aotag.hpp"
namespace aotag {
void Update(HocClkBpmpSharedInfo &info) {
const u32 regval = MMIO32(PmcBase + PmcTsensorStatus1);
if (!(regval & Status1ValidBit)) {
info.tempAO = InvalidSentinel;
return;
}
const u32 abs = (regval >> Status1AbsShift) & Status1AbsMask;
const bool frac = (regval & Status1FracBit) != 0;
const bool negative = (regval & Status1SignBit) != 0;
s32 temp = static_cast<s32>(abs) * 1000 + (frac ? 500 : 0);
if (negative) {
temp = -temp;
}
info.tempAO = temp;
}
} // namespace aotag
-39
View File
@@ -1,39 +0,0 @@
/*
* Copyright (c) 2014 - 2017, NVIDIA CORPORATION. All rights reserved.
* 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 aotag {
constexpr u32 PmcBase = 0x7000E400;
constexpr u32 PmcTsensorStatus1 = 0x4AC;
constexpr u32 Status1ValidBit = (1u << 31);
constexpr u32 Status1AbsShift = 8;
constexpr u32 Status1AbsMask = 0xFF;
constexpr u32 Status1FracBit = (1u << 7);
constexpr u32 Status1SignBit = (1u << 0);
constexpr s32 InvalidSentinel = -125;
void Update(HocClkBpmpSharedInfo &info);
} // namespace aotag
-96
View File
@@ -1,96 +0,0 @@
/*
* 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/>.
*
*/
#include "bpmp_mmu.hpp"
namespace bpmpMmu {
u32 AlignUp(u32 x, u32 a) {
return (x + a - 1) & ~(a - 1);
}
u32 AlignDown(u32 x, u32 a) {
return x & ~(a - 1);
}
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);
}
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);
}
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);
}
void Disable() {
if (!(MMIO32(CacheBase + CacheConfig) & CfgEnableCache)) {
return;
}
// Clean and invalidate cache.
Maintenance(MaintCleanInvalidWay, false);
// Disable cache.
MMIO32(CacheBase + CacheConfig) = 0;
}
} // namespace bpmpMmu
-91
View File
@@ -1,91 +0,0 @@
/*
* 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 },
};
u32 AlignUp(u32 x, u32 a);
u32 AlignDown(u32 x, u32 a);
void Maintenance(u32 op, bool force);
void SetEntry(u32 idx, const MmuEntry &entry);
void Enable();
void Disable();
} // namespace bpmpMmu
-28
View File
@@ -1,28 +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/>.
*
*/
#pragma once
#include <cstdint>
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
-61
View File
@@ -1,61 +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 "freq.hpp"
namespace freq {
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;
}
void Update(HocClkBpmpSharedInfo &info) {
info.freqMem = static_cast<u32>(MeasurePtoFreqHz(ClkPtoEmc, 1) / 1000);
info.freqCpu = static_cast<u32>(MeasurePtoFreqHz(ClkPtoCclkGDiv2, 2) / 1000);
}
} // namespace freq
-44
View File
@@ -1,44 +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/>.
*
*/
#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;
u64 MeasurePtoFreqHz(u32 srcId, u32 multiplier);
void Update(HocClkBpmpSharedInfo &info);
} // namespace freq
-21
View File
@@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015-2016 Jurģis Brigmanis <velko.x@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,6 +0,0 @@
#ifndef _STD_ASSERT_H
#define _STD_ASSERT_H
#include "fslc/fslc_assert.h"
#endif /* _STD_ASSERT_H */
@@ -1,35 +0,0 @@
#ifndef FSLC_ASSERT_H
#define FSLC_ASSERT_H
#ifndef ALT_FSLC_NAMES
#define fslc_assert assert
#endif /* ALT_FSLC_NAMES */
#ifdef NDEBUG
#define fslc_assert(expr) ((void)0)
#else /* NDEBUG */
/* Note: __PRETTY_FUNCTION__ is GCC specific */
#define fslc_assert(expr) ((expr) ? (void)0 : __fslc_assert_fail(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__))
#endif /* NDEBUG */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void __fslc_assert_fail(const char *expr, const char *file, unsigned int line, const char *func);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* FSLC_ASSERT_H */
@@ -1,74 +0,0 @@
#ifndef FSLC_STDIO_H
#define FSLC_STDIO_H
#include <stdarg.h>
#include <stddef.h>
#ifndef ALT_FSLC_NAMES
#define FSLC_FILE FILE
#define fslc_stdout stdout
#define fslc_stdin stdin
#define fslc_putchar putchar
#define fslc_fputc fputc
#define fslc_fputs fputs
#define fslc_puts puts
#define fslc_vfprintf vfprintf
#define fslc_vprintf vprintf
#define fslc_fprintf fprintf
#define fslc_printf printf
#define fslc_fwrite fwrite
#define fslc_fread fread
#define fslc_getc getc
#define fslc_getchar getchar
#define fslc_ungetc ungetc
#define fslc_fgets fgets
#endif /* ALT_FSLC_NAMES */
typedef struct _FSLC_FILE FSLC_FILE;
struct _FSLC_FILE
{
void *user_ptr;
int (*putc)(int c, FSLC_FILE *stream);
int (*getc)(FSLC_FILE *stream);
void (*pre_output)(FSLC_FILE *stream);
void (*post_output)(FSLC_FILE *stream);
int ungetc_buf;
};
extern FSLC_FILE *fslc_stdout;
extern FSLC_FILE *fslc_stdin;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int fslc_putchar(int c);
int fslc_fputc(int c, FSLC_FILE *stream);
int fslc_fputs(const char *str, FSLC_FILE *stream);
int fslc_puts(const char *str);
int fslc_vfprintf(FSLC_FILE *stream, const char *format, va_list arg);
int fslc_vprintf(const char *format, va_list arg);
int fslc_fprintf(FSLC_FILE *stream, const char *format, ...);
int fslc_printf(const char *format, ...);
size_t fslc_fwrite(const void *ptr, size_t size, size_t count, FSLC_FILE *stream);
size_t fslc_fread(void *ptr, size_t size, size_t count, FSLC_FILE *stream);
int fslc_getc(FSLC_FILE *stream);
int fslc_getchar(void);
int fslc_ungetc(int c, FSLC_FILE *stream);
char *fslc_fgets(char *str, int num, FSLC_FILE *stream);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* FSLC_STDIO_H */
@@ -1,25 +0,0 @@
#ifndef FSLC_STDLIB_H
#define FSLC_STDLIB_H
#include <stddef.h>
#ifndef ALT_FSLC_NAMES
#define fslc_bsearch bsearch
#endif /* ALT_FSLC_NAMES */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void *fslc_bsearch(const void *key, const void *base, size_t num, size_t size, int (*cmp_proc)(const void *, const void *));
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* FSLC_STDLIB_H */
@@ -1,50 +0,0 @@
#ifndef FSLC_STRING_H
#define FSLC_STRING_H
#include <stddef.h>
#ifndef ALT_FSLC_NAMES
#define fslc_memset memset
#define fslc_memcpy memcpy
#define fslc_memmove memmove
#define fslc_memcmp memcmp
#define fslc_strlen strlen
#define fslc_strcpy strcpy
#define fslc_strncpy strncpy
#define fslc_strcmp strcmp
#define fslc_strncmp strncmp
#define fslc_strchr strchr
#define fslc_strstr strstr
#define fslc_strpbrk strpbrk
#define fslc_strspn strspn
#define fslc_strtok_r strtok_r
#endif /* ALT_FSLC_NAMES */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void *fslc_memset(void *ptr, int value, size_t num);
void *fslc_memcpy(void *dest, const void *src, size_t len);
void *fslc_memmove(void *dest, const void *src, size_t len);
int fslc_memcmp(const void *ptr1, const void *ptr2, size_t num);
size_t fslc_strlen(const char *str);
char *fslc_strcpy(char *dest, const char *src);
char *fslc_strncpy(char *dest, const char *src, size_t len);
int fslc_strcmp(const char *str1, const char *str2);
int fslc_strncmp(const char *str1, const char *str2, size_t num);
char *fslc_strchr(const char *str, int c);
char *fslc_strstr(const char *search_in, const char *search_for);
char *fslc_strpbrk(const char *str, const char *delim);
size_t fslc_strspn(const char *str, const char *delim);
char *fslc_strtok_r(char *str, const char *delim, char **save_p);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* FSLC_STRING_H */
@@ -1,23 +0,0 @@
#ifndef FSLC_STDLIBX_H
#define FSLC_STDLIBX_H
#include <stddef.h>
#ifndef ALT_FSLC_NAMES
#define fslc_bsearch_i bsearch_i
#endif /* ALT_FSLC_NAMES */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int fslc_bsearch_i(const void *key, const void *base, size_t num, size_t size, int (*cmp_proc)(const void *, const void *));
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* FSLC_STDLIBX_H */
@@ -1,33 +0,0 @@
#ifndef FSLC_STRINGX_H
#define FSLC_STRINGX_H
#include <stddef.h>
#ifndef ALT_FSLC_NAMES
#define fslc_memset_l memset_l
#define fslc_strcpy_e strcpy_e
#define fslc_strncpy_e strncpy_e
#endif /* ALT_FSLC_NAMES */
#if __SIZEOF_LONG__ == 4
#define memset_32 memset_l
#elif __SIZEOF_LONG__ == 8
#define memset_32(A, V, S) memset_l(A, (((unsigned long)(V)<<32)|(V)), S)
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void *fslc_memset_l(void *ptr, unsigned long value, size_t num);
char *fslc_strcpy_e(char *dest, const char *src);
char *fslc_strncpy_e(char *dest, const char *src, size_t len);
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* FSLC_STRINGX_H */
@@ -1,6 +0,0 @@
#ifndef _STD_STDIO_H
#define _STD_STDIO_H
#include "fslc/fslc_stdio.h"
#endif /* _STD_STDIO_H */
@@ -1,6 +0,0 @@
#ifndef _STD_STDLIB_H
#define _STD_STDLIB_H
#include "fslc/fslc_stdlib.h"
#endif /* _STD_STDLIB_H */
@@ -1,6 +0,0 @@
#ifndef _STD_STRING_H
#define _STD_STRING_H
#include "fslc/fslc_string.h"
#endif /* _STD_STRING_H */
@@ -1,12 +0,0 @@
#include "fslc_stdlib.h"
#include "stdlibx.h"
void *fslc_bsearch(const void *key, const void *base, size_t num, size_t size, int (*cmp_proc)(const void *, const void *))
{
int sidx = fslc_bsearch_i(key, base, num, size, cmp_proc);
if (sidx < 0)
return NULL;
return (char *)base + (sidx * size);
}
@@ -1,31 +0,0 @@
#include <stddef.h>
int fslc_bsearch_i(const void *key, const void *base, size_t num, size_t size, int (*cmp_proc)(const void *, const void *))
{
int lo = 0;
int hi = num - 1;
char *cbase = (char *)base;
while (lo <= hi)
{
int i = lo + ((hi - lo) >> 1);
char *ptr = cbase + (i * size);
int cmp = cmp_proc(key, ptr);
if (cmp == 0)
return i;
if (cmp > 0)
{
lo = i + 1;
}
else
{
hi = i - 1;
}
}
return ~lo;
}
@@ -1,10 +0,0 @@
#include "fslc_stdio.h"
int fslc_fprintf(FSLC_FILE *stream, const char *format, ...)
{
va_list args;
va_start(args, format);
int r = fslc_vfprintf(stream, format, args);
va_end(args);
return r;
}
@@ -1,12 +0,0 @@
#include "fslc_stdio.h"
int fslc_fputc(int c, FSLC_FILE *stream)
{
if (stream->pre_output) stream->pre_output(stream);
int r = stream->putc(c, stream);
if (stream->post_output) stream->post_output(stream);
return r;
}
@@ -1,15 +0,0 @@
#include "fslc_stdio.h"
int _fslc_fputs_impl(const char *str, FSLC_FILE *stream);
int fslc_fputs(const char *str, FSLC_FILE *stream)
{
if (stream->pre_output) stream->pre_output(stream);
int r = _fslc_fputs_impl(str, stream);
if (stream->post_output) stream->post_output(stream);
return r;
}
@@ -1,13 +0,0 @@
#include "fslc_stdio.h"
int _fslc_fputs_impl(const char *str, FSLC_FILE *stream)
{
int res = 0;
for (;*str; ++str)
{
int pr = stream->putc(*str, stream);
if (pr < 0) return pr;
++res;
}
return res;
}
@@ -1,38 +0,0 @@
#include "fslc_string.h"
#include <stdint.h>
int fslc_memcmp(const void *ptr1, const void *ptr2, size_t num)
{
unsigned char *b_p1 = (unsigned char *)ptr1;
unsigned char *b_p2 = (unsigned char *)ptr2;
for (;((uintptr_t)b_p1 & (sizeof(long)-1)) && num > 0; ++b_p1, ++b_p2, --num)
{
if (*b_p1 != *b_p2)
{
return *b_p1 > *b_p2 ? 1 : -1;
}
}
unsigned long *w_p1 = (unsigned long *)b_p1;
unsigned long *w_p2 = (unsigned long *)b_p2;
for (;num >= sizeof(unsigned long); ++w_p1, ++w_p2, num -= sizeof(unsigned long))
{
if (*w_p1 != *w_p2)
{
return *w_p1 > *w_p2 ? 1 : -1;
}
}
b_p1 = (unsigned char *)w_p1;
b_p2 = (unsigned char *)w_p2;
for (; num > 0; ++b_p1, ++b_p2, --num)
{
if (*b_p1 != *b_p2)
{
return *b_p1 > *b_p2 ? 1 : -1;
}
}
return 0;
}
@@ -1,40 +0,0 @@
#include "fslc_string.h"
#include <stdint.h>
void *fslc_memcpy(void *dest, const void *src, size_t len)
{
/* Copy byte-by-byte up to word-aligned.
*
* There's a possibility, that src and dest have different offsets
* to word boundary and can not both be used efficiently.
*
* In this case we use align to dest argument, because it then can be
* written in one operation, instead of read-modify-write of 2 words.
*
* Most of the time both parameters should be word-aligned, however.
*/
unsigned char *b_src = (unsigned char *)src;
unsigned char *b_dst = (unsigned char *)dest;
for (;((uintptr_t)b_dst & (sizeof(unsigned long)-1)) && len > 0; b_src++, b_dst++, len--)
{
*b_dst = *b_src;
}
/* Then all word-aligned */
unsigned long *w_src = (unsigned long*)b_src;
unsigned long *w_dst = (unsigned long*)b_dst;
for (;len >= sizeof(unsigned long); w_src++, w_dst++, len -= sizeof(unsigned long))
{
*w_dst = *w_src;
}
/* And byte-by-byte the rest */
b_src = (unsigned char *)w_src;
b_dst = (unsigned char *)w_dst;
for (; len > 0; b_src++, b_dst++, len--)
{
*b_dst = *b_src;
}
return dest;
}
@@ -1,69 +0,0 @@
#include "fslc_string.h"
#include <stdint.h>
void *fslc_memmove(void *dest, const void *src, size_t len)
{
unsigned char *b_dst = (unsigned char *)dest;
unsigned char *b_src = (unsigned char *)src;
/* Need to check if memory regions overlaps. Then if the dest address is
* larger than source, we need to copy backwards (or else source regions
* ending will be overwritten by writes to dest, before they are read).
*
* In other cases it works identically as memcpy(). Same considerations
* about address alignment applies here.
*/
if ( b_dst < b_src + len && b_dst > b_src)
{
b_src += len;
b_dst += len;
/* Byte-by-byte down to word-aligned. */
for (;((uintptr_t)b_dst & (sizeof(unsigned long)-1)) && len > 0; )
{
b_src--, b_dst--, len--;
*b_dst = *b_src;
}
/* Then all word-aligned */
unsigned long *w_src = (unsigned long*)b_src;
unsigned long *w_dst = (unsigned long*)b_dst;
for (;len >= sizeof(unsigned long); )
{
w_src--, w_dst--, len -= sizeof(unsigned long);
*w_dst = *w_src;
}
/* And byte-by-byte the rest */
b_src = (unsigned char *)w_src;
b_dst = (unsigned char *)w_dst;
for (; len > 0; )
{
b_src--, b_dst--, len--;
*b_dst = *b_src;
}
} else {
/* Byte-by-byte up to word-aligned. */
for (;((uintptr_t)b_dst & (sizeof(unsigned long)-1)) && len > 0; b_src++, b_dst++, len--)
{
*b_dst = *b_src;
}
/* Then all word-aligned */
unsigned long *w_src = (unsigned long *)b_src;
unsigned long *w_dst = (unsigned long *)b_dst;
for (;len >= sizeof(unsigned long); w_src++, w_dst++, len -= sizeof(unsigned long))
{
*w_dst = *w_src;
}
/* And byte-by-byte the rest */
b_src = (unsigned char *)w_src;
b_dst = (unsigned char *)w_dst;
for (; len > 0; b_src++, b_dst++, len--)
{
*b_dst = *b_src;
}
}
return dest;
}
@@ -1,22 +0,0 @@
#include "fslc_string.h"
#include "stringx.h"
void *fslc_memset(void *ptr, int value, size_t num)
{
/* Will use memset_l() to actually fill the memory region. If value is non-zero, we need
* to replicate lowest byte to all the long's bytes.
*/
if (value)
{
unsigned char c_val = value;
unsigned long l_val = (c_val << 0) | (c_val << 8) | (c_val << 16) | (c_val << 24);
#if __SIZEOF_LONG__ == 8
l_val |= l_val << 32;
#endif
return fslc_memset_l(ptr, l_val, num);
}
else
return fslc_memset_l(ptr, value, num);
}
@@ -1,31 +0,0 @@
#include "stringx.h"
#include <stdint.h>
void *fslc_memset_l(void *ptr, unsigned long value, size_t num)
{
unsigned char *b_src = (unsigned char *)&value;
unsigned char *b_dst = (unsigned char *)ptr;
/* Byte by byte - to aligned word address */
b_src += ((uintptr_t)b_dst & (sizeof(unsigned long)-1));
for (;((uintptr_t)b_dst & (sizeof(unsigned long)-1)) && num > 0; b_dst++,b_src++,num--)
{
*b_dst = *b_src;
}
/* All aligned */
unsigned long *w_dst = (unsigned long *)b_dst;
for (;num >= sizeof(unsigned long); w_dst++,num -= sizeof(unsigned long))
{
*w_dst= value;
}
/* Unaligned ending */
b_src = (unsigned char *)&value;
b_dst = (unsigned char *)w_dst;
for (; num > 0; b_dst++,b_src++,num--)
{
*b_dst = *b_src;
}
return ptr;
}
@@ -1,10 +0,0 @@
#include "fslc_stdio.h"
int fslc_printf(const char *format, ...)
{
va_list args;
va_start(args, format);
int r = fslc_vfprintf(fslc_stdout, format, args);
va_end(args);
return r;
}
@@ -1,6 +0,0 @@
#include "fslc_stdio.h"
int fslc_putchar(int c)
{
return fslc_fputc(c, fslc_stdout);
}
@@ -1,21 +0,0 @@
#include "fslc_stdio.h"
int _fslc_fputs_impl(const char *str, FSLC_FILE *stream);
int fslc_puts(const char *str)
{
if (fslc_stdout->pre_output) fslc_stdout->pre_output(fslc_stdout);
int r = _fslc_fputs_impl(str, fslc_stdout);
if (r >= 0)
{
int nlr = fslc_stdout->putc('\n', fslc_stdout);
if (nlr < 0)
r = nlr;
}
if (fslc_stdout->post_output) fslc_stdout->post_output(fslc_stdout);
return r;
}
@@ -1,3 +0,0 @@
#include "fslc_stdio.h"
FSLC_FILE *fslc_stdout;
@@ -1,10 +0,0 @@
#include "fslc_string.h"
char *fslc_strchr(const char *str, int c)
{
for (;*str; ++str)
if (*str == c)
return (char *)str;
return NULL;
}
@@ -1,8 +0,0 @@
#include "fslc_string.h"
int fslc_strcmp(const char *str1, const char *str2)
{
for( ; *str1 && *str1 == *str2; ++str1, ++str2);
return *((unsigned char *)str1) - *((unsigned char *)str2);
}
@@ -1,8 +0,0 @@
#include "fslc_string.h"
#include "stringx.h"
char *fslc_strcpy(char *dest, const char *src)
{
fslc_strcpy_e(dest, src);
return dest;
}
@@ -1,10 +0,0 @@
#include "stringx.h"
char *fslc_strcpy_e(char *dest, const char *src)
{
for(; *src; ++src, ++dest)
*dest = *src;
*dest = 0;
return dest;
}
@@ -1,9 +0,0 @@
#include "fslc_string.h"
size_t fslc_strlen(const char *str)
{
size_t res = 0;
for(; *str; ++str) ++res;
return res;
}
@@ -1,8 +0,0 @@
#include "fslc_string.h"
int fslc_strncmp(const char *str1, const char *str2, size_t num)
{
for( ; *str1 && *str1 == *str2 && num > 1; ++str1, ++str2, --num);
return *((unsigned char *)str1) - *((unsigned char *)str2);
}
@@ -1,9 +0,0 @@
#include "fslc_string.h"
char *_fslc_strncpy_impl(char *dest, const char *src, size_t len);
char *fslc_strncpy(char *dest, const char *src, size_t len)
{
_fslc_strncpy_impl(dest, src, len);
return dest;
}
@@ -1,9 +0,0 @@
#include "stringx.h"
char *_fslc_strncpy_impl(char *dest, const char *src, size_t len);
char *fslc_strncpy_e(char *dest, const char *src, size_t len)
{
char *r = _fslc_strncpy_impl(dest, src, len);
return *r ? r + 1: r;
}
@@ -1,10 +0,0 @@
#include <stddef.h>
char *_fslc_strncpy_impl(char *dest, const char *src, size_t len)
{
for(; *src && len > 1; ++src, ++dest, --len)
*dest = *src;
*dest = *src;
return dest;
}
@@ -1,14 +0,0 @@
#include "fslc_string.h"
char *fslc_strpbrk(const char *str, const char *delim)
{
for (; *str; ++str)
{
const char *d;
for (d = delim; *d; ++d)
if (*str == *d)
return (char *)str;
}
return NULL;
}
@@ -1,18 +0,0 @@
#include "fslc_string.h"
size_t fslc_strspn(const char *str, const char *delim)
{
size_t r;
for (r = 0; *str; ++str, ++r)
{
const char *d;
for (d = delim; *d; ++d)
if (*str == *d) // skip to next if found
break;
if (*d == 0) // inner loop exited on \0 - char was not there
break;
}
return r;
}
@@ -1,23 +0,0 @@
#include "fslc_string.h"
char *fslc_strstr(const char *search_in, const char *search_for)
{
while (*search_in)
{
char *rc = fslc_strchr(search_in, search_for[0]); // find first char
if (rc)
{
const char *si = rc + 1, *sf = search_for + 1; // search strcmp-style from second char
for( ; *si && *si == *sf; ++si, ++sf);
if (*sf == 0) return rc; // end of search-for - found result
search_in = rc + 1; // not this one, continue from next char
}
else
return NULL; // did not find first char - exit
}
return NULL; // reached end of source - did not find
}
@@ -1,27 +0,0 @@
#include "fslc_string.h"
char *fslc_strtok_r(char *str, const char *delim, char **save_p)
{
if (str)
*save_p = str;
if (*save_p == NULL)
return NULL;
char *r = *save_p + fslc_strspn(*save_p, delim);
char *p = fslc_strpbrk(r, delim);
if (p)
{
*(p++) = 0;
*save_p = p;
}
else
{
if (*r == 0) r = NULL;
*save_p = NULL;
}
return r;
}
@@ -1,347 +0,0 @@
#include "fslc_stdio.h"
/* There'a a funny behaviour of va_list and va_arg() macros:
* - on x86_64 compiler generates a branch of some sort for each va_arg() macro. Normally it is not a problem,
* however it kinda screws up branch coverage analysis. Therefore I decided to move va_arg() accesses into
* dedicated functions, minimizing the issue to one untaken branch per access function.
* - passing va_list by value works fine on x86_64, but makes some tests to fail on i386. It seems that modifications
* done by va_arg() is not passed back to caller. Subsequent calls to va_arg() or access functions returns wrong
* values on that platform.
* - OK, then. Let's pass pointer to va_list instead. Not so fast. Works fine on i386, but fails to compile on
* x86_64. Fine! Wrapping va_list into a dedicated struct and passing around pointer to it.
*
* Why such a fuss about a few untaken branches in tests? Well, just because! Also, if I ever decide to refactor
* _fslc_vfprintf_impl() - the case statement is getting a bit long and messy, it should make things easier if
* argument list can be passed around freely (as opposed to using it from single function).
*/
struct va_list_w
{
va_list arg;
};
/* Prototype for printf's internal implementation */
static int _fslc_vfprintf_impl(FSLC_FILE *stream, const char *format, struct va_list_w *args);
/* Main exported function */
int fslc_vfprintf(FSLC_FILE *stream, const char *format, va_list arg)
{
if (stream->pre_output) stream->pre_output(stream);
struct va_list_w argw;
va_copy(argw.arg, arg); // can not assign directly, should make a copy
int res = _fslc_vfprintf_impl(stream, format, &argw);
va_end(argw.arg); // and free it after use
if (stream->post_output) stream->post_output(stream);
return res;
}
/* Prototype to private FSLIBC function - implemented elsewhere */
int _fslc_fputs_impl(const char *str, FSLC_FILE *stream);
/* Prototypes for internal functions */
static int _fslc_put_sint_l(signed long v, FSLC_FILE *stream);
static int _fslc_put_uint_l(unsigned long v, FSLC_FILE *stream);
static int _fslc_put_hex_l(unsigned long v, FSLC_FILE *stream, char alpha);
static int _get_sint_arg(struct va_list_w *arg);
static unsigned _get_uint_arg(struct va_list_w *arg);
static long long _get_slonglong_arg(struct va_list_w *arg);
static unsigned long long _get_ulonglong_arg(struct va_list_w *arg);
static void *_get_ptr_arg(struct va_list_w *arg);
/* There are some considerations when formatting Integers for output:
* - on 32-bit systems integers will mostly be 32 bits long, except when one REALLY
* wants to print long long. It should be more optimal to use 32-bit calculations
* when possible and invoke 64-bit only when needed. To support that we provide 2
* separate implementations.
* - on 64-bit systems may be 32 or 64 bits, but there is no real benefit from
* using shorter bit size. Hence common implementation.
* - this library does not aim to support systems with shorter integer sizes (like 16-bit)
* but it it did, it would probably make sense to provide optimized ports for these cases
* as well.
*
* Buffer size calculations:
* Max UInt32 fits in 10 bytes (+1 for zero terminator) decimal
* 2**32 - 1 == 4294967295z
* FFFFFFFFz
* 01234567890
*
* Max UInt64 fits in 10 bytes (and +1 for \0)
* 2**64 -1 == 18446744073709551615z
* FFFFFFFFFFFFFFFFz
* 012345678901234567890
*/
#define BUFSIZE_LONG_LONG_DECIMAL 20
#define BUFSIZE_LONG_HEX (__SIZEOF_LONG__ * 2)
#define BUFSIZE_LONG_LONG_HEX (__SIZEOF_LONG_LONG__ * 2)
#if __SIZEOF_LONG__ == 4
#define BUFSIZE_LONG_DECIMAL 10
static int _fslc_put_sint_ll(signed long long v, FSLC_FILE *stream);
static int _fslc_put_uint_ll(unsigned long long v, FSLC_FILE *stream);
static int _fslc_put_hex_ll(unsigned long long v, FSLC_FILE *stream, char alpha);
#define _get_slong_arg _get_sint_arg
#define _get_ulong_arg _get_uint_arg
#elif __SIZEOF_LONG__ == 8
#define BUFSIZE_LONG_DECIMAL BUFSIZE_LONG_LONG_DECIMAL
#define _fslc_put_sint_ll _fslc_put_sint_l
#define _fslc_put_uint_ll _fslc_put_uint_l
#define _fslc_put_hex_ll _fslc_put_hex_l
#define _get_slong_arg _get_slonglong_arg
#define _get_ulong_arg _get_ulonglong_arg
#endif
#define FLAG_LONG 1
#define FLAG_VERYLONG 3
static int _fslc_vfprintf_impl(FSLC_FILE *stream, const char *format, struct va_list_w *arg)
{
int res = 0;
int pr;
const char *c;
for (c = format; *c; ++c)
{
if (*c == '%')
{
int flags = 0;
for (;;)
{
++c;
switch (*c)
{
case 's':
pr = _fslc_fputs_impl((const char *)_get_ptr_arg(arg), stream);
if (pr < 0) return pr;
res += pr;
break;
case 'c':
pr = stream->putc(_get_sint_arg(arg), stream);
if (pr < 0) return pr;
++res;
break;
case '%':
pr = stream->putc('%', stream);
if (pr < 0) return pr;
++res;
break;
case 'i':
case 'd':
if ((flags & FLAG_VERYLONG) == FLAG_VERYLONG)
pr = _fslc_put_sint_ll(_get_slonglong_arg(arg), stream);
else if (flags & FLAG_LONG)
pr = _fslc_put_sint_l(_get_slong_arg(arg), stream);
else
pr = _fslc_put_sint_l(_get_sint_arg(arg), stream);
if (pr < 0) return pr;
res += pr;
break;
case 'u':
if ((flags & FLAG_VERYLONG) == FLAG_VERYLONG)
pr = _fslc_put_uint_ll(_get_ulonglong_arg(arg), stream);
else if (flags & FLAG_LONG)
pr = _fslc_put_uint_l(_get_ulong_arg(arg), stream);
else
pr = _fslc_put_uint_l(_get_uint_arg(arg), stream);
if (pr < 0) return pr;
res += pr;
break;
case 'x':
if ((flags & FLAG_VERYLONG) == FLAG_VERYLONG)
pr = _fslc_put_hex_ll(_get_ulonglong_arg(arg), stream, 'a');
else if (flags & FLAG_LONG)
pr = _fslc_put_hex_l(_get_ulong_arg(arg), stream, 'a');
else
pr = _fslc_put_hex_l(_get_uint_arg(arg), stream, 'a');
if (pr < 0) return pr;
res += pr;
break;
case 'X':
if ((flags & FLAG_VERYLONG) == FLAG_VERYLONG)
pr = _fslc_put_hex_ll(_get_ulonglong_arg(arg), stream, 'A');
else if (flags & FLAG_LONG)
pr = _fslc_put_hex_l(_get_ulong_arg(arg), stream, 'A');
else
pr = _fslc_put_hex_l(_get_uint_arg(arg), stream, 'A');
if (pr < 0) return pr;
res += pr;
break;
case 'p':
pr = _fslc_fputs_impl("0x", stream);
if (pr < 0) return pr;
res += pr;
pr = _fslc_put_hex_l(_get_ulong_arg(arg), stream, 'a');
if (pr < 0) return pr;
res += pr;
break;
case 'l':
if (flags & FLAG_LONG)
flags |= FLAG_VERYLONG;
else
flags |= FLAG_LONG;
continue;
}
break;
}
} else {
pr = stream->putc(*c, stream);
if (pr < 0) return pr;
++res;
}
}
return res;
}
static int _get_sint_arg(struct va_list_w *arg)
{
return va_arg(arg->arg, signed int);
}
static unsigned _get_uint_arg(struct va_list_w *arg)
{
return va_arg(arg->arg, unsigned int);
}
static long long _get_slonglong_arg(struct va_list_w *arg)
{
return va_arg(arg->arg, signed long long);
}
static unsigned long long _get_ulonglong_arg(struct va_list_w *arg)
{
return va_arg(arg->arg, unsigned long long);
}
static void *_get_ptr_arg(struct va_list_w *arg)
{
return va_arg(arg->arg, void *);
}
static int _fslc_put_sint_l(signed long v, FSLC_FILE *stream)
{
if (v < 0)
{
int pr = stream->putc('-', stream);
if (pr < 0) return pr;
pr = _fslc_put_uint_l(-v, stream);
if (pr < 0) return pr;
return pr+1;
} else
return _fslc_put_uint_l(v, stream);
}
static int _fslc_put_uint_l(unsigned long v, FSLC_FILE *stream)
{
char dbuff[BUFSIZE_LONG_DECIMAL+1], *p;
dbuff[BUFSIZE_LONG_DECIMAL] = 0;
for (p = dbuff + BUFSIZE_LONG_DECIMAL; v ; v /= 10)
{
*(--p) = (v % 10) + '0';
}
if (*p == 0) *(--p) = '0';
return _fslc_fputs_impl(p, stream);
}
static int _fslc_put_hex_l(unsigned long v, FSLC_FILE *stream, char alpha)
{
char dbuff[BUFSIZE_LONG_HEX+1], *p;
dbuff[BUFSIZE_LONG_HEX] = 0;
for (p = dbuff + BUFSIZE_LONG_HEX; v ; v >>= 4)
{
char digit = (v & 0xF);
*(--p) = digit < 10 ? digit + '0' : digit - 10 + alpha;
}
if (*p == 0) *(--p) = '0';
return _fslc_fputs_impl(p, stream);
}
#if __SIZEOF_LONG__ == 4
static int _fslc_put_sint_ll(signed long long v, FSLC_FILE *stream)
{
if (v < 0)
{
int pr = stream->putc('-', stream);
if (pr < 0) return pr;
pr = _fslc_put_uint_ll(-v, stream);
if (pr < 0) return pr;
return pr+1;
} else
return _fslc_put_uint_ll(v, stream);
}
static int _fslc_put_uint_ll(unsigned long long v, FSLC_FILE *stream)
{
char dbuff[BUFSIZE_LONG_LONG_DECIMAL+1], *p;
dbuff[BUFSIZE_LONG_LONG_DECIMAL] = 0;
for (p = dbuff + BUFSIZE_LONG_LONG_DECIMAL; v ; v /= 10)
{
*(--p) = (v % 10) + '0';
}
if (*p == 0) *(--p) = '0';
return _fslc_fputs_impl(p, stream);
}
static int _fslc_put_hex_ll(unsigned long long v, FSLC_FILE *stream, char alpha)
{
char dbuff[BUFSIZE_LONG_LONG_HEX+1], *p;
dbuff[BUFSIZE_LONG_LONG_HEX] = 0;
for (p = dbuff + BUFSIZE_LONG_LONG_HEX; v ; v >>= 4)
{
char digit = (v & 0xF);
*(--p) = digit < 10 ? digit + '0' : digit - 10 + alpha;
}
if (*p == 0) *(--p) = '0';
return _fslc_fputs_impl(p, stream);
}
#endif
@@ -1,6 +0,0 @@
#include "fslc_stdio.h"
int fslc_vprintf(const char *format, va_list arg)
{
return fslc_vfprintf(fslc_stdout, format, arg);
}
@@ -1,39 +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 <stdio.h>
#include "regs.hpp"
namespace {
int UartStreamPutc(int c, FILE *) {
UartPutc(static_cast<char>(c));
return c;
}
void UartStreamPostOutput(FILE *) {
UartFlush();
}
FILE g_uartStdout = { nullptr, UartStreamPutc, nullptr, nullptr, UartStreamPostOutput, -1 };
} // namespace
void InitializeLibc() {
stdout = &g_uartStdout;
}
-111
View File
@@ -1,111 +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 <stdio.h>
#include <string.h>
#include "actmon.hpp"
#include "aotag.hpp"
#include "bpmp_mmu.hpp"
#include "freq.hpp"
#include "libc_platform.hpp"
#include "pllmb.hpp"
#include "regs.hpp"
#include "sensors.hpp"
#include <hocclk/bpmp.h>
extern "C" [[noreturn]] void _panic_reboot();
namespace {
HocClkBpmpSharedInfo &SharedInfo() {
return *reinterpret_cast<HocClkBpmpSharedInfo *>(WorkRamStart);
}
} // namespace
[[noreturn]] void HandleShutdown(HocClkBpmpSharedInfo &info) {
/* Deinit MMU otherwise sleep will fatal. */
bpmpMmu::Disable();
info.magic = 0x0;
UartPutsForce("[hoc-bpmpfw]: Halting...\n");
for (;;)
;
}
void HandleCommand(HocClkBpmpSharedInfo &info) {
switch (info.cmd) {
case HocClkBpmpCmd_None:
break;
case HocClkBpmpCmd_RequestShutdown:
HandleShutdown(info);
break;
case HocClkBpmpCmd_SetUartEnabled:
g_uartLoggingEnabled = (info.cmdArg1 != 0);
info.cmd = HocClkBpmpCmd_None;
break;
default:
info.cmd = HocClkBpmpCmd_None;
break;
}
}
void MainLoop(HocClkBpmpSharedInfo &info) {
for (;;) {
HandleCommand(info);
const u32 temp1 = MMIO32(SocthermBase + SocthermSensorTemp1);
const u32 temp2 = MMIO32(SocthermBase + SocthermSensorTemp2);
info.tempCpu = TranslateSocthermTemp(static_cast<u16>(temp1 >> 16));
info.tempGpu = TranslateSocthermTemp(static_cast<u16>(temp1));
info.tempMem = TranslateSocthermTemp(static_cast<u16>(temp2 >> 16));
info.tempPllx = TranslateSocthermTemp(static_cast<u16>(temp2));
freq::Update(info);
actmon::Update(info);
aotag::Update(info);
info.freqMemPll = static_cast<u32>(pllmb::MeasureRamClockHz() / 1000);
msleep(250);
}
}
extern "C" void main() {
bpmpMmu::Enable();
InitializeLibc();
UartPutsForce("[hoc-bpmpfw]: Starting bpmpfw\n");
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 */
MainLoop(info);
UartPutsForce("[hoc-bpmpfw]: PANIC\n");
/* Kill execution */
_panic_reboot();
}
-84
View File
@@ -1,84 +0,0 @@
/*
* Copyright (c) Souldbminer
* Copyright (c) KazushiMe
* 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 "pllmb.hpp"
namespace pllmb {
u64 MeasurePtoHz(u32 ptoId, u32 divider, u32 preselReg, u32 preselMask) {
u32 preselOrigBit = 0;
if (preselReg) {
u32 val = MMIO32(freq::CarBase + preselReg);
usleep(10);
preselOrigBit = val & preselMask;
val &= ~preselMask;
val |= preselMask;
MMIO32(freq::CarBase + preselReg) = val;
usleep(10);
}
constexpr u32 cycleCount = 16;
const u32 savedCntl = MMIO32(freq::CarBase + freq::CarPtoClkCntCntl);
u32 val = freq::PtoDivSelDiv1 | freq::PtoClkEnable | (cycleCount - 1);
val |= ptoId << freq::PtoSrcSelShift;
MMIO32(freq::CarBase + freq::CarPtoClkCntCntl) = val;
usleep(10);
MMIO32(freq::CarBase + freq::CarPtoClkCntCntl) = val | freq::PtoCntRst;
usleep(10);
MMIO32(freq::CarBase + freq::CarPtoClkCntCntl) = val;
usleep(10);
MMIO32(freq::CarBase + freq::CarPtoClkCntCntl) = val | freq::PtoCntEn;
usleep(500);
while (MMIO32(freq::CarBase + freq::CarPtoClkCntStatus) & PtoCyclePeriod)
;
u32 cnt = MMIO32(freq::CarBase + freq::CarPtoClkCntStatus) & freq::PtoClkCntMask;
cnt *= divider;
const u64 rateHz = (static_cast<u64>(cnt) * 32768) / cycleCount;
usleep(10);
MMIO32(freq::CarBase + freq::CarPtoClkCntCntl) = savedCntl;
usleep(10);
if (preselReg) {
u32 val2 = MMIO32(freq::CarBase + preselReg);
usleep(10);
val2 &= ~preselMask;
val2 |= preselOrigBit;
MMIO32(freq::CarBase + preselReg) = val2;
usleep(10);
}
return rateHz;
}
u64 MeasureRamClockHz() {
const u64 pllmbHz = MeasurePtoHz(PtoPllmb, 2, PllmMisc2, PllmMisc2PllmbBit);
if (pllmbHz != 0) {
return pllmbHz;
}
return MeasurePtoHz(PtoPllm, 2, PllmMisc2, PllmMisc2PllmBit);
}
} // namespace pllmb
-39
View File
@@ -1,39 +0,0 @@
/*
* Copyright (c) Souldbminer
* Copyright (c) KazushiMe
* 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"
namespace pllmb {
constexpr u32 PtoPllm = 2;
constexpr u32 PtoPllmb = 37;
constexpr u32 PllmMisc2 = 0x9C;
constexpr u32 PllmMisc2PllmBit = (1u << 8);
constexpr u32 PllmMisc2PllmbBit = (1u << 9);
constexpr u32 PtoCyclePeriod = (1u << 31); // PTO_CLK_CNT_STATUS busy bit
u64 MeasurePtoHz(u32 ptoId, u32 divider, u32 preselReg, u32 preselMask);
u64 MeasureRamClockHz();
} // namespace pllmb
-77
View File
@@ -1,77 +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 "regs.hpp"
[[maybe_unused]] void usleep(u32 us) {
while (us) {
const u32 delay = (us > HaltMaxCnt) ? HaltMaxCnt : us;
us -= delay;
MMIO32(FlowCtlrBase + FlowCtlrHaltCopEvents) = HaltModeWaitEvent | HaltUsec | delay;
}
}
[[maybe_unused]] void msleep(u32 ms) {
while (ms) {
const u32 delay = (ms > HaltMaxCnt) ? HaltMaxCnt : ms;
ms -= delay;
MMIO32(FlowCtlrBase + FlowCtlrHaltCopEvents) = HaltModeWaitEvent | HaltMsec | delay;
}
}
bool g_uartLoggingEnabled = false;
void UartPutc(char c) {
if (!g_uartLoggingEnabled) {
return;
}
while (!(MMIO32(UartBBase + UartLsr) & UartLsrThre))
;
MMIO32(UartBBase + UartThr) = static_cast<u32>(static_cast<u8>(c));
}
void UartFlush() {
if (!g_uartLoggingEnabled) {
return;
}
while (!(MMIO32(UartBBase + UartLsr) & UartLsrTmty))
;
}
void UartPuts(const char *s) {
while (*s) {
UartPutc(*s++);
}
}
void UartPutHex32(u32 v) {
static const char digits[] = "0123456789ABCDEF";
for (int i = 28; i >= 0; i -= 4) {
UartPutc(digits[(v >> i) & 0xF]);
}
}
void UartPutsForce(const char *s) {
while (*s) {
while (!(MMIO32(UartBBase + UartLsr) & UartLsrThre))
;
MMIO32(UartBBase + UartThr) = static_cast<u32>(static_cast<u8>(*s++));
}
while (!(MMIO32(UartBBase + UartLsr) & UartLsrTmty))
;
}
-58
View File
@@ -1,58 +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/>.
*
*/
#pragma once
#include "defs.hpp"
#define MMIO32(addr) (*reinterpret_cast<volatile u32 *>(addr))
constexpr u32 FwRamStart = 0x40004000;
constexpr u32 FwRamEnd = 0x4000C000;
constexpr u32 WorkRamStart = FwRamEnd;
constexpr u32 WorkRamEnd = 0x40010000;
constexpr u32 FlowCtlrBase = 0x60007000;
constexpr u32 FlowCtlrHaltCopEvents = 0x004;
constexpr u32 HaltModeWaitEvent = (2u << 29);
constexpr u32 HaltMsec = (1u << 24);
constexpr u32 HaltUsec = (1u << 25);
constexpr u32 HaltMaxCnt = 0xFF;
[[maybe_unused]] void usleep(u32 us);
[[maybe_unused]] void msleep(u32 ms);
constexpr u32 TmrBase = 0x60005000;
constexpr u32 TimerUsCntr1Us = 0x10;
constexpr u32 UartBBase = 0x70006040;
constexpr u32 UartThr = 0x00;
constexpr u32 UartLsr = 0x14;
constexpr u32 UartLsrThre = (1u << 5);
constexpr u32 UartLsrTmty = (1u << 6);
extern bool g_uartLoggingEnabled;
void UartPutc(char c);
void UartFlush();
void UartPuts(const char *s);
void UartPutHex32(u32 v);
void UartPutsForce(const char *s);
-29
View File
@@ -1,29 +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 "sensors.hpp"
s32 TranslateSocthermTemp(u16 val) {
s32 t = ((val >> 8) & 0xFF) * 1000;
if (val & (1u << 7)) {
t += 500;
}
if (val & (1u << 0)) {
t = -t;
}
return t;
}
-25
View File
@@ -1,25 +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/>.
*
*/
#pragma once
#include "regs.hpp"
constexpr u32 SocthermBase = 0x700E2000;
constexpr u32 SocthermSensorTemp1 = 0x1C8;
constexpr u32 SocthermSensorTemp2 = 0x1CC;
s32 TranslateSocthermTemp(u16 val);
-117
View File
@@ -1,117 +0,0 @@
/*
* Copyright (c) Atmosphere-NX
*
* 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/>.
*
*/
.section .vectors, "ax", %progbits
.align 3
.global reset
reset:
b _start
b _panic
.section .text.start, "ax", %progbits
.align 3
.global _start
.extern main
_start:
/* Mask IRQ + FIQ, enter SVC mode. */
msr cpsr_f, #0xC0
msr cpsr_cf, #0xD3
/* Set the stack pointer. */
ldr sp, =__stack_top
/* Zero .bss. */
ldr r0, =__bss_start__
ldr r1, =__bss_end__
mov r2, #0
1:
cmp r0, r1
bge 2f
str r2, [r0], #4
b 1b
2:
/* Run global constructors. */
ldr r4, =__init_array_start
ldr r5, =__init_array_end
3:
cmp r4, r5
bge 4f
ldr r0, [r4], #4
mov lr, pc
bx r0
b 3b
4:
bl main
.global _panic
_panic:
ldr r0, =0x70006040 /* UART_B_BASE */
adr r3, _panic_msg
7:
ldrb r1, [r3], #1
cmp r1, #0
beq 9f
8:
ldr r2, [r0, #0x14] /* UART_LSR */
tst r2, #0x20 /* UART_LSR_THRE */
beq 8b
str r1, [r0]
b 7b
9:
ldr r2, [r0, #0x14] /* UART_LSR */
tst r2, #0x40 /* UART_LSR_TMTY */
beq 9b
b _panic_reboot
_panic_msg:
.asciz "[bpmpfw] PANIC\n"
.align 2
.global _panic_reboot
_panic_reboot:
ldr r0, =0x7000EC40 /* PMC_BASE + APBDEV_PMC_SCRATCH200 */
ldr r1, =0xF8F00000 /* Panic code. */
str r1, [r0]
ldr r4, =0x60005000 /* TMR_BASE */
/* Write the magic */
ldr r1, =0xC45A
str r1, [r4, #0x18C]
/* Disable the counters */
mov r1, #0x2
str r1, [r4, #0x188]
/* Start periodic timer */
ldr r1, =0xC0000000
str r1, [r4, #0x080]
/* Set reboot source to the timer we started */
ldr r1, =0x8019
str r1, [r4, #0x180]
/* Enable the counters */
mov r1, #0x1
str r1, [r4, #0x188]
_panic_hang:
b _panic_hang
-10
View File
@@ -19,16 +19,6 @@ for arg in "$@"; do
esac
done
echo
echo "*** Compiling bpmpfw ***"
pushd "$ROOT_DIR/bpmpfw"
make -j$CORES
popd > /dev/null
mkdir -p "$ROOT_DIR/sysmodule/data"
cp -vf "$ROOT_DIR/bpmpfw/out/bpmpfw.bin" "$ROOT_DIR/sysmodule/data/bpmpfw.bin"
echo
echo "*** Compiling hoc-clk ***"
TITLE_ID="$(grep -oP '"title_id":\s*"0x\K(\w+)' "$ROOT_DIR/sysmodule/perms.json")"
@@ -30,9 +30,7 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __SWITCH__ // Wont be defined in BPMP-FW
#include <switch/types.h>
#endif
typedef enum
{
@@ -1,58 +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/>.
*
*/
#pragma once
#include <stdint.h>
#include "board.h"
#define HOCCLK_BPMP_MAGIC 0x42504D50 // 'BPMP'
typedef enum {
HocClkBpmpCmd_None = 0,
HocClkBpmpCmd_RequestShutdown = 1, // Request BPMP-FW to shut down for sleep mode.
HocClkBpmpCmd_SetUartEnabled = 2, // cmdArg1: 0/1.
} HocClkBpmpCmd;
/*
* Shared with BPMP-FW
* Any 64 bit data type is not permitted.
*/
typedef struct {
u32 magic; // should be HOCCLK_BPMP_MAGIC once bpmpfw is running
u32 status; // Reserved for error codes and such
s32 tempCpu;
s32 tempGpu;
s32 tempMem;
s32 tempPllx;
u32 freqCpu; // kHz
u32 freqMem; // kHz
u32 freqMemPll; // kHz
u32 emcLoadAll;
u32 emcLoadCpu;
u32 emcBwAll;
u32 emcBwCpu;
u32 emcBwGpu;
u32 cmd; // HocClkBpmpCmd; 0 = no command pending
u32 cmdArg1;
u32 cmdArg2;
u32 cmdArg3;
u32 cmdArg4;
s32 tempAO;
} HocClkBpmpSharedInfo;
static_assert(sizeof(HocClkBpmpSharedInfo) == 0x50);
@@ -29,7 +29,6 @@
#include <stdint.h>
#include "board.h"
#include "bpmp.h"
typedef struct {
@@ -85,11 +84,8 @@ typedef struct {
bool rebootRequired;
bool isFirstLoad;
// Populated from bpmpfw's work-RAM via SmcCopyFromIram (see HocClkBpmpSharedInfo above).
HocClkBpmpSharedInfo bpmpInfo;
// Reserved for future use
u8 reserved[0x308];
u8 reserved[0x358];
} HocClkContext;
typedef struct
@@ -44,7 +44,6 @@ tsl::elm::ListItem* aotagTempItem = NULL;
tsl::elm::ListItem* dramTempItem = NULL;
tsl::elm::ListItem* cTypeItem = NULL;
tsl::elm::ListItem* creditsItem = NULL;
tsl::elm::ListItem* bpmpStatusItem = NULL;
#define R_ARROW "\u2192"
@@ -153,8 +152,6 @@ void AboutGui::listUI()
// custRevItem = new tsl::elm::ListItem("CUST revision:");
// this->listElement->addItem(custRevItem);
bpmpStatusItem = new tsl::elm::ListItem("BPMP Status:");
this->listElement->addItem(bpmpStatusItem);
kipVersionItem = new tsl::elm::ListItem("KIP version:");
this->listElement->addItem(kipVersionItem);
@@ -285,7 +282,6 @@ void AboutGui::refresh()
dramTempItem->setValue(strings[12]);
cTypeItem->setValue(hocClkFormatConsoleType(this->context->consoleType, true));
bpmpStatusItem->setValue(this->context->bpmpInfo.magic == HOCCLK_BPMP_MAGIC ? "Enabled" : "Halted");
}
class CreditsSubMenu : public AboutGui {
+3 -7
View File
@@ -22,7 +22,7 @@ CONFIG_DIR := horizon-oc
BUILD := build
OUTDIR := out
RESOURCES := res
SOURCES := src src/hos src/i2c src/ipc src/board src/bpmp src/display src/tsensor src/mgr src/file src/soc src/mapping src/pwr src/util
SOURCES := src src/hos src/i2c src/ipc src/board src/display src/tsensor src/mgr src/file src/soc src/mapping src/pwr src/util
DATA := data
INCLUDES := ../common/include src/hos src/soc src/i2c src/util src/pwr src/ipc
EXEFS_SRC := exefs_src
@@ -105,7 +105,7 @@ export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
export APP_JSON := $(TOPDIR)/perms.json
.PHONY: $(BUILD) clean clean-libs clean-build all libs sync-bpmpfw
.PHONY: $(BUILD) clean clean-libs clean-build all libs
#---------------------------------------------------------------------------------
all: $(BUILD)
@@ -116,11 +116,7 @@ libs:
$(LIBNAMES):
@echo $@
sync-bpmpfw:
@$(MAKE) --no-print-directory -C $(TOPDIR)/../bpmpfw
@cp -f $(TOPDIR)/../bpmpfw/out/bpmpfw.bin $(TOPDIR)/data/bpmpfw.bin
$(BUILD): libs sync-bpmpfw
$(BUILD): libs
@[ -d $@ ] || mkdir -p $@
@[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR)
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+2 -9
View File
@@ -3,7 +3,7 @@
"title_id": "0x00FF0000636C6BFF",
"title_id_range_min": "0x00FF0000636C6BFF",
"title_id_range_max": "0x00FF0000636C6BFF",
"main_thread_stack_size": "0x0000F000",
"main_thread_stack_size": "0x0000C000",
"main_thread_priority": 16,
"default_cpu_id": 3,
"process_category": 1,
@@ -46,14 +46,7 @@
"size": "0x00001000",
"is_io": true
}
}, {
"type": "map",
"value": {
"address": "0x60005000",
"is_ro": false,
"size": "0x00001000",
"is_io": true
}
}, {
"type": "map",
"value": {
+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
@@ -1,33 +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/>.
*
*/
#pragma once
#include <switch.h>
#include <hocclk/bpmp.h>
namespace bpmp {
Result StartBpmfwExecution();
void StartSleepMonitorThread();
void StopSleepMonitorThread();
const HocClkBpmpSharedInfo *GetSharedInfo();
// Always true on stock exosphere
bool IsAwake();
}
@@ -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,5 +18,14 @@
*/
#pragma once
#include <cstdint>
#include <hocclk.h>
#include <registers.h>
#include <switch.h>
void InitializeLibc();
#include "../board/board.hpp"
namespace pllmb {
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() {