66 Commits
1.2.1 ... 2.0.0

Author SHA1 Message Date
Lightos1
3704d46136 set default step mode to 66MHz 2026-04-20 21:30:02 +02:00
Lightos1
b374117c37 properly fix time stuff - thanks masa! 2026-04-20 20:14:29 +02:00
Lightos1
c9c5d08919 add updated spanish translation by tdrr 2026-04-20 19:57:24 +02:00
Lightos1
c963dd8369 fix auto latency according to spec 2026-04-20 15:18:14 +02:00
Lightos1
7fc9682de0 use max freq in jedec mode regardless of it being jedec or not 2026-04-20 15:06:14 +02:00
Lightos1
0c555f34c3 remove (failing) cust rev check 2026-04-20 14:54:37 +02:00
Lightos1
fb37d5f0e7 reduce context size 2026-04-20 14:37:36 +02:00
Lightos1
a7619c39d2 set read/write latency to 1600 cause it's only used on erista 2026-04-20 07:31:52 +02:00
souldbminersmwc
55b97156ce hocclk: improve kip logging and add failsafe 2026-04-19 19:16:51 -04:00
souldbminersmwc
37ad65e768 final hoc 2.0.0 changes 2026-04-19 19:15:10 -04:00
souldbminersmwc
3b4877b287 update submodules 2026-04-19 15:38:07 -04:00
Lightos1
01d79aab30 fix off by one 2026-04-19 19:57:58 +02:00
Lightos1
3bb7b4cbd5 change colors 2026-04-19 19:17:36 +02:00
Lightos1
9c16f8b2b8 fix typo 2026-04-19 16:12:57 +02:00
Lightos1
78112547b6 prevent switch from combusting into 4 billion volts :D 2026-04-19 15:47:46 +02:00
Lightos1
3c7a42b033 formating 2026-04-19 15:13:35 +02:00
Lightos1
203587523a change result 2026-04-19 15:13:19 +02:00
Lightos1
07d0824c55 Merge pull request #66 from Horizon-OC/mrf-but-no-z-with-z
mrf
2026-04-19 15:07:02 +02:00
Lightos1
f1eab00ce1 revert custom and pcv to default settings 2026-04-19 14:51:15 +02:00
Lightos1
6a851d4095 nso start check, timing tbreak and some horrendous ui code 2026-04-18 23:00:21 +02:00
Lightos1
822e9f2817 add t2 trp cap 2026-04-18 19:02:07 +02:00
Lightos1
55b3a4230c Remove useless comments, yes I cannot be bothered to write ui code myself :D; fuck ui 2026-04-18 16:31:49 +02:00
Lightos1
be61b9df0e add sloppy configurator: todo fix crap 2026-04-18 16:30:24 +02:00
Lightos1
6c8d429c64 add missing configs 2026-04-18 12:21:42 +02:00
Lightos1
aa95f526b8 add mrf latency stuff to configs 2026-04-18 12:19:55 +02:00
Lightos1
638ddb499c add jedec mode 2026-04-18 11:52:06 +02:00
Lightos1
c95b6fde88 assign all mtc tables 2026-04-17 23:40:17 +02:00
Lightos1
2dd726723e add latency switching 2026-04-17 20:26:32 +02:00
Lightos1
119f49a3a4 fix freq list generation and dvb 2026-04-17 16:36:12 +02:00
Lightos1
51f935c252 initial mrf implementation with some bugs to fix 2026-04-16 20:51:26 +02:00
Lightos1
e1003520eb Revert "initial mrf implementation with some bugs to fix"
This reverts commit 819913ffd9.
2026-04-16 20:49:58 +02:00
Lightos1
3b5185df8d Reapply "Partial workflow fixes? (#63)"
This reverts commit 26c1dd1f99.
2026-04-16 20:49:42 +02:00
Lightos1
26c1dd1f99 Revert "Partial workflow fixes? (#63)"
This reverts commit 923bd0c0ba.
2026-04-16 20:49:11 +02:00
Lightos1
819913ffd9 initial mrf implementation with some bugs to fix 2026-04-16 20:48:22 +02:00
Lightos1
923bd0c0ba Partial workflow fixes? (#63)
* Update build.yml

* Update build.yml

* Update build.yml
2026-04-13 16:53:25 +02:00
Lightos1
185e5bcf31 Update build.yml 2026-04-13 15:52:20 +02:00
souldbminersmwc
d1ef905ac5 hocmon: real temps fixes 2026-04-12 20:56:39 -04:00
souldbminersmwc
fad9b5be50 hocmon: fix vdd2/vddq flip and full/mini mode 2026-04-12 20:06:23 -04:00
souldbminersmwc
c6424403b3 hocmon: partially add ram bw 2026-04-12 17:43:45 -04:00
souldbminersmwc
2b23498285 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-12 16:53:01 -04:00
souldbminersmwc
c7db7a7632 hocclk: fix compilation 2026-04-12 16:51:52 -04:00
Lightos1
985ecdc980 add fatal handler payload 2026-04-12 22:04:19 +02:00
Lightos1
e1d75f0084 add oc_log files 2026-04-12 22:00:45 +02:00
souldbminersmwc
f276ca0187 hocclk: rename mem display unit to ram display unit 2026-04-11 21:05:28 -04:00
souldbminersmwc
ae39b1b1bd hocclk: fix MT/s display on 1600mhz 2026-04-11 21:03:31 -04:00
souldbminersmwc
9321aed1d1 bump version 2026-04-11 20:58:22 -04:00
souldbminersmwc
756c44ba82 hocclk: fix compile errors 2026-04-11 20:57:28 -04:00
souldbminersmwc
037f011c9b hocclk: peak emc bw and mem display unit option
also correct bw reading to keep it accurate
2026-04-11 20:56:35 -04:00
souldbminersmwc
d2a46e1202 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-11 19:08:19 -04:00
souldbminersmwc
976f133c97 add MT/S to mem profiles 2026-04-11 19:08:12 -04:00
Lightos1
accdf6dc34 fix Z - thanks masa! 2026-04-12 01:05:11 +02:00
souldbminersmwc
b417099a4a hocclk: add RAM bandwidth monitor 2026-04-11 19:00:22 -04:00
souldbminersmwc
b7440a38a5 hocclk: reduce memory usage for real this time 2026-04-11 18:25:30 -04:00
souldbminersmwc
6369382de1 Merge branch 'main' of https://github.com/Horizon-OC/Horizon-OC 2026-04-11 17:59:20 -04:00
souldbminersmwc
65db0b4989 hocclk: 800mV minimum display voltage 2026-04-11 17:59:18 -04:00
Lightos1
19b29b97e1 separate pcv.hpp into erista/mariko files 2026-04-10 17:45:32 +02:00
Lightos1
ec230e35d0 make this pretty 2026-04-09 11:33:44 +02:00
Lightos1
5942cfd68d ldr: refactor asm 2026-04-09 11:32:00 +02:00
souldbminersmwc
66d0109a9a hocclk: autosave kip and reduce uv3 to 675 2026-04-08 19:15:26 -04:00
souldbminersmwc
6334603f9e hocclk: undo alignment
this doesnt help at all, it makes it less aligned
2026-04-08 17:51:20 -04:00
souldbminersmwc
5d12c1721a hocclk: reorganize about section 2026-04-08 17:49:00 -04:00
Lightos1
7e42394894 fix GpuClkOsLimit name 2026-04-08 22:17:28 +02:00
Lightos1
07dd65eebf move GpuClkOsLimit to common 2026-04-08 22:15:52 +02:00
Lightos1
ca8bb25660 add GpuOsLimit verification to mariko; erista will be pushed once verified 2026-04-08 21:30:36 +02:00
souldbminersmwc
90e53b52b2 hocclk: refactoring and ram pll measurement 2026-04-07 19:48:17 -04:00
Lightos1
c6cd863526 Bump ams version 2026-04-07 19:45:16 +02:00
76 changed files with 4083 additions and 1066 deletions

View File

@@ -54,6 +54,9 @@ jobs:
echo $SHORT_SHA > dist/.commit
echo $GITHUB_SHA >> dist/.commit
- name: Clone Libnx
run: git clone https://github.com/switchbrew/libnx.git
- name: Clone Atmosphere
run: git clone --depth=1 --single-branch https://github.com/Atmosphere-NX/Atmosphere.git atmosphere -b $(cat ams_ver.txt)
@@ -81,14 +84,23 @@ jobs:
ccache --set-config=max_size=10G
ccache --set-config=compiler_check=content
ccache --zero-stats
- name: Build Libnx
shell: bash
run: |
export CC="ccache aarch64-none-elf-gcc"
export CXX="ccache aarch64-none-elf-g++"
pushd libnx
make -j$(($(nproc) * 4)) install CXX="ccache aarch64-none-elf-g++" CC="ccache aarch64-none-elf-gcc"
popd
- name: Build hoc-clk sysmodule and overlay
shell: bash
run: |
export CC="ccache aarch64-none-elf-gcc"
export CXX="ccache aarch64-none-elf-g++"
ROOT_DIR="$GITHUB_WORKSPACE/Source/sys-clk"
ROOT_DIR="$GITHUB_WORKSPACE/Source/hoc-clk"
DIST_DIR="$ROOT_DIR/dist"
mkdir -p "$DIST_DIR"
@@ -101,13 +113,14 @@ jobs:
echo "TITLE_ID: $TITLE_ID"
pushd "$ROOT_DIR/sysmodule"
git config --global --add safe.directory "$GITHUB_WORKSPACE"
make -j$(($(nproc) * 2)) CXX="ccache aarch64-none-elf-g++" CC="ccache aarch64-none-elf-gcc"
popd
mkdir -p "$DIST_DIR/atmosphere/contents/$TITLE_ID/flags"
cp -vf \
"$ROOT_DIR/sysmodule/out/horizon-oc.nsp" \
"$ROOT_DIR/sysmodule/out/hoc-clk.nsp" \
"$DIST_DIR/atmosphere/contents/$TITLE_ID/exefs.nsp"
: >"$DIST_DIR/atmosphere/contents/$TITLE_ID/flags/boot2.flag"

View File

@@ -31,6 +31,7 @@
namespace ams::ldr::hoc {
volatile CustomizeTable C = {
/* Disables RAM powerdown */
.hpMode = DISABLED,
@@ -39,8 +40,12 @@ volatile CustomizeTable C = {
.eristaEmcMaxClock1 = 1600000,
.eristaEmcMaxClock2 = 1600000,
.marikoEmcMaxClock = 1866000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
.marikoEmcVddqVolt = 600000, /* Micron: 600mV, other manafacturers: 640mV */
/* Available: 66MHz step rate, 100MHz step rate and jedec. */
/* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */
.stepMode = StepMode_66MHz,
.marikoEmcMaxClock = 2133000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */
.marikoEmcVddqVolt = 600000,
.emcDvbShift = 0,
// Primary
@@ -54,6 +59,29 @@ volatile CustomizeTable C = {
.t7_tWTR = 0,
.t8_tREFI = 0,
/* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
/* This is a lazy workaround until I find the issue... */
.t2_tRP_cap = 2,
/* Frequency where non low timings gets used. */
.timingEmcTbreak = DISABLED,
.low_t6_tRTW = DISABLED,
.low_t7_tWTR = DISABLED,
.readLatency = {
DISABLED,
DISABLED,
DISABLED,
DISABLED,
},
.writeLatency = {
DISABLED,
DISABLED,
DISABLED,
DISABLED,
},
/* You can mix and match different latencies if needed */
/*
* Read:
@@ -68,13 +96,13 @@ volatile CustomizeTable C = {
* 1331WL = 12
*/
.mem_burst_read_latency = RL_1866,
.mem_burst_write_latency = WL_1866,
.mem_burst_read_latency = RL_1600,
.mem_burst_write_latency = WL_1600,
.eristaCpuUV = 0,
.eristaCpuVmin = 800,
.eristaCpuMaxVolt = 1200,
/* Unlocks up to 2295 Mhz CPU, usage is not recommended. */
/* Unlocks up to 2397 Mhz CPU, usage is not recommended. */
.eristaCpuUnlock = DISABLED,
.marikoCpuUVLow = 0, // No undervolt

View File

@@ -20,7 +20,7 @@
#pragma once
#define CUST_REV 1
#define CUST_REV 2
#include "oc_common.hpp"
#include "pcv/pcv_common.hpp"
@@ -36,6 +36,12 @@ enum TableConfig: u32 {
EXTREME_TABLE = 4,
};
enum StepMode: u32 {
StepMode_66MHz = 0,
StepMode_100MHz = 1,
StepMode_Jedec = 2,
};
/*
* Read:
* 2133RL = 40
@@ -82,6 +88,8 @@ typedef struct CustomizeTable {
u32 eristaEmcMaxClock;
u32 eristaEmcMaxClock1;
u32 eristaEmcMaxClock2;
StepMode stepMode;
u32 marikoEmcMaxClock;
u32 marikoEmcVddqVolt;
u32 emcDvbShift;
@@ -95,6 +103,15 @@ typedef struct CustomizeTable {
u32 t7_tWTR;
u32 t8_tREFI;
u32 t2_tRP_cap;
u32 timingEmcTbreak;
u32 low_t6_tRTW;
u32 low_t7_tWTR;
u32 readLatency[4];
u32 writeLatency[4];
u32 mem_burst_read_latency;
u32 mem_burst_write_latency;

View File

@@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../oc_common.hpp"
#include <stratosphere.hpp>
#include "../mtc_timing_value.hpp"
namespace ams::ldr::hoc::pcv::erista {

View File

@@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../oc_common.hpp"
#include <stratosphere.hpp>
#include "../mtc_timing_value.hpp"
#include "timing_tables.hpp"
@@ -30,6 +30,70 @@ namespace ams::ldr::hoc::pcv::mariko {
rext = 0x1A;
}
void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) {
latency += index * latencyStep;
}
static u32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) {
u32 maxIndex = 0;
for (u32 i = 0; i < latencySize; ++i) {
if (latencyArray[i]) {
maxIndex = i;
}
}
return maxIndex;
}
void AutoLatency(volatile u32 &latency, u32 freq, u32 latencyStep) {
if (freq > 1600'000 && freq <= 1866'000) { /* 1866tRWL */
latency += latencyStep * 2;
} else { /* 2133tRWL */
latency += latencyStep * 3;
}
}
void HandleLatency(u32 freq, volatile u32 &latency, volatile u32 *latencyArray, u32 indexMax, u32 latencyStep) {
for (u32 i = 0; i <= indexMax; ++i) {
if (latencyArray[i] != 0 && freq <= latencyArray[i]) {
SwitchLatency(latency, i, latencyStep);
return;
}
}
SwitchLatency(latency, indexMax, latencyStep);
}
void HandleLatency(u32 freq) {
static u32 rlIndexMax = GetMaxLatencyIndex(C.readLatency, std::size(C.readLatency));
static u32 wlIndexMax = GetMaxLatencyIndex(C.writeLatency, std::size(C.writeLatency));
constexpr u32 ReadLatencyStep = 4;
constexpr u32 WriteLatencyStep = 2;
bool autoLatencyRead = false, autoLatencyWrite = false;
if (rlIndexMax == 0) {
AutoLatency(RL, freq, ReadLatencyStep);
autoLatencyRead = true;
}
if (wlIndexMax == 0) {
AutoLatency(WL, freq, WriteLatencyStep);
autoLatencyWrite = true;
}
if (autoLatencyRead && autoLatencyWrite) {
return;
}
if (!autoLatencyRead) {
HandleLatency(freq, RL, C.readLatency, rlIndexMax, ReadLatencyStep);
}
if (!autoLatencyWrite) {
HandleLatency(freq, WL, C.writeLatency, wlIndexMax, WriteLatencyStep);
}
}
void CalculateMrw2() {
static const u8 rlMapDBI[8] = {
6, 12, 16, 22, 28, 32, 36, 40
@@ -59,8 +123,80 @@ namespace ams::ldr::hoc::pcv::mariko {
mrw2 = static_cast<u8>(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6)));
}
void CalculateTimings() {
void CalculateTimings(double tCK_avg, u32 freq) {
RL = RL_1331;
WL = WL_1331;
HandleLatency(freq);
GetRext();
/* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */
/* This is a lazy workaround until I find the issue... */
u32 tRPpbIndex = C.t2_tRP;
if (WL == WL_1331) {
tRPpbIndex = MIN(C.t2_tRP_cap, C.t2_tRP);
}
tRCD = tRCD_values[C.t1_tRCD];
tRPpb = tRP_values[tRPpbIndex];
tRAS = tRAS_values[C.t3_tRAS];
tRRD = tRRD_values[C.t4_tRRD];
tRFCpb = tRFC_values[C.t5_tRFC];
u32 tRTW = C.t6_tRTW;
u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
if (freq < C.timingEmcTbreak) {
tRTW = C.low_t6_tRTW;
tWTR = 10 - tWTR_values[C.low_t7_tWTR];
}
s32 finetRTW = C.fineTune_t6_tRTW;
s32 finetWTR = C.fineTune_t7_tWTR;
tRC = tRAS + tRPpb;
tRFCab = tRFCpb * 2;
tXSR = static_cast<double>(tRFCab + 7.5);
tFAW = static_cast<u32>(tRRD * 4.0);
tRPab = tRPpb + 3;
tR2P = CEIL((RL * 0.426) - 2.0);
tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (tRTW * 3) + finetRTW;
tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
tRATM = CEIL((tRTM - 10.0) + (RL * 0.426));
rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017);
qpop = rdv - 14;
quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782);
quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg);
einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width;
einput = quse - CEIL(9.928 / tCK_avg);
u32 qrst_duration = FLOOR(8.399 - tCK_avg);
u32 qrstLow = MAX(static_cast<s32>(einput - qrst_duration - 2), static_cast<s32>(0));
qrst = PACK_U32(qrst_duration, qrstLow);
ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0);
qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput);
tW2P = (CEIL(WL * 1.7303) * 2) - 5;
tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast<double>(tW2P))) + (BL / 2));
tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR;
tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753));
tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0;
wdv = WL;
wsv = WL - 2;
wev = 0xA + (WL - 14);
u32 obdlyHigh = 3 / FLOOR(MIN(static_cast<double>(2), tCK_avg * (WL - 7)));
u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0);
obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow);
pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361)));
tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0;
u32 tMMRI = tRCD + (tCK_avg * 3);
pdex2mrr = tMMRI + 10;
CalculateMrw2();
}

View File

@@ -18,7 +18,7 @@
namespace ams::ldr::hoc::pcv::mariko {
void CalculateTimings();
void CalculateTimings(double tCK_avg, u32 freq);
}

View File

@@ -111,60 +111,54 @@ namespace ams::ldr::hoc {
const std::array<u32, 10> tWTR_values = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
const std::array<u32, 6> tREFpb_values = { 3900, 5850, 7800, 11700, 15600, 99999 };
const double tCK_avg = 1000'000.0 / C.marikoEmcMaxClock;
inline u32 tRCD;
inline u32 tRPpb;
inline u32 tRAS;
inline double tRRD;
inline u32 tRFCpb;
const u32 tRCD = tRCD_values[C.t1_tRCD];
const u32 tRPpb = tRP_values[C.t2_tRP];
const u32 tRAS = tRAS_values[C.t3_tRAS];
const double tRRD = tRRD_values[C.t4_tRRD];
const u32 tRFCpb = tRFC_values[C.t5_tRFC];
const u32 tWTR = 10 - tWTR_values[C.t7_tWTR];
const s32 finetRTW = C.fineTune_t6_tRTW;
const s32 finetWTR = C.fineTune_t7_tWTR;
inline u32 tRC;
inline u32 tRFCab;
inline double tXSR;
inline u32 tFAW;
inline double tRPab;
const u32 tRC = tRAS + tRPpb;
const u32 tRFCab = tRFCpb * 2;
const double tXSR = static_cast<double>(tRFCab + 7.5);
const u32 tFAW = static_cast<u32>(tRRD * 4.0);
const double tRPab = tRPpb + 3;
inline u32 RL;
inline u32 WL;
const u32 tR2P = CEIL((RL * 0.426) - 2.0);
const u32 tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW;
const u32 tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg);
const u32 tRATM = CEIL((tRTM - 10.0) + (RL * 0.426));
inline u32 tR2P;
inline u32 tR2W;
inline u32 tRTM;
inline u32 tRATM;
inline u32 rext;
const u32 rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017);
const u32 qpop = rdv - 14;
const u32 quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782);
const u32 quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg);
const u32 einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width;
const u32 einput = quse - CEIL(9.928 / tCK_avg);
const u32 qrst_duration = FLOOR(8.399 - tCK_avg);
const u32 qrstLow = MAX(static_cast<s32>(einput - qrst_duration - 2), static_cast<s32>(0));
const u32 qrst = PACK_U32(qrst_duration, qrstLow);
const u32 ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0);
const u32 qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput);
const u32 tW2P = (CEIL(WL * 1.7303) * 2) - 5;
const u32 tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast<double>(tW2P))) + (BL / 2));
const u32 tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR;
const u32 tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753));
const u32 tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0;
inline u32 rdv;
inline u32 qpop;
inline u32 quse_width;
inline u32 quse;
inline u32 einput_duration;
inline u32 einput;
inline u32 qrst;
inline u32 ibdly;
inline u32 qsafe;
const u32 wdv = WL;
const u32 wsv = WL - 2;
const u32 wev = 0xA + (WL - 14);
inline u32 tW2P;
inline u32 tWTPDEN;
inline u32 tW2R;
inline u32 tWTM;
inline u32 tWATM;
const u32 obdlyHigh = 3 / FLOOR(MIN(static_cast<double>(2), tCK_avg * (WL - 7)));
const u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0);
const u32 obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow);
inline u32 wdv;
inline u32 wsv;
inline u32 wev;
const u32 pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361)));
inline u32 obdly;
const u32 tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0;
inline u32 pdex2rw;
const double tMMRI = tRCD + (tCK_avg * 3);
const double pdex2mrr = tMMRI + 10; /* Do this properly? */
inline u32 tCLKSTOP;
inline u32 pdex2mrr;
inline u8 mrw2;
}

View File

@@ -28,6 +28,7 @@
#endif
#include "customize.hpp"
#include "oc_log.hpp"
#define PATCH_OFFSET(offset, value) \
static_assert(sizeof(__typeof__(offset)) <= sizeof(u64)); \
@@ -50,6 +51,7 @@ namespace ams::ldr {
R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013);
R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014);
R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015);
R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
}
namespace ams::ldr::hoc {

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
* Copyright (c) 2019 Atmosphere-NX
*
* 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/>.
*/
/* See https://github.com/lulle2007200/emuMMC/blob/internal-emummc/source/ */
#include "oc_common.hpp"
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
#include "fatal_handler_bin.h"
#endif
namespace ams::ldr::hoc {
#define ATMOSPHERE_REBOOT_TO_FATAL_MAGIC 0x32454641
#define ATMOSPHERE_IRAM_PAYLOAD_BASE 0x40010000
#define ATMOSPHERE_FATAL_ERROR_ADDR 0x4003E000
_Alignas(4096) u8 working_buf[4096];
void SmcRebootToIramPayload() {
SecmonArgs args;
args.X[0] = 0xC3000401;
args.X[1] = 65001;
args.X[2] = 0;
args.X[3] = 2;
svcCallSecureMonitor(&args);
}
Result SmcCopyToIram(uintptr_t dest, const void *src, u32 size) {
SecmonArgs args;
args.X[0] = 0xF0000201;
args.X[1] = (u64)src;
args.X[2] = (u64)dest;
args.X[3] = size;
args.X[4] = 1;
svcCallSecureMonitor(&args);
Result rc = 0;
if (args.X[0] != 0) {
rc = (26u | ((u32)args.X[0] << 9u));
}
return rc;
}
Result SmcCopyFromIram(void *dest, uintptr_t src, u32 size) {
SecmonArgs args;
args.X[0] = 0xF0000201;
args.X[1] = (u64)dest;
args.X[2] = (u64)src;
args.X[3] = size;
args.X[4] = 0;
svcCallSecureMonitor(&args);
Result rc = 0;
if (args.X[0] != 0) {
rc = (26u | ((u32)args.X[0] << 9u));
}
return rc;
}
struct log_ctx_t {
u32 magic;
u32 sz;
u32 start;
u32 end;
char buf[];
};
#define IRAM_LOG_CTX_ADDR 0x4003C000
#define IRAM_LOG_MAX_SZ 4096
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
void Log(const char *data, ...) {
static const u32 max_log_sz = sizeof(working_buf) - sizeof(log_ctx_t);
static bool initDone = false;
log_ctx_t *log_ctx = (log_ctx_t*)working_buf;
SmcCopyFromIram(working_buf, IRAM_LOG_CTX_ADDR, sizeof(working_buf));
if (!initDone) {
initDone = true;
log_ctx->buf[0] = '\0';
log_ctx->magic = 0xaabbccdd;
log_ctx->start = 0;
log_ctx->end = 0;
}
va_list args;
va_start(args, data);
s32 res = vsnprintf(log_ctx->buf + log_ctx->end, max_log_sz - log_ctx->end, data, args);
va_end(args);
if (res < 0 || res >= (static_cast<s32>(max_log_sz - log_ctx->end))) {
SmcCopyToIram(IRAM_LOG_CTX_ADDR, working_buf, sizeof(working_buf));
return;
}
log_ctx->end += res;
SmcCopyToIram(IRAM_LOG_CTX_ADDR, working_buf, sizeof(working_buf));
}
#endif
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
void ViewLog() {
constexpr size_t PageSize = 4096;
for (size_t ofs = 0; ofs < fatal_handler_bin_size; ofs += PageSize) {
memcpy(&working_buf, fatal_handler_bin + ofs, std::min(fatal_handler_bin_size - ofs, PageSize));
SmcCopyToIram(ATMOSPHERE_IRAM_PAYLOAD_BASE + ofs, &working_buf, std::min(fatal_handler_bin_size - ofs, PageSize));
}
SmcRebootToIramPayload();
while(true){}
}
#endif
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2019 m4xw <m4x@m4xw.net>
* Copyright (c) 2019 Atmosphere-NX
*
* 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/>.
*/
/* See https://github.com/lulle2007200/emuMMC/blob/internal-emummc/source/ */
#pragma once
namespace ams::ldr::hoc {
void Log(const char *data, ...);
void ViewLog();
}

View File

@@ -173,11 +173,14 @@ namespace ams::ldr::hoc::pcv {
void Patch(uintptr_t mapped_nso, size_t nso_size) {
#ifdef ATMOSPHERE_IS_STRATOSPHERE
SafetyCheck();
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
if (isMariko)
if (isMariko) {
mariko::Patch(mapped_nso, nso_size);
else
} else {
erista::Patch(mapped_nso, nso_size);
}
#endif
}

View File

@@ -22,244 +22,12 @@
#include "../oc_common.hpp"
#include "pcv_common.hpp"
#include "pcv_erista.hpp"
#include "pcv_mariko.hpp"
#include "pcv_asm.hpp"
namespace ams::ldr::hoc::pcv {
namespace mariko {
constexpr cvb_entry_t CpuCvbTableDefault[] = {
{ 204000, { 721589, -12695, 27 }, { } },
{ 306000, { 747134, -14195, 27 }, { } },
{ 408000, { 776324, -15705, 27 }, { } },
{ 510000, { 809160, -17205, 27 }, { } },
{ 612000, { 845641, -18715, 27 }, { } },
{ 714000, { 885768, -20215, 27 }, { } },
{ 816000, { 929540, -21725, 27 }, { } },
{ 918000, { 976958, -23225, 27 }, { } },
{ 1020000, { 1028021, -24725, 27 }, { 1120000 } },
{ 1122000, { 1082730, -26235, 27 }, { 1120000 } },
{ 1224000, { 1141084, -27735, 27 }, { 1120000 } },
{ 1326000, { 1203084, -29245, 27 }, { 1120000 } },
{ 1428000, { 1268729, -30745, 27 }, { 1120000 } },
{ 1581000, { 1374032, -33005, 27 }, { 1120000 } },
{ 1683000, { 1448791, -34505, 27 }, { 1120000 } },
{ 1785000, { 1527196, -36015, 27 }, { 1120000 } },
{ 1887000, { 1609246, -37515, 27 }, { 1120000 } },
{ 1963500, { 1675751, -38635, 27 }, { 1120000 } },
{ },
};
constexpr u32 CpuClkOfficial = 1963'500;
constexpr u32 CpuVoltOfficial = 1120;
constexpr u32 CpuVminOfficial = 620;
static const u32 cpuVoltagePatchValues[] = { 850, 38, 1120, 1000, 100, 1000, 0 };
static const s32 cpuVoltagePatchOffsets[] = { -2, -1, 5, 6, 7, 8, 9 };
static_assert(sizeof(cpuVoltagePatchValues) == sizeof(cpuVoltagePatchOffsets), "Invalid cpuVoltagePatch size");
static const u32 cpuVoltThermalData[] = { 620, 1120, 20000, 620, 1120, 70000, 950, 1132, 0, 950, 1227, 0 };
static const u32 allowedCpuMaxFrequencies[] = { 1'963'500, 2'091'000, 2'193'000, 2'295'000, 2'397'000, 2'499'000, 2'601'000, 2'703'000, };
constexpr cvb_entry_t GpuCvbTableDefault[] = {
// GPUB01_NA_CVB_TABLE
{ 76800, {}, { 610000, } },
{ 153600, {}, { 610000, } },
{ 230400, {}, { 610000, } },
{ 307200, {}, { 610000, } },
{ 384000, {}, { 610000, } },
{ 460800, {}, { 610000, } },
{ 537600, {}, { 801688, -10900, -163, 298, -10599, 162, } },
{ 614400, {}, { 824214, -5743, -452, 238, -6325, 81, } },
{ 691200, {}, { 848830, -3903, -552, 119, -4030, -2, } },
{ 768000, {}, { 891575, -4409, -584, 0, -2849, 39, } },
{ 844800, {}, { 940071, -5367, -602, -60, -63, -93, } },
{ 921600, {}, { 986765, -6637, -614, -179, 1905, -13, } },
{ 998400, {}, { 1098475, -13529, -497, -179, 3626, 9, } },
{ 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40, } },
{ 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110, } },
{ 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313, } },
{ 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559, } },
{ },
};
constexpr u32 GpuClkPllMax = 1300'000'000;
constexpr u32 GpuClkPllLimit = 2'600'000;
constexpr u32 GpuVminOfficial = 610;
static const u32 gpuDVFSPattern[] = { 1050, 1000, 100, 1000, 10, };
static const u32 gpuVoltThermalPattern[] = { 800, 1120, 0, 610, 1120, 20000, 610, 1120, 30000, 610, 1120, 50000, 610, 1120, 70000, 610, 1120, 90000, };
static_assert(sizeof(gpuVoltThermalPattern) == 72, "Invalid gpuVoltThermalPattern");
/* GPU Max Clock asm Pattern:
*
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
*
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
*/
inline constexpr u32 asm_pattern[] = {0x52820000, 0x72A001C0};
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) {
return ((ins1 ^ ins2) >> 5) == 0;
};
inline auto asm_get_rd = [](u32 ins) {
return ins & ((1 << 5) - 1);
};
inline auto asm_set_rd = [](u32 ins, u8 rd) {
return (ins & 0xFFFFFFE0) | (rd & 0x1F);
};
inline auto asm_set_imm16 = [](u32 ins, u16 imm) {
return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5);
};
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
return asm_compare_no_rd(*ptr32, asm_pattern[0]);
}
constexpr emc_dvb_dvfs_table_t EmcDvbTableDefault[] = {
{ 204000, { 637, 637, 637, } },
{ 408000, { 637, 637, 637, } },
{ 800000, { 637, 637, 637, } },
{ 1065600, { 637, 637, 637, } },
{ 1331200, { 650, 637, 637, } },
{ 1600000, { 675, 650, 637, } },
};
constexpr u32 EmcClkOSAlt = 1331'200;
constexpr u32 EmcClkPllmLimit = 2133'000'000;
constexpr u32 EmcVddqDefault = 600'000;
constexpr u32 MemVdd2Default = 1100'000;
constexpr u32 MTC_TABLE_REV = 3;
void Patch(uintptr_t mapped_nso, size_t nso_size);
}
namespace erista {
static u32 maxEmcClocks[] = { C.eristaEmcMaxClock2, C.eristaEmcMaxClock1, C.eristaEmcMaxClock, };
#define GET_MAX_OF_ARR(ARR) (*std::max_element(ARR, ARR + std::size(ARR)))
constexpr cvb_entry_t CpuCvbTableDefault[] = {
// CPU_PLL_CVB_TABLE_ODN
{ 204000, {721094}, { } },
{ 306000, {754040}, { } },
{ 408000, {786986}, { } },
{ 510000, {819932}, { } },
{ 612000, {852878}, { } },
{ 714000, {885824}, { } },
{ 816000, {918770}, { } },
{ 918000, {951716}, { } },
{ 1020000, {984662}, { -2875621, 358099, -8585} },
{ 1122000, {1017608}, { -52225, 104159, -2816} },
{ 1224000, {1050554}, { 1076868, 8356, -727} },
{ 1326000, {1083500}, { 2208191, -84659, 1240} },
{ 1428000, {1116446}, { 2519460, -105063, 1611} },
{ 1581000, {1130000}, { 2889664, -122173, 1834} },
{ 1683000, {1168000}, { 5100873, -279186, 4747} },
{ 1785000, {1227500}, { 5100873, -279186, 4747} },
{ },
};
constexpr u32 CpuVoltOfficial = 1227;
constexpr u32 CpuVminOfficial = 825;
constexpr u32 CpuVoltL4T = 1257'000;
static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 };
static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "invalid cpuVoltDvfsPattern size");
static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 };
static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "invalid cpuVoltageThermalPattern size");
constexpr u32 GpuClkPllLimit = 2'600'000;
constexpr u32 GpuClkPllMax = 921'600'000;
constexpr u32 GpuVminOfficial = 810;
constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 };
inline bool CpuMaxVoltPatternFn(u32* ptr32) {
u32 val = *ptr32;
return (val == 1132 || val == 1170 || val == 1227);
}
static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, };
static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern");
static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 };
static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "invalid gpuVoltageThermalPattern size");
/* GPU Max Clock asm Pattern:
*
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
*
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
*/
inline constexpr u32 asm_pattern[] = {
0x52820000, 0x72A001C0
};
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) {
return ((ins1 ^ ins2) >> 5) == 0;
};
inline auto asm_get_rd = [](u32 ins) {
return ins & ((1 << 5) - 1);
};
inline auto asm_set_rd = [](u32 ins, u8 rd) {
return (ins & 0xFFFFFFE0) | (rd & 0x1F);
};
inline auto asm_set_imm16 = [](u32 ins, u16 imm) {
return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5);
};
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
return asm_compare_no_rd(*ptr32, asm_pattern[0]);
};
constexpr cvb_entry_t GpuCvbTableDefault[] = {
// NA_FREQ_CVB_TABLE
{ 76800, {}, { 814294, 8144, -940, 808, -21583, 226, } },
{ 153600, {}, { 856185, 8144, -940, 808, -21583, 226, } },
{ 230400, {}, { 898077, 8144, -940, 808, -21583, 226, } },
{ 307200, {}, { 939968, 8144, -940, 808, -21583, 226, } },
{ 384000, {}, { 981860, 8144, -940, 808, -21583, 226, } },
{ 460800, {}, { 1023751, 8144, -940, 808, -21583, 226, } },
{ 537600, {}, { 1065642, 8144, -940, 808, -21583, 226, } },
{ 614400, {}, { 1107534, 8144, -940, 808, -21583, 226, } },
{ 691200, {}, { 1149425, 8144, -940, 808, -21583, 226, } },
{ 768000, {}, { 1191317, 8144, -940, 808, -21583, 226, } },
{ 844800, {}, { 1233208, 8144, -940, 808, -21583, 226, } },
{ 921600, {}, { 1275100, 8144, -940, 808, -21583, 226, } },
{ },
};
constexpr u32 MemVoltHOS = 1125'000;
constexpr u32 EmcClkMinFreq = 40800; /* 40.8 MHz table only exists on erista. */
constexpr u32 EmcClkPllmLimit = 1866'000'000;
constexpr u32 MTC_TABLE_REV = 7;
void Patch(uintptr_t mapped_nso, size_t nso_size);
}
inline auto MatchesPattern = [](u32 *base, const auto &offsets, const auto &values) {
for (size_t i = 0; i < std::size(values); ++i) {
if (*(base + offsets[i]) != values[i]) {

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) 2023 hanai3Bi
*
* 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 <stratosphere.hpp>
namespace ams::ldr::hoc::pcv {
constexpr u32 NopIns = 0x1f2003d5;
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) {
return ((ins1 ^ ins2) >> 5) == 0;
};
inline auto asm_get_rd = [](u32 ins) {
return ins & ((1 << 5) - 1);
};
inline auto asm_set_rd = [](u32 ins, u8 rd) {
return (ins & 0xFFFFFFE0) | (rd & 0x1F);
};
inline auto asm_set_imm16 = [](u32 ins, u16 imm) {
return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5);
};
inline auto AsmGetImm16 = [](u32 ins) {
return static_cast<u16>((ins >> 5) & 0xFFFF);
};
inline auto AsmCompareBrNoRd = [](u32 ins1, u32 ins2) {
constexpr u32 RegMask = ~(((1 << 5) - 1) << 5);
return ((ins1 & RegMask) ^ (ins2 & RegMask)) == 0;
};
inline auto AsmCompareAddNoImm12 = [](u32 ins1, u32 ins2) {
constexpr u32 Imm12Mask = ~(((1 << 12) - 1) << 10);
return ((ins1 & Imm12Mask) ^ (ins2 & Imm12Mask)) == 0;
};
inline auto AsmCompareAdrpNoImm = [](u32 ins1, u32 ins2) {
constexpr u32 ImmMask = ~((((1 << 2) - 1) << 29) | (((1 << 19) - 1) << 5));
return ((ins1 & ImmMask) ^ (ins2 & ImmMask)) == 0;
};
}

View File

@@ -46,11 +46,6 @@ namespace ams::ldr::hoc::pcv {
unsigned long dvco_calibration_max;
} cvb_cpu_dfll_data;
typedef struct emc_dvb_dvfs_table_t {
u64 freq;
s32 volt[4] = {0};
} emc_dvb_dvfs_table_t;
typedef struct __attribute__((packed)) div_nmp {
u8 divn_shift;
u8 divn_width;
@@ -119,7 +114,7 @@ namespace ams::ldr::hoc::pcv {
static_assert(sizeof(regulator) == 0x120);
constexpr u32 CpuClkOSLimit = 1785'000;
constexpr u32 GpuClkOsLimit = 921'600;
constexpr u32 EmcClkOSLimit = 1600'000;
#define R_SKIP() R_SUCCEED()

View File

@@ -134,7 +134,7 @@ namespace ams::ldr::hoc::pcv::erista {
Result GpuFreqMaxAsm(u32 *ptr32) {
// Check if both two instructions match the pattern
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1])))
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1])))
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
// Both instructions should operate on the same register
@@ -142,6 +142,12 @@ namespace ams::ldr::hoc::pcv::erista {
if (rd != asm_get_rd(ins2))
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
/* Verify the limit. */
/* TODO: Make this a little bit cleaner at some point. */
if (AsmGetImm16(ins1) != (GpuClkOsLimit & 0xFFFF) || AsmGetImm16(ins2) != (GpuClkOsLimit >> 16)) {
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
}
u32 max_clock;
switch (C.eristaGpuUV) {
case 0:
@@ -158,8 +164,8 @@ namespace ams::ldr::hoc::pcv::erista {
break;
}
u32 asm_patch[2] = {
asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd),
asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)};
asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd),
asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)};
PATCH_OFFSET(ptr32, asm_patch[0]);
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) 2023 hanai3Bi
*
* 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 "../oc_common.hpp"
#include "pcv_common.hpp"
#include "pcv_asm.hpp"
namespace ams::ldr::hoc::pcv::erista {
static u32 maxEmcClocks[] = { C.eristaEmcMaxClock2, C.eristaEmcMaxClock1, C.eristaEmcMaxClock, };
#define GET_MAX_OF_ARR(ARR) (*std::max_element(ARR, ARR + std::size(ARR)))
constexpr cvb_entry_t CpuCvbTableDefault[] = {
// CPU_PLL_CVB_TABLE_ODN
{ 204000, {721094}, { } },
{ 306000, {754040}, { } },
{ 408000, {786986}, { } },
{ 510000, {819932}, { } },
{ 612000, {852878}, { } },
{ 714000, {885824}, { } },
{ 816000, {918770}, { } },
{ 918000, {951716}, { } },
{ 1020000, {984662}, { -2875621, 358099, -8585} },
{ 1122000, {1017608}, { -52225, 104159, -2816} },
{ 1224000, {1050554}, { 1076868, 8356, -727} },
{ 1326000, {1083500}, { 2208191, -84659, 1240} },
{ 1428000, {1116446}, { 2519460, -105063, 1611} },
{ 1581000, {1130000}, { 2889664, -122173, 1834} },
{ 1683000, {1168000}, { 5100873, -279186, 4747} },
{ 1785000, {1227500}, { 5100873, -279186, 4747} },
{ },
};
constexpr u32 CpuVoltOfficial = 1227;
constexpr u32 CpuVminOfficial = 825;
constexpr u32 CpuVoltL4T = 1257'000;
static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 };
static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "invalid cpuVoltDvfsPattern size");
static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 };
static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "invalid cpuVoltageThermalPattern size");
constexpr u32 GpuClkPllLimit = 2'600'000;
constexpr u32 GpuClkPllMax = 921'600'000;
constexpr u32 GpuVminOfficial = 810;
constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 };
static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, };
static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern");
static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 };
static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "invalid gpuVoltageThermalPattern size");
/* GPU Max Clock asm Pattern:
*
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
*
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
*/
inline constexpr u32 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 };
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]);
};
constexpr cvb_entry_t GpuCvbTableDefault[] = {
// NA_FREQ_CVB_TABLE
{ 76800, {}, { 814294, 8144, -940, 808, -21583, 226, } },
{ 153600, {}, { 856185, 8144, -940, 808, -21583, 226, } },
{ 230400, {}, { 898077, 8144, -940, 808, -21583, 226, } },
{ 307200, {}, { 939968, 8144, -940, 808, -21583, 226, } },
{ 384000, {}, { 981860, 8144, -940, 808, -21583, 226, } },
{ 460800, {}, { 1023751, 8144, -940, 808, -21583, 226, } },
{ 537600, {}, { 1065642, 8144, -940, 808, -21583, 226, } },
{ 614400, {}, { 1107534, 8144, -940, 808, -21583, 226, } },
{ 691200, {}, { 1149425, 8144, -940, 808, -21583, 226, } },
{ 768000, {}, { 1191317, 8144, -940, 808, -21583, 226, } },
{ 844800, {}, { 1233208, 8144, -940, 808, -21583, 226, } },
{ 921600, {}, { 1275100, 8144, -940, 808, -21583, 226, } },
{ },
};
constexpr u32 MemVoltHOS = 1125'000;
constexpr u32 EmcClkMinFreq = 40800; /* 40.8 MHz table only exists on erista. */
constexpr u32 EmcClkPllmLimit = 1866'000'000;
constexpr u32 MTC_TABLE_REV = 7;
constexpr u32 MtcTableCountDefault = 10;
constexpr size_t MtcFullTableSize = sizeof(EristaMtcTable) * MtcTableCountDefault;
constexpr u32 MtcFullTableCount = 3;
/* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
enum DramId {
ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH = 0,
ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE = 1,
ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC = 2, /* This doesn't have a table in pcv? Wtf? */
ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH = 4,
ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX = 7, /* No table, but expected */
};
enum MtcTableIndex {
T210SdevEmcDvfsTableS4gb01 = 0, /* HB-MGCH */
T210SdevEmcDvfsTableS6gb01 = 1, /* HM-MGCH */
T210SdevEmcDvfsTableH4gb01 = 2, /* HR-NLE */
MtcTableIndex_Invalid = 3,
};
struct MtcDramIndex {
DramId dramId;
MtcTableIndex index;
};
constexpr MtcDramIndex mtcIndexTable[] = {
{ ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, },
{ ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, },
{ ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, },
};
void Patch(uintptr_t mapped_nso, size_t nso_size);
}

View File

@@ -18,6 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <vector>
#include "pcv.hpp"
#include "../mtc_timing_value.hpp"
#include "../mariko/calculate_timings.hpp"
@@ -53,7 +54,7 @@ namespace ams::ldr::hoc::pcv::mariko {
R_SKIP();
}
PATCH_OFFSET(ptr + 0, C.marikoGpuVmin);
PATCH_OFFSET(ptr, C.marikoGpuVmin);
PATCH_OFFSET(ptr + 3, C.marikoGpuVmin);
PATCH_OFFSET(ptr + 6, C.marikoGpuVmin);
PATCH_OFFSET(ptr + 9, C.marikoGpuVmin);
@@ -268,13 +269,21 @@ namespace ams::ldr::hoc::pcv::mariko {
Result GpuFreqMaxAsm(u32 *ptr32) {
// Check if both two instructions match the pattern
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1])))
if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) {
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
}
// Both instructions should operate on the same register
u8 rd = asm_get_rd(ins1);
if (rd != asm_get_rd(ins2))
if (rd != asm_get_rd(ins2)) {
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
}
/* Verify the limit. */
/* TODO: Make this a little bit cleaner at some point. */
if (AsmGetImm16(ins1) != (GpuClkOsLimit & 0xFFFF) || AsmGetImm16(ins2) != (GpuClkOsLimit >> 16)) {
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
}
u32 max_clock;
switch (C.marikoGpuUV) {
@@ -291,9 +300,10 @@ namespace ams::ldr::hoc::pcv::mariko {
max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq;
break;
}
u32 asm_patch[2] = {
asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd),
asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)
asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd),
asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd)
};
PATCH_OFFSET(ptr32, asm_patch[0]);
@@ -340,6 +350,8 @@ namespace ams::ldr::hoc::pcv::mariko {
TABLE->shadow_regs_ca_train.PARAM = VALUE; \
TABLE->shadow_regs_rdwr_train.PARAM = VALUE;
const double tCK_avg = 1000'000.0 / table->rate_khz;
#define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg))
/* Ram power down */
@@ -363,7 +375,7 @@ namespace ams::ldr::hoc::pcv::mariko {
const u32 dyn_self_ref_control = (static_cast<u32>(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000);
CalculateTimings();
CalculateTimings(tCK_avg, table->rate_khz);
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD));
@@ -390,7 +402,7 @@ namespace ams::ldr::hoc::pcv::mariko {
WRITE_PARAM_ALL_REG(table, emc_twtm, tWTM);
WRITE_PARAM_ALL_REG(table, emc_twatm, tWATM);
WRITE_PARAM_ALL_REG(table, emc_rext, rext);
WRITE_PARAM_ALL_REG(table, emc_wext, (C.marikoEmcMaxClock >= 2533000) ? 0x19 : 0x16);
WRITE_PARAM_ALL_REG(table, emc_wext, (table->rate_khz >= 2533000) ? 0x19 : 0x16);
WRITE_PARAM_ALL_REG(table, emc_refresh, refresh_raw);
WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, refresh_raw / 4);
WRITE_PARAM_ALL_REG(table, emc_trefbw, trefbw);
@@ -434,7 +446,7 @@ namespace ams::ldr::hoc::pcv::mariko {
constexpr double MC_ARB_DIV = 4.0;
constexpr u32 MC_ARB_SFA = 2;
table->burst_mc_regs.mc_emem_arb_cfg = C.marikoEmcMaxClock / (33.3 * 1000) / MC_ARB_DIV;
table->burst_mc_regs.mc_emem_arb_cfg = table->rate_khz / (33.3 * 1000) / MC_ARB_DIV;
table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2;
table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1;
table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1;
@@ -476,7 +488,7 @@ namespace ams::ldr::hoc::pcv::mariko {
table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = 0x115;
if (C.marikoEmcMaxClock >= 2133000) {
if (table->rate_khz >= 2133000) {
table->la_scale_regs.mc_ftop_ptsa_rate = 0x1F;
} else {
table->la_scale_regs.mc_ftop_ptsa_rate = 0x1B;
@@ -485,14 +497,14 @@ namespace ams::ldr::hoc::pcv::mariko {
table->la_scale_regs.mc_ptsa_grant_decrement = 0x17ff;
constexpr u32 MaskHigh = 0xFF00FFFF;
constexpr u32 Mask2 = 0xFFFFFF00;
constexpr u32 Mask3 = 0xFF00FF00;
constexpr u32 Mask2 = 0xFFFFFF00;
constexpr u32 Mask3 = 0xFF00FF00;
const u32 allowance1 = static_cast<u32>(0x32000 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
const u32 allowance2 = static_cast<u32>(0x9C40 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
const u32 allowance3 = static_cast<u32>(0xB540 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
const u32 allowance4 = static_cast<u32>(0x9600 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
const u32 allowance5 = static_cast<u32>(0x8980 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF;
const u32 allowance1 = static_cast<u32>(0x32000 / (table->rate_khz / 1000)) & 0xFF;
const u32 allowance2 = static_cast<u32>(0x9C40 / (table->rate_khz / 1000)) & 0xFF;
const u32 allowance3 = static_cast<u32>(0xB540 / (table->rate_khz / 1000)) & 0xFF;
const u32 allowance4 = static_cast<u32>(0x9600 / (table->rate_khz / 1000)) & 0xFF;
const u32 allowance5 = static_cast<u32>(0x8980 / (table->rate_khz / 1000)) & 0xFF;
table->la_scale_regs.mc_latency_allowance_xusb_0 = (table->la_scale_regs.mc_latency_allowance_xusb_0 & MaskHigh) | (allowance1 << 16);
table->la_scale_regs.mc_latency_allowance_xusb_1 = (table->la_scale_regs.mc_latency_allowance_xusb_1 & MaskHigh) | (allowance1 << 16);
@@ -516,8 +528,8 @@ namespace ams::ldr::hoc::pcv::mariko {
table->dram_timings.t_rp = tRFCpb;
table->dram_timings.t_rfc = tRFCab;
table->dram_timings.rl = RL;
table->dram_timings.rl = RL;
table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast<u32>(mrw2);
table->emc_cfg_2 = 0x11083D;
}
@@ -526,24 +538,24 @@ namespace ams::ldr::hoc::pcv::mariko {
constexpr u32 PllOscInKHz = 38400;
constexpr u32 PllOscHalfKHz = 19200;
double target_freq_d = static_cast<double>(C.marikoEmcMaxClock);
double target_freq_d = static_cast<double>(table->rate_khz);
s32 divm_candidate_half = static_cast<u8>(C.marikoEmcMaxClock / PllOscHalfKHz);
s32 divm_candidate_half = static_cast<u8>(table->rate_khz / PllOscHalfKHz);
bool remainder_check = (C.marikoEmcMaxClock - PllOscInKHz * (C.marikoEmcMaxClock / PllOscInKHz)) > (C.marikoEmcMaxClock - PllOscHalfKHz * divm_candidate_half) && static_cast<int>(((target_freq_d / PllOscHalfKHz - divm_candidate_half - 0.5) * 8192.0)) != 0;
bool remainder_check = (table->rate_khz - PllOscInKHz * (table->rate_khz / PllOscInKHz)) > (table->rate_khz - PllOscHalfKHz * divm_candidate_half) && static_cast<int>(((target_freq_d / PllOscHalfKHz - divm_candidate_half - 0.5) * 8192.0)) != 0;
u32 divm_final = remainder_check + 1;
table->pllmb_divm = divm_final;
double div_step_d = static_cast<double>(PllOscInKHz) / divm_final;
s32 divn_integer = static_cast<u8>(C.marikoEmcMaxClock / div_step_d);
s32 divn_integer = static_cast<u8>(table->rate_khz / div_step_d);
table->pllmb_divn = divn_integer;
u32 divn_fraction = static_cast<s32>((target_freq_d / div_step_d - divn_integer - 0.5) * 8192.0);
u32 actual_freq_khz = static_cast<u32>((divn_integer + 0.5 + divn_fraction * 0.000122070312) * div_step_d);
if (C.marikoEmcMaxClock - 2366001 < 133999) {
if (table->rate_khz - 2366001 < 133999) {
s32 divn_fraction_ssc = static_cast<s32>((actual_freq_khz * 0.997 / div_step_d - divn_integer - 0.5) * 8192.0);
double delta_scaled = (0.3 / div_step_d + 0.3 / div_step_d) * (divn_fraction - divn_fraction_ssc);
@@ -571,7 +583,7 @@ namespace ams::ldr::hoc::pcv::mariko {
table->pllm_ss_cfg &= 0xBFFFFFFF;
table->pllmb_ss_cfg &= 0xBFFFFFFF;
u64 pair = (static_cast<u64>(divn_fraction) << 32) | static_cast<u64>(C.marikoEmcMaxClock);
u64 pair = (static_cast<u64>(divn_fraction) << 32) | static_cast<u64>(table->rate_khz);
u32 pll_misc = (table->pllm_ss_ctrl2 & 0xFFFF0000) | static_cast<u32>((pair - actual_freq_khz) >> 32);
table->pllm_ss_ctrl2 = pll_misc;
@@ -579,80 +591,226 @@ namespace ams::ldr::hoc::pcv::mariko {
}
}
Result MemFreqMtcTable(u32 *ptr) {
u32 khz_list[] = {1600000, 1331200, 204000};
u32 khz_list_size = sizeof(khz_list) / sizeof(u32);
namespace {
std::vector<u32> newEmcList;
u32 *nsoStart;
}
// Generate list for mtc table pointers
MarikoMtcTable *table_list[khz_list_size];
for (u32 i = 0; i < khz_list_size; i++) {
u8 *table = reinterpret_cast<u8 *>(ptr) - offsetof(MarikoMtcTable, rate_khz) - i * sizeof(MarikoMtcTable);
table_list[i] = reinterpret_cast<MarikoMtcTable *>(table);
R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable());
R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
void MtcGenerateJedecTable() {
const u32 jedecFreqs[] = { 1866000, 1996000, 2133000, 2400000, 2666000, 2933000, 3200000 };
constexpr u32 JedecFreqCount = std::size(jedecFreqs);
for (u32 i = 0; i < JedecFreqCount; ++i) {
if (jedecFreqs[i] <= C.marikoEmcMaxClock) {
newEmcList.push_back(jedecFreqs[i]);
} else {
break;
}
}
if (C.marikoEmcMaxClock <= EmcClkOSLimit)
if (newEmcList.back() != C.marikoEmcMaxClock) {
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
}
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
}
void MtcGenerateFreqTables() {
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
return;
}
newEmcList.clear();
newEmcList.reserve(DvfsTableEntryCount);
newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault));
u32 stepRate = 0;
switch (C.stepMode) {
case StepMode_66MHz:
stepRate = 66667;
break;
case StepMode_100MHz:
stepRate = 100000;
break;
case StepMode_Jedec:
MtcGenerateJedecTable();
return;
default:
stepRate = 66667;
break;
}
constexpr u32 RoundHz = 1000;
for (u32 stepIndex = 1;; ++stepIndex) {
u32 newFreq = EmcClkOSLimit + stepIndex * stepRate;
newFreq = (newFreq / RoundHz) * RoundHz;
if (newFreq > C.marikoEmcMaxClock) {
if (newEmcList.back() != C.marikoEmcMaxClock) {
newEmcList.push_back(static_cast<u32>(C.marikoEmcMaxClock));
}
break;
}
newEmcList.push_back(newFreq);
}
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
}
Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) {
R_UNLESS(tableStart->rate_khz == expectedFreq, ldr::ResultInvalidMtcTable());
R_UNLESS(tableStart->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable());
R_SUCCEED();
}
Result MtcValidateAllTables(MarikoMtcTable *tableStart, const u32 *validationList, u32 tableCount) {
for (u32 i = 0; i < tableCount; ++i) {
R_UNLESS(R_SUCCEEDED(VerifyMtcTable(&tableStart[i], validationList[i])), ldr::ResultInvalidMtcTable());
}
R_SUCCEED();
}
DramId GetDramId() {
u64 id64;
splGetConfig(SplConfigItem_DramId, &id64);
return static_cast<DramId>(id64);
}
MtcTableIndex GetMtcDramIndex(DramId dramId) {
for (u32 i = 0; i < std::size(mtcIndexTable); ++i) {
if (mtcIndexTable[i].dramId == dramId) {
return mtcIndexTable[i].index;
}
}
return MtcTableIndex_Invalid;
}
NORETURN void AbortInvalidDramId() {
panic::SmcError(panic::Emc);
CRASH("Invalid dram id\n");
}
u32 GetMtcOffset(MtcTableIndex index) {
if (index < T210b0SdevEmcDvfsTableS4gb03) {
return index * mariko::MtcFullTableSize;
}
/* There are 2 erista mtc tables between T210b0SdevEmcDvfsTableS4gb01 and T210b0SdevEmcDvfsTableS4gb03, so we have to do this adjustment. */
return mariko::MtcFullTableSize * index + (2 * erista::MtcFullTableSize);
}
void PrepareMtcMemoryRegion(u8 *firstTable, MarikoMtcTable *usedTable) {
memmove(firstTable, usedTable, mariko::MtcFullTableSize);
/* Clear all other tables. */
/* 1 erista table is excluded because it's always before firstTable. */
/* We also exclude the used table obviously. */
constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * (mariko::MtcFullTableCount - 1) + (erista::MtcFullTableSize * (erista::MtcFullTableCount - 1));
memset(firstTable + mariko::MtcFullTableSize, 0, RemainingRegionSize);
}
void MtcExtendTables(MarikoMtcTable *table) {
for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) {
std::memcpy(&table[i], &table[i - 1], sizeof(MarikoMtcTable));
table[i].rate_khz = newEmcList[i];
}
}
bool patchedMtc = false;
Result MemFreqMtcTable(u32 *ptr) {
if (C.marikoEmcMaxClock <= EmcClkOSLimit || patchedMtc) {
R_SKIP();
}
MarikoMtcTable *table_alt = table_list[1], *table_max = table_list[0];
MarikoMtcTable *tmp = new MarikoMtcTable;
static const DramId dramId = [] {
DramId id = GetDramId();
id = IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL;
return id;
}();
// Copy unmodified 1600000 table to tmp
std::memcpy(reinterpret_cast<void *>(tmp), reinterpret_cast<void *>(table_max), sizeof(MarikoMtcTable));
static const MtcTableIndex mtcIndex = [] {
MtcTableIndex idx = GetMtcDramIndex(dramId);
/* If for some reason this happens, there is no chance of recovering this. */
if (idx == MtcTableIndex_Invalid) {
AbortInvalidDramId();
}
return idx;
}();
/* Adjust timings properly according to the new frequency. */
MemMtcTableAutoAdjust(table_max);
/* Offset to dram id specific mtc table. */
static const u32 mtcOffset = GetMtcOffset(mtcIndex);
MemMtcPllmbDivisor(table_max);
// Overwrite 13312000 table with unmodified 1600000 table copied back
std::memcpy(reinterpret_cast<void *>(table_alt), reinterpret_cast<void *>(tmp), sizeof(MarikoMtcTable));
/* Offset from 1600MHz pointer to 204Mhz table start. */
constexpr u32 StartAdjustment = offsetof(MarikoMtcTable, rate_khz) + sizeof(MarikoMtcTable) * 2;
u8 *startPtr = reinterpret_cast<u8 *>(ptr) - StartAdjustment;
MarikoMtcTable *table = reinterpret_cast<MarikoMtcTable *>(startPtr + mtcOffset);
R_UNLESS(R_SUCCEEDED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault)), ldr::ResultInvalidMtcTable());
delete tmp;
table = reinterpret_cast<MarikoMtcTable *>(startPtr);
PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
PrepareMtcMemoryRegion(startPtr, table);
if (R_FAILED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault))) {
panic::SmcError(panic::Emc);
}
MtcExtendTables(table);
if (R_FAILED(MtcValidateAllTables(table, newEmcList.data(), newEmcList.size()))) {
panic::SmcError(panic::Emc);
}
for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) {
MemMtcTableAutoAdjust(&table[i]);
MemMtcPllmbDivisor(&table[i]);
}
patchedMtc = true;
R_SUCCEED();
}
Result MemFreqDvbTable(u32 *ptr) {
emc_dvb_dvfs_table_t *default_end = reinterpret_cast<emc_dvb_dvfs_table_t *>(ptr);
emc_dvb_dvfs_table_t *new_start = default_end + 1;
DvbEntry *default_end = reinterpret_cast<DvbEntry *>(ptr);
DvbEntry *new_start = default_end + 1;
// Validate existing table
void *mem_dvb_table_head = reinterpret_cast<u8 *>(new_start) - sizeof(EmcDvbTableDefault);
bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0;
bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0;
R_UNLESS(validated, ldr::ResultInvalidDvbTable());
if (C.marikoEmcMaxClock <= EmcClkOSLimit)
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
R_SKIP();
int32_t voltAdd = 25 * C.emcDvbShift;
#define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000),
/* TODO: More fine tuned values? */
if (C.marikoEmcMaxClock < 1862400) {
std::memcpy(new_start, default_end, sizeof(emc_dvb_dvfs_table_t));
} else if (C.marikoEmcMaxClock < 2131200) {
emc_dvb_dvfs_table_t oc_table = {1862400, {700, 675, 650, }};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
} else if (C.marikoEmcMaxClock < 2400000) {
emc_dvb_dvfs_table_t oc_table = {2131200, { 725, 700, 675} };
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
} else if (C.marikoEmcMaxClock < 2665600) {
emc_dvb_dvfs_table_t oc_table = {2400000, {DVB_VOLT(750, 725, 700)}};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
} else if (C.marikoEmcMaxClock < 2931200) {
emc_dvb_dvfs_table_t oc_table = {2665600, {DVB_VOLT(775, 750, 725)}};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
} else if (C.marikoEmcMaxClock < 3200000) {
emc_dvb_dvfs_table_t oc_table = {2931200, {DVB_VOLT(800, 775, 750)}};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
} else {
emc_dvb_dvfs_table_t oc_table = {3200000, {DVB_VOLT(800, 800, 775)}};
std::memcpy(new_start, &oc_table, sizeof(emc_dvb_dvfs_table_t));
}
new_start->freq = C.marikoEmcMaxClock;
s32 voltAdd = 25 * C.emcDvbShift;
#define DVB_VOLT(zero, one, two) std::min(zero + voltAdd, 1050), std::min(one + voltAdd, 1025), std::min(two + voltAdd, 1000),
DvbEntry emcDvbTableNew[] = {
{ 204000, { 637, 637, 637, } },
{ 1331200, { 650, 637, 637, } },
{ 1600000, { 675, 650, 637, } },
{ 1866000, { DVB_VOLT(700, 675, 650) } },
{ 2133000, { DVB_VOLT(725, 700, 675) } },
{ 2400000, { DVB_VOLT(750, 725, 700) } },
{ 2666000, { DVB_VOLT(775, 750, 725) } },
{ 2933000, { DVB_VOLT(800, 775, 750) } },
{ 3200000, { DVB_VOLT(800, 800, 775) } },
{ 0xFFFFFFFF, { } },
};
u32 j = MtcTableCountDefault;
for (u32 i = MtcTableCountDefault; i < newEmcList.size(); ++i) {
if (newEmcList[i] >= emcDvbTableNew[j].freq && newEmcList[i] < emcDvbTableNew[j + 1].freq) {
emcDvbTableNew[j].freq = newEmcList[i];
++j;
} else {
break;
}
}
std::memset(mem_dvb_table_head, 0, sizeof(EmcDvbTableDefault));
std::memcpy(mem_dvb_table_head, &emcDvbTableNew, sizeof(emcDvbTableNew));
/* Max dvfs entry is 32, but HOS doesn't seem to boot if exact freq doesn't exist in dvb table,
reason why it's like this
*/
@@ -661,8 +819,9 @@ namespace ams::ldr::hoc::pcv::mariko {
}
Result MemFreqMax(u32 *ptr) {
if (C.marikoEmcMaxClock <= EmcClkOSLimit)
if (C.marikoEmcMaxClock <= EmcClkOSLimit) {
R_SKIP();
}
PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
R_SUCCEED();
@@ -676,12 +835,13 @@ namespace ams::ldr::hoc::pcv::mariko {
I2cSession _session;
Result res = i2cOpenSession(&_session, dev);
if (R_FAILED(res))
if (R_FAILED(res)) {
return res;
}
cmd.reg = reg;
cmd.val = val;
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
i2csessionClose(&_session);
return res;
}
@@ -693,21 +853,24 @@ namespace ams::ldr::hoc::pcv::mariko {
constexpr u32 uv_min = 250'000;
auto validator = [entry]() {
R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
R_SUCCEED();
};
R_TRY(validator());
u32 emc_uv = C.marikoEmcVddqVolt;
if (!emc_uv)
R_SKIP();
if (emc_uv % uv_step)
if (!emc_uv) {
R_SKIP();
}
if (emc_uv % uv_step) {
emc_uv = (emc_uv + uv_step - 1) / uv_step * uv_step; // rounding
}
PATCH_OFFSET(ptr, emc_uv);
@@ -722,28 +885,61 @@ namespace ams::ldr::hoc::pcv::mariko {
return resultI2C;
}
Result MemMtcTableAsm(u32 *ptr) {
constexpr u32 AddpOffset = 1;
constexpr u32 BrOffset = 12;
constexpr u32 MovOffset = 10;
/* Ensure we don't dereference memory before nso start. */
R_UNLESS(ptr - BrOffset >= nsoStart, ldr::ResultInvalidMtcTablePattern());
u32 adrp = *(ptr - AddpOffset);
R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTablePattern());
/* We don't check for matching register because both registers must be x0 in order to pass the previous checks. */
/* The correct instructions will always be x0 since the mtcTable pointer is returned. */
/* Pray this does not break. */
u32 br = *(ptr - BrOffset);
R_UNLESS(AsmCompareBrNoRd(br, MtcBrAsm), ldr::ResultInvalidMtcTablePattern());
/* Pray this does not break either. */
u32 mov = *(ptr - MovOffset);
R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTablePattern());
u8 movRd = asm_get_rd(mov);
u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), movRd);
PATCH_OFFSET(ptr - BrOffset, NopIns);
PATCH_OFFSET(ptr - MovOffset, movCountPatch);
R_SUCCEED();
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
MtcGenerateFreqTables();
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
PatcherEntry<u32> patches[] = {
{"CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit},
{"CPU Freq Table", CpuFreqCvbTable<true>, 1, nullptr, CpuCvbDefaultMaxFreq},
{"CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial},
{"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial},
{"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0x0000FFCF},
{"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial},
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial},
{"GPU Freq Table", GpuFreqCvbTable<true>, 1, nullptr, GpuCvbDefaultMaxFreq},
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn},
{"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax},
{"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit},
{"MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit},
{"MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit},
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit},
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit},
{"MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault},
{"MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default},
{ "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit },
{ "CPU Freq Table", CpuFreqCvbTable<true>, 1, nullptr, CpuCvbDefaultMaxFreq },
{ "CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial },
{ "CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial },
{ "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low },
{ "GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial },
{ "GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
{ "GPU Freq Table", GpuFreqCvbTable<true>, 1, nullptr, GpuCvbDefaultMaxFreq },
{ "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
{ "GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
{ "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
{ "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit },
{ "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit },
{ "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
{ "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
{ "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault },
{ "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default },
{ "Mem Table Asm", &MemMtcTableAsm, 0, &MemMtcGetGetTablePatternFn },
};
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) {

View File

@@ -0,0 +1,285 @@
/*
* Copyright (C) Switch-OC-Suite
*
* Copyright (c) 2023 hanai3Bi
*
* 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 "../oc_common.hpp"
#include "pcv_common.hpp"
#include "pcv_asm.hpp"
namespace ams::ldr::hoc::pcv::mariko {
constexpr cvb_entry_t CpuCvbTableDefault[] = {
{ 204000, { 721589, -12695, 27 }, { } },
{ 306000, { 747134, -14195, 27 }, { } },
{ 408000, { 776324, -15705, 27 }, { } },
{ 510000, { 809160, -17205, 27 }, { } },
{ 612000, { 845641, -18715, 27 }, { } },
{ 714000, { 885768, -20215, 27 }, { } },
{ 816000, { 929540, -21725, 27 }, { } },
{ 918000, { 976958, -23225, 27 }, { } },
{ 1020000, { 1028021, -24725, 27 }, { 1120000 } },
{ 1122000, { 1082730, -26235, 27 }, { 1120000 } },
{ 1224000, { 1141084, -27735, 27 }, { 1120000 } },
{ 1326000, { 1203084, -29245, 27 }, { 1120000 } },
{ 1428000, { 1268729, -30745, 27 }, { 1120000 } },
{ 1581000, { 1374032, -33005, 27 }, { 1120000 } },
{ 1683000, { 1448791, -34505, 27 }, { 1120000 } },
{ 1785000, { 1527196, -36015, 27 }, { 1120000 } },
{ 1887000, { 1609246, -37515, 27 }, { 1120000 } },
{ 1963500, { 1675751, -38635, 27 }, { 1120000 } },
{ },
};
constexpr u32 CpuClkOfficial = 1963'500;
constexpr u32 CpuVoltOfficial = 1120;
constexpr u32 CpuVminOfficial = 620;
constexpr u32 CpuTune0Low = 0xFFCF;
static const u32 cpuVoltagePatchValues[] = { 850, 38, 1120, 1000, 100, 1000, 0 };
static const s32 cpuVoltagePatchOffsets[] = { -2, -1, 5, 6, 7, 8, 9 };
static_assert(sizeof(cpuVoltagePatchValues) == sizeof(cpuVoltagePatchOffsets), "Invalid cpuVoltagePatch size");
static const u32 cpuVoltThermalData[] = { 620, 1120, 20000, 620, 1120, 70000, 950, 1132, 0, 950, 1227, 0 };
static const u32 allowedCpuMaxFrequencies[] = { 1'963'500, 2'091'000, 2'193'000, 2'295'000, 2'397'000, 2'499'000, 2'601'000, 2'703'000, };
constexpr cvb_entry_t GpuCvbTableDefault[] = {
// GPUB01_NA_CVB_TABLE
{ 76800, {}, { 610000, } },
{ 153600, {}, { 610000, } },
{ 230400, {}, { 610000, } },
{ 307200, {}, { 610000, } },
{ 384000, {}, { 610000, } },
{ 460800, {}, { 610000, } },
{ 537600, {}, { 801688, -10900, -163, 298, -10599, 162, } },
{ 614400, {}, { 824214, -5743, -452, 238, -6325, 81, } },
{ 691200, {}, { 848830, -3903, -552, 119, -4030, -2, } },
{ 768000, {}, { 891575, -4409, -584, 0, -2849, 39, } },
{ 844800, {}, { 940071, -5367, -602, -60, -63, -93, } },
{ 921600, {}, { 986765, -6637, -614, -179, 1905, -13, } },
{ 998400, {}, { 1098475, -13529, -497, -179, 3626, 9, } },
{ 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40, } },
{ 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110, } },
{ 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313, } },
{ 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559, } },
{ },
};
constexpr u32 GpuClkPllMax = 1300'000'000;
constexpr u32 GpuClkPllLimit = 2'600'000;
constexpr u32 GpuVminOfficial = 610;
static const u32 gpuDVFSPattern[] = { 1050, 1000, 100, 1000, 10, };
static const u32 gpuVoltThermalPattern[] = { 800, 1120, 0, 610, 1120, 20000, 610, 1120, 30000, 610, 1120, 50000, 610, 1120, 70000, 610, 1120, 90000, };
static_assert(sizeof(gpuVoltThermalPattern) == 72, "Invalid gpuVoltThermalPattern");
/* GPU Max Clock asm Pattern:
*
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
*
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
* sf | opc | | hw | imm16 | Rd
* #31 |30 29|28 27 26 25 24 23|22 21|20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 |4 3 2 1 0
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
*/
inline constexpr u32 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 };
inline bool GpuMaxClockPatternFn(u32 *ptr32) {
return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]);
}
struct DvbEntry {
u64 freq;
s32 volt[4] = {};
};
constexpr DvbEntry EmcDvbTableDefault[] = {
{ 204000, { 637, 637, 637, } },
{ 408000, { 637, 637, 637, } },
{ 800000, { 637, 637, 637, } },
{ 1065600, { 637, 637, 637, } },
{ 1331200, { 650, 637, 637, } },
{ 1600000, { 675, 650, 637, } },
};
constexpr u32 EmcListDefault[] = { 204000, 1331200, 1600000, };
constexpr u32 EmcListSizeDefault = std::size(EmcListDefault);
constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1;
constexpr u32 EmcRateStep = 33'000;
constexpr u32 EmcRateStepScale = 33'200;
constexpr u32 EmcClkOSAlt = 1331'200;
constexpr u32 EmcClkPllmLimit = 2133'000'000;
constexpr u32 EmcVddqDefault = 600'000;
constexpr u32 MemVdd2Default = 1100'000;
constexpr u32 MTC_TABLE_REV = 3;
constexpr u32 MtcTableCountDefault = 3;
constexpr size_t MtcFullTableSize = sizeof(MarikoMtcTable) * MtcTableCountDefault;
constexpr u32 MtcFullTableCount = 17;
/* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */
enum DramId : u64 {
HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 3,
AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 5,
IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 6,
IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 8,
IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 9,
IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 10,
IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE = 11,
HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 12,
HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 13,
HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 14,
HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE = 15,
IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 17,
IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 18,
HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 19,
IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 20,
HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 21,
AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 22,
HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 23,
AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 24,
IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 25,
HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF = 26,
AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 27,
AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 28,
IOWA_4GB_HYNIX_H54G46CYRBX267 = 29,
HOAG_4GB_HYNIX_H54G46CYRBX267 = 30,
AULA_4GB_HYNIX_H54G46CYRBX267 = 31,
IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 32,
HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB = 33,
AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 34,
};
enum MtcTableIndex {
T210b0SdevEmcDvfsTableS4gb01 = 0, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableS4gb03 = 1, /* Samsung AM-MGCJ 4Gb */
T210b0SdevEmcDvfsTableS8gb03 = 2, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableH4gb03 = 3, /* Hynix NME 4Gb */
T210b0SdevEmcDvfsTableM4gb03 = 4, /* Micron WT:F 4Gb */
T210b0SdevEmcDvfsTableS4gbY01 = 5, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableS1y4gbY01 = 6, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableS1y8gbY01 = 7, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableS1y4gbX03 = 8, /* Samsung AA-MGCL 4Gb */
T210b0SdevEmcDvfsTableS1y8gbX03 = 9, /* Samsung AA-MGCL 8Gb */
T210b0SdevEmcDvfsTableS1y4gb01 = 10, /* (Unused) Samsung 4Gb */
T210b0SdevEmcDvfsTableM1y4gb01 = 11, /* Micron WT:E 4Gb */
T210b0SdevEmcDvfsTableH1y4gb01 = 12, /* Hynix NEE 4Gb */
T210b0SdevEmcDvfsTableS1y8gb04 = 13, /* Samsung AM-MGCJ 8Gb */
T210b0SdevEmcDvfsTableS1z4gb01 = 14, /* Samsung AB-MGCL 4Gb */
T210b0SdevEmcDvfsTableH1a4gb01 = 15, /* Hynix x267 4Gb */
T210b0SdevEmcDvfsTableM1a4gb01 = 16, /* Micron WT:B 8Gb */
MtcTableIndex_Invalid = 17,
};
struct MtcDramIndex {
DramId dramId;
MtcTableIndex index;
};
constexpr MtcDramIndex mtcIndexTable[] = {
{ HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
{ AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
{ IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, T210b0SdevEmcDvfsTableH1y4gb01, },
{ IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, T210b0SdevEmcDvfsTableS4gb03, },
{ IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, T210b0SdevEmcDvfsTableS1y8gb04, },
{ IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME, T210b0SdevEmcDvfsTableH4gb03, },
{ IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE, T210b0SdevEmcDvfsTableM1y4gb01, },
{ HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, T210b0SdevEmcDvfsTableS4gb03, },
{ HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, T210b0SdevEmcDvfsTableS1y8gb04, },
{ HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME, T210b0SdevEmcDvfsTableH4gb03, },
{ HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE, T210b0SdevEmcDvfsTableM1y4gb01, },
{ IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
{ IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
{ HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
{ IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1z4gb01, },
{ HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1y8gb04, },
{ AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, T210b0SdevEmcDvfsTableS1y8gb04, },
{ HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
{ AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, },
{ IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
{ HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
{ AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF, T210b0SdevEmcDvfsTableM4gb03, },
{ AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, T210b0SdevEmcDvfsTableS1y8gbX03, },
{ IOWA_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
{ HOAG_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
{ AULA_4GB_HYNIX_H54G46CYRBX267, T210b0SdevEmcDvfsTableH1a4gb01, },
{ IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
{ HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
{ AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB, T210b0SdevEmcDvfsTableM1a4gb01, },
};
/*
710006abfc 40 01 1f d6 br x10
*/
/*
710006ac28 a0 03 00 90 adrp x0,0x71000de000
710006ac2c 00 80 16 91 add x0=>SdevEmcDvfsTableS4gb01,x0,#0x5a0
*/
/* Br */
/*
| Z | OP | Fixed | A | M | RN | RM
31 30 29 28 27 26 25 | 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 |11 | 10 | 9 8 7 6 5 | 4 3 2 1 0
1 1 0 1 0 1 1 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 Rn 0 0 0 0 0
Z op A M Rm
*/
/* Adrp */
/*
OP | ImmLow | ImmHigh | RD
31 | 30 29 28 27 26 25 24 | 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 | 4 3 2 1 0
*/
/* ADD (immediate) */
/*
SF | OP | S | Fixed value | Sh | Imm12 | RN | RD
31 | 30 | 29 | 28 27 26 25 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0
*/
constexpr u32 MtcBrAsm = 0xD61F0140;
constexpr u32 MtcMovAsm = 0x52800068;
constexpr u32 MtcAdrpAsm = 0x900003A0;
constexpr u32 MtcAddAsm = 0x91168000;
ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) {
/* This builds an address that gets returned, so the register must be x0 by convention. */
return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
}
void Patch(uintptr_t mapped_nso, size_t nso_size);
}

View File

@@ -38,7 +38,7 @@ include $(DEVKITPRO)/libnx/switch_rules
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
APP_TITLE := Horizon OC Monitor
APP_VERSION := 1.3.2+r4-hoc-r3
APP_VERSION := 1.3.2+r4-hoc-r4
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
@@ -65,6 +65,7 @@ IS_STATUS_MONITOR_DIRECTIVE := 1
CFLAGS += -DIS_STATUS_MONITOR_DIRECTIVE=$(IS_STATUS_MONITOR_DIRECTIVE)
# Enable appearance overriding
export MSYS2_ARG_CONV_EXCL := -DUI_OVERRIDE_PATH
UI_OVERRIDE_PATH := /config/status-monitor/
CFLAGS += -DUI_OVERRIDE_PATH="\"$(UI_OVERRIDE_PATH)\""

View File

@@ -583,8 +583,12 @@ void Misc(void*) {
realCPU_Hz = hocclkCTX.realFreqs[HocClkModule_CPU];
realGPU_Hz = hocclkCTX.realFreqs[HocClkModule_GPU];
realRAM_Hz = hocclkCTX.realFreqs[HocClkModule_MEM];
partLoad[HocClkPartLoad_EMC] = hocclkCTX.partLoad[HocClkPartLoad_EMC];
partLoad[HocClkPartLoad_EMCCpu] = hocclkCTX.partLoad[HocClkPartLoad_EMCCpu];
partLoad[HocClkPartLoad_EMC] = hocclkCTX.partLoad[HocClkPartLoad_EMC];
partLoad[HocClkPartLoad_EMCCpu] = hocclkCTX.partLoad[HocClkPartLoad_EMCCpu];
partLoad[HocClkPartLoad_RamBWAll] = hocclkCTX.partLoad[HocClkPartLoad_RamBWAll];
partLoad[HocClkPartLoad_RamBWCpu] = hocclkCTX.partLoad[HocClkPartLoad_RamBWCpu];
partLoad[HocClkPartLoad_RamBWGpu] = hocclkCTX.partLoad[HocClkPartLoad_RamBWGpu];
partLoad[HocClkPartLoad_RamBWPeak] = hocclkCTX.partLoad[HocClkPartLoad_RamBWPeak];
realCPU_Temp = hocclkCTX.temps[HocClkThermalSensor_CPU];
realGPU_Temp = hocclkCTX.temps[HocClkThermalSensor_GPU];
realPLLX_Temp = hocclkCTX.temps[HocClkThermalSensor_PLLX];
@@ -714,8 +718,12 @@ void Misc3(void*) {
if (R_SUCCEEDED(hocclkCheck)) {
HocClkContext hocclkCTX;
if (R_SUCCEEDED(hocclkIpcGetCurrentContext(&hocclkCTX))) {
partLoad[HocClkPartLoad_EMC] = hocclkCTX.partLoad[HocClkPartLoad_EMC];
partLoad[HocClkPartLoad_EMCCpu] = hocclkCTX.partLoad[HocClkPartLoad_EMCCpu];
partLoad[HocClkPartLoad_EMC] = hocclkCTX.partLoad[HocClkPartLoad_EMC];
partLoad[HocClkPartLoad_EMCCpu] = hocclkCTX.partLoad[HocClkPartLoad_EMCCpu];
partLoad[HocClkPartLoad_RamBWAll] = hocclkCTX.partLoad[HocClkPartLoad_RamBWAll];
partLoad[HocClkPartLoad_RamBWCpu] = hocclkCTX.partLoad[HocClkPartLoad_RamBWCpu];
partLoad[HocClkPartLoad_RamBWGpu] = hocclkCTX.partLoad[HocClkPartLoad_RamBWGpu];
partLoad[HocClkPartLoad_RamBWPeak] = hocclkCTX.partLoad[HocClkPartLoad_RamBWPeak];
realCPU_Temp = hocclkCTX.temps[HocClkThermalSensor_CPU];
realGPU_Temp = hocclkCTX.temps[HocClkThermalSensor_GPU];
@@ -1274,6 +1282,7 @@ struct FullSettings {
bool showFPS;
bool showRES;
bool showRDSD;
std::string ramInfoMode;
bool useDynamicColors;
bool disableScreenshots;
uint16_t separatorColor;
@@ -1309,6 +1318,7 @@ struct MiniSettings {
std::string show;
bool showpartLoad;
bool showpartLoadCPUGPU;
std::string ramInfoMode;
bool invertBatteryDisplay;
bool disableScreenshots;
bool sleepExit;
@@ -1343,6 +1353,7 @@ struct MicroSettings {
uint16_t textColor;
std::string show;
bool showpartLoad;
std::string ramInfoMode;
bool setPosBottom;
bool disableScreenshots;
bool sleepExit;
@@ -1430,6 +1441,7 @@ ALWAYS_INLINE void GetConfigSettings(MiniSettings* settings) {
settings->show = "DTC+BAT+CPU+GPU+RAM+TMP+FPS+RES";
settings->showpartLoad = true;
settings->showpartLoadCPUGPU = false;
settings->ramInfoMode = "LOAD";
settings->invertBatteryDisplay = true;
settings->refreshRate = 1;
settings->disableScreenshots = false;
@@ -1639,6 +1651,12 @@ ALWAYS_INLINE void GetConfigSettings(MiniSettings* settings) {
settings->showpartLoadCPUGPU = (key != "FALSE");
}
// Process RAM info mode
it = section.find("ram_info_mode");
if (it != section.end()) {
settings->ramInfoMode = it->second;
}
// Invert the battery display value
it = section.find("invert_battery_display");
if (it != section.end()) {
@@ -1727,6 +1745,7 @@ ALWAYS_INLINE void GetConfigSettings(MicroSettings* settings) {
convertStrToRGBA4444("#FFFF", &(settings->textColor));
settings->show = "FPS+CPU+GPU+RAM+SOC+BAT+DTC";
settings->showpartLoad = true;
settings->ramInfoMode = "LOAD";
settings->setPosBottom = false;
settings->disableScreenshots = false;
settings->sleepExit = false;
@@ -1926,6 +1945,12 @@ ALWAYS_INLINE void GetConfigSettings(MicroSettings* settings) {
settings->showpartLoad = (key != "FALSE");
}
// Process RAM info mode
it = section.find("ram_info_mode");
if (it != section.end()) {
settings->ramInfoMode = it->second;
}
// Process show string
it = section.find("show");
if (it != section.end()) {
@@ -2257,6 +2282,7 @@ ALWAYS_INLINE void GetConfigSettings(FullSettings* settings) {
settings->showFPS = true;
settings->showRES = true;
settings->showRDSD = true;
settings->ramInfoMode = "LOAD";
settings->useDynamicColors = true;
settings->disableScreenshots = false;
convertStrToRGBA4444("#888F", &(settings->separatorColor));
@@ -2352,6 +2378,11 @@ ALWAYS_INLINE void GetConfigSettings(FullSettings* settings) {
settings->showRDSD = !(key == "FALSE");
}
it = section.find("ram_info_mode");
if (it != section.end()) {
settings->ramInfoMode = it->second;
}
it = section.find("use_dynamic_colors");
if (it != section.end()) {
key = it->second;

View File

@@ -377,6 +377,21 @@ public:
});
list->addItem(showRDSD);
{
const std::string curRamInfoMode = getCurrentRamInfoMode("full");
auto* ramInfoModeItem = new tsl::elm::ListItem("RAM Info Mode");
ramInfoModeItem->setValue(curRamInfoMode);
ramInfoModeItem->setClickListener([this, ramInfoModeItem](u64 keys) -> bool {
if (!(keys & KEY_A)) return false;
const std::string cur = ult::parseValueFromIniSection(configIniPath, "full", "ram_info_mode");
const std::string next = cur == "Bandwidth" ? "Load" : "Bandwidth";
ult::setIniFileValue(configIniPath, "full", "ram_info_mode", next);
ramInfoModeItem->setValue(next);
return true;
});
list->addItem(ramInfoModeItem);
}
auto* dynamicColors = new tsl::elm::ToggleListItem("Use Dynamic Colors", getCurrentUseDynamicColors());
dynamicColors->setStateChangedListener([this](bool state) {
ult::setIniFileValue(configIniPath, "fps-graph", "use_dynamic_colors", state ? "true" : "false");
@@ -417,13 +432,13 @@ public:
});
list->addItem(showFullCPU);
auto* showVDDQ = new tsl::elm::ToggleListItem("VDD2", getCurrentShowVDDQ());
auto* showVDDQ = new tsl::elm::ToggleListItem("VDDQ", getCurrentShowVDDQ());
showVDDQ->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "show_vddq", state ? "true" : "false");
});
list->addItem(showVDDQ);
auto* showVDD2 = new tsl::elm::ToggleListItem("VDDQ", getCurrentShowVDD2());
auto* showVDD2 = new tsl::elm::ToggleListItem("VDD2", getCurrentShowVDD2());
showVDD2->setStateChangedListener([this, section](bool state) {
ult::setIniFileValue(configIniPath, section, "show_vdd2", state ? "true" : "false");
});
@@ -449,6 +464,21 @@ public:
list->addItem(partLoadCPUGPU);
}
if (isMiniMode || isMicroMode) {
const std::string curRamInfoMode = getCurrentRamInfoMode(section);
auto* ramInfoModeItem = new tsl::elm::ListItem("RAM Info Mode");
ramInfoModeItem->setValue(curRamInfoMode);
ramInfoModeItem->setClickListener([this, section, ramInfoModeItem](u64 keys) -> bool {
if (!(keys & KEY_A)) return false;
const std::string cur = ult::parseValueFromIniSection(configIniPath, section, "ram_info_mode");
const std::string next = cur == "Bandwidth" ? "Load" : "Bandwidth";
ult::setIniFileValue(configIniPath, section, "ram_info_mode", next);
ramInfoModeItem->setValue(next);
return true;
});
list->addItem(ramInfoModeItem);
}
if (isMiniMode || isMicroMode) {
auto* invertBatteryDisplay = new tsl::elm::ToggleListItem("Invert Battery Display", getCurrentInvertBatteryDisplay());
invertBatteryDisplay->setStateChangedListener([this, section](bool state) {
@@ -616,6 +646,11 @@ private:
return value != "FALSE";
}
std::string getCurrentRamInfoMode(const std::string& section) {
const std::string value = ult::parseValueFromIniSection(configIniPath, section, "ram_info_mode");
return (value == "Bandwidth") ? "Bandwidth" : "Load";
}
bool getCurrentInvertBatteryDisplay() {
const std::string section = isMiniMode ? "mini" : "micro";
std::string value = ult::parseValueFromIniSection(configIniPath, section, "invert_battery_display");

View File

@@ -26,6 +26,11 @@ private:
char BatteryDraw_c[64] = "";
char FPS_var_compressed_c[64] = "";
char RAM_load_c[64] = "";
char RAM_load2_c[64] = "";
char RAM_bw_peak_c[16] = "";
char RAM_bw_total_c[16] = "";
char RAM_bw_gpu_c[16] = "";
char RAM_bw_cpu_c[16] = "";
char Resolutions_c[64] = "";
char readSpeed_c[32] = "";
@@ -237,17 +242,40 @@ public:
renderer->drawString(DeltaRAM_c, false, COMMON_MARGIN + deltaOffset, height_offset, 15, (settings.textColor));
}
if (R_SUCCEEDED(hocclkCheck)) {
static std::vector<std::string> partLoadColoredChars = {"CPU", "GPU"};
//static auto loadLabelWidth = renderer->getTextDimensions("Load: ", false, 15).first;
renderer->drawString("Load", false, COMMON_MARGIN, height_offset+15, 15, (settings.catColor2));
renderer->drawStringWithColoredSections(RAM_load_c, false, partLoadColoredChars, COMMON_MARGIN + valueOffset, height_offset+15, 15, (settings.textColor), settings.catColor2);
if (settings.ramInfoMode == "Bandwidth") {
// Fixed column layout: labels at fixed x, values at fixed x after max label width
// Col1: Peak/GPU labels, Col2: Total/CPU labels
// Values start at a fixed offset so columns don't shift with different digit counts
static const uint32_t bwLbl1W = renderer->getTextDimensions("Peak ", false, 15).first;
static const uint32_t bwLbl2W = renderer->getTextDimensions("Total ", false, 15).first;
static const uint32_t bwValW = renderer->getTextDimensions("99.9 GB/s", false, 15).first;
static const uint32_t bwGap = renderer->getTextDimensions(" ", false, 15).first;
const uint32_t xV1 = COMMON_MARGIN + bwLbl1W;
const uint32_t xL2 = xV1 + bwValW + bwGap;
const uint32_t xV2 = xL2 + bwLbl2W;
// Row 1: Peak ... Total
renderer->drawString("Peak", false, COMMON_MARGIN, height_offset+15, 15, settings.catColor2);
renderer->drawString(RAM_bw_peak_c, false, xV1, height_offset+15, 15, settings.textColor);
renderer->drawString("Total", false, xL2, height_offset+15, 15, settings.catColor2);
renderer->drawString(RAM_bw_total_c, false, xV2, height_offset+15, 15, settings.textColor);
// Row 2: GPU ... CPU
renderer->drawString("GPU", false, COMMON_MARGIN, height_offset+30, 15, settings.catColor2);
renderer->drawString(RAM_bw_gpu_c, false, xV1, height_offset+30, 15, settings.textColor);
renderer->drawString("CPU", false, xL2, height_offset+30, 15, settings.catColor2);
renderer->drawString(RAM_bw_cpu_c, false, xV2, height_offset+30, 15, settings.textColor);
} else {
static std::vector<std::string> partLoadColoredChars = {"CPU", "GPU"};
renderer->drawString("Load", false, COMMON_MARGIN, height_offset+15, 15, (settings.catColor2));
renderer->drawStringWithColoredSections(RAM_load_c, false, partLoadColoredChars, COMMON_MARGIN + valueOffset, height_offset+15, 15, (settings.textColor), settings.catColor2);
}
}
}
if (R_SUCCEEDED(Hinted)) {
const uint32_t ramUsageOffset = (R_SUCCEEDED(hocclkCheck) && settings.ramInfoMode == "Bandwidth") ? height_offset + 50 : height_offset + 40;
//static auto textWidth = renderer->getTextDimensions("Total \nApplication \nApplet \nSystem \nSystem Unsafe ", false, 15).first;
renderer->drawString("Total\nApplication\nApplet\nSystem\nSystem Unsafe", false, COMMON_MARGIN, height_offset + 40, 15, (settings.catColor2));
renderer->drawString(RAM_var_compressed_c, false, COMMON_MARGIN + valueOffset, height_offset + 40, 15, (settings.textColor));
renderer->drawString(RAM_percentage_var_compressed_c, false, ramPercentageOffset, height_offset + 40, 15, (settings.textColor));
renderer->drawString("Total\nApplication\nApplet\nSystem\nSystem Unsafe", false, COMMON_MARGIN, ramUsageOffset, 15, (settings.catColor2));
renderer->drawString(RAM_var_compressed_c, false, COMMON_MARGIN + valueOffset, ramUsageOffset, 15, (settings.textColor));
renderer->drawString(RAM_percentage_var_compressed_c, false, ramPercentageOffset, ramUsageOffset, 15, (settings.textColor));
}
}
@@ -484,12 +512,27 @@ public:
);
if (R_SUCCEEDED(hocclkCheck)) {
const int RAM_GPU_Load = partLoad[HocClkPartLoad_EMC] - partLoad[HocClkPartLoad_EMCCpu];
snprintf(RAM_load_c, sizeof RAM_load_c,
"%u.%u%% CPU %u.%u%% GPU %u.%u%%",
partLoad[HocClkPartLoad_EMC] / 10, partLoad[HocClkPartLoad_EMC] % 10,
partLoad[HocClkPartLoad_EMCCpu] / 10, partLoad[HocClkPartLoad_EMCCpu] % 10,
RAM_GPU_Load / 10, RAM_GPU_Load % 10);
if (settings.ramInfoMode == "Bandwidth") {
const unsigned bwAll = partLoad[HocClkPartLoad_RamBWAll] / 1000;
const unsigned bwAllD = (partLoad[HocClkPartLoad_RamBWAll] % 1000) / 100;
const unsigned bwPeak = partLoad[HocClkPartLoad_RamBWPeak] / 1000;
const unsigned bwPeakD= (partLoad[HocClkPartLoad_RamBWPeak]% 1000) / 100;
const unsigned bwCpu = partLoad[HocClkPartLoad_RamBWCpu] / 1000;
const unsigned bwCpuD = (partLoad[HocClkPartLoad_RamBWCpu] % 1000) / 100;
const unsigned bwGpu = partLoad[HocClkPartLoad_RamBWGpu] / 1000;
const unsigned bwGpuD = (partLoad[HocClkPartLoad_RamBWGpu] % 1000) / 100;
snprintf(RAM_bw_peak_c, sizeof RAM_bw_peak_c, "%u.%u GB/s", bwPeak, bwPeakD);
snprintf(RAM_bw_total_c, sizeof RAM_bw_total_c, "%u.%u GB/s", bwAll, bwAllD);
snprintf(RAM_bw_gpu_c, sizeof RAM_bw_gpu_c, "%u.%u GB/s", bwGpu, bwGpuD);
snprintf(RAM_bw_cpu_c, sizeof RAM_bw_cpu_c, "%u.%u GB/s", bwCpu, bwCpuD);
} else {
const int RAM_GPU_Load = partLoad[HocClkPartLoad_EMC] - partLoad[HocClkPartLoad_EMCCpu];
snprintf(RAM_load_c, sizeof RAM_load_c,
"%u.%u%% CPU %u.%u%% GPU %u.%u%%",
partLoad[HocClkPartLoad_EMC] / 10, partLoad[HocClkPartLoad_EMC] % 10,
partLoad[HocClkPartLoad_EMCCpu] / 10, partLoad[HocClkPartLoad_EMCCpu] % 10,
RAM_GPU_Load / 10, RAM_GPU_Load % 10);
}
}
///Thermal
snprintf(SOC_temperature_c, sizeof SOC_temperature_c, "%.1f\u00B0C", SOC_temperatureF);

View File

@@ -955,6 +955,11 @@ public:
const float RAM_Total_all_f = (RAM_Total_application_u + RAM_Total_applet_u + RAM_Total_system_u + RAM_Total_systemunsafe_u) / (1024.0f * 1024.0f * 1024.0f);
const float RAM_Used_all_f = (RAM_Used_application_u + RAM_Used_applet_u + RAM_Used_system_u + RAM_Used_systemunsafe_u) / (1024.0f * 1024.0f * 1024.0f);
snprintf(MICRO_RAM_all_c, sizeof(MICRO_RAM_all_c), "%.0f%.0fGB", RAM_Used_all_f, RAM_Total_all_f);
} else if (settings.ramInfoMode == "Bandwidth" && R_SUCCEEDED(hocclkCheck)) {
// Bandwidth mode: show GB/s from context (partLoad values are in MB/s)
const unsigned bwAll = partLoad[HocClkPartLoad_RamBWAll] / 1000;
const unsigned bwAllD = (partLoad[HocClkPartLoad_RamBWAll] % 1000) / 100;
snprintf(MICRO_RAM_all_c, sizeof(MICRO_RAM_all_c), "%u.%uGB/s", bwAll, bwAllD);
} else {
// User wants percentage display
if (R_SUCCEEDED(hocclkCheck)) {

View File

@@ -362,8 +362,32 @@ public:
width = renderer->getTextDimensions("100%@4444.4444 mV", false, fontsize).first;
}
if (settings.realTemps) {
width += renderer->getTextDimensions(" 888.8°C", false, fontsize).first;
width += renderer->getTextDimensions(" 88.8°C ", false, fontsize).first;
}
} else if (key == "RAM" && settings.ramInfoMode == "Bandwidth" && R_SUCCEEDED(hocclkCheck)) {
width = renderer->getTextDimensions("99.9GB/s@4444.4", false, fontsize).first;
if (settings.realVolts) {
if (isMariko) {
if (settings.showVDD2 && settings.decimalVDD2 && settings.showVDDQ)
width += renderer->getTextDimensions("4444.4444 mV 4444 mV", false, fontsize).first;
else if (settings.showVDD2 && !settings.decimalVDD2 && settings.showVDDQ)
width += renderer->getTextDimensions("4444 mV 4444 mV", false, fontsize).first;
else if (settings.showVDD2 && settings.decimalVDD2)
width += renderer->getTextDimensions("4444.4 mV", false, fontsize).first;
else if (settings.showVDD2 && !settings.decimalVDD2)
width += renderer->getTextDimensions("4444 mV", false, fontsize).first;
else if (settings.showVDDQ)
width += renderer->getTextDimensions("4444 mV", false, fontsize).first;
} else {
if (settings.decimalVDD2)
width += renderer->getTextDimensions("4444.44 mV", false, fontsize).first;
else
width += renderer->getTextDimensions("4444 mV", false, fontsize).first;
}
}
if (settings.realTemps) {
width += renderer->getTextDimensions(" 88.8°C ", false, fontsize).first;
}
} else if (key == "GPU" || (key == "RAM" && settings.showpartLoad && R_SUCCEEDED(hocclkCheck))) {
//dimensions = renderer->drawString("100.0%@4444.4", false, 0, 0, fontsize, renderer->a(0x0000));
@@ -380,15 +404,15 @@ public:
width = renderer->getTextDimensions("100%[100%,100%]@4444.4444 mV", false, fontsize).first;
}
}
if (key == "GPU" && settings.realTemps) {
width += renderer->getTextDimensions(" 88.8°C", false, fontsize).first;
}
if (settings.realTemps) {
width += renderer->getTextDimensions(" 88.8°C ", false, fontsize).first;
}
} else if (key == "RAM" && (!settings.showpartLoad || R_FAILED(hocclkCheck))) {
//dimensions = renderer->drawString("44444444MB@4444.4", false, 0, 0, fontsize, renderer->a(0x0000));
if (!settings.realVolts) {
width = renderer->getTextDimensions("100%@4444.4", false, fontsize).first;
if (settings.realTemps) {
width += renderer->getTextDimensions(" 88.8°C", false, fontsize).first;
width += renderer->getTextDimensions(" 88.8°C ", false, fontsize).first;
}
} else {
if (isMariko) {
@@ -1142,53 +1166,64 @@ public:
RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
}
} else {
unsigned partLoadInt;
if (R_SUCCEEDED(hocclkCheck)) {
partLoadInt = partLoad[HocClkPartLoad_EMC] / 10;
if (settings.showpartLoadCPUGPU) {
unsigned ramCpuLoadInt = partLoad[HocClkPartLoad_EMCCpu] / 10;
int RAM_GPU_Load = partLoad[HocClkPartLoad_EMC] - partLoad[HocClkPartLoad_EMCCpu];
unsigned ramGpuLoadInt = RAM_GPU_Load / 10;
if (settings.realFrequencies && realRAM_Hz) {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%[%u%%,%u%%]@%hu.%hhu",
partLoadInt, ramCpuLoadInt, ramGpuLoadInt,
realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
} else {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%[%u%%,%u%%]@%hu.%hhu",
partLoadInt, ramCpuLoadInt, ramGpuLoadInt,
RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
}
} else {
if (settings.realFrequencies && realRAM_Hz) {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%@%hu.%hhu", partLoadInt,
realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
} else {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%@%hu.%hhu", partLoadInt,
RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
}
}
if (settings.ramInfoMode == "Bandwidth" && R_SUCCEEDED(hocclkCheck)) {
// Bandwidth mode: show GB/s from context (partLoad values are in MB/s)
const uint32_t ramFreq = settings.realFrequencies && realRAM_Hz ? realRAM_Hz : RAM_Hz;
const unsigned bwAll = partLoad[HocClkPartLoad_RamBWAll] / 1000;
const unsigned bwAllD = (partLoad[HocClkPartLoad_RamBWAll] % 1000) / 100;
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u.%uGB/s@%hu.%hhu",
bwAll, bwAllD,
ramFreq / 1000000, (ramFreq / 100000) % 10);
} else {
const uint64_t RAM_Total_all = RAM_Total_application_u + RAM_Total_applet_u +
RAM_Total_system_u + RAM_Total_systemunsafe_u;
const uint64_t RAM_Used_all = RAM_Used_application_u + RAM_Used_applet_u +
RAM_Used_system_u + RAM_Used_systemunsafe_u;
partLoadInt = (RAM_Total_all > 0) ? (unsigned)((RAM_Used_all * 100) / RAM_Total_all) : 0;
unsigned partLoadInt;
if (settings.realFrequencies && realRAM_Hz) {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%@%hu.%hhu", partLoadInt,
realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
if (R_SUCCEEDED(hocclkCheck)) {
partLoadInt = partLoad[HocClkPartLoad_EMC] / 10;
if (settings.showpartLoadCPUGPU) {
unsigned ramCpuLoadInt = partLoad[HocClkPartLoad_EMCCpu] / 10;
int RAM_GPU_Load = partLoad[HocClkPartLoad_EMC] - partLoad[HocClkPartLoad_EMCCpu];
unsigned ramGpuLoadInt = RAM_GPU_Load / 10;
if (settings.realFrequencies && realRAM_Hz) {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%[%u%%,%u%%]@%hu.%hhu",
partLoadInt, ramCpuLoadInt, ramGpuLoadInt,
realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
} else {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%[%u%%,%u%%]@%hu.%hhu",
partLoadInt, ramCpuLoadInt, ramGpuLoadInt,
RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
}
} else {
if (settings.realFrequencies && realRAM_Hz) {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%@%hu.%hhu", partLoadInt,
realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
} else {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%@%hu.%hhu", partLoadInt,
RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
}
}
} else {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%@%hu.%hhu", partLoadInt,
RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
const uint64_t RAM_Total_all = RAM_Total_application_u + RAM_Total_applet_u +
RAM_Total_system_u + RAM_Total_systemunsafe_u;
const uint64_t RAM_Used_all = RAM_Used_application_u + RAM_Used_applet_u +
RAM_Used_system_u + RAM_Used_systemunsafe_u;
partLoadInt = (RAM_Total_all > 0) ? (unsigned)((RAM_Used_all * 100) / RAM_Total_all) : 0;
if (settings.realFrequencies && realRAM_Hz) {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%@%hu.%hhu", partLoadInt,
realRAM_Hz / 1000000, (realRAM_Hz / 100000) % 10);
} else {
snprintf(MINI_RAM_var_compressed_c, sizeof(MINI_RAM_var_compressed_c),
"%u%%@%hu.%hhu", partLoadInt,
RAM_Hz / 1000000, (RAM_Hz / 100000) % 10);
}
}
}
}

View File

@@ -0,0 +1,81 @@
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/base_rules
################################################################################
IPL_LOAD_ADDR := 0x40010000
################################################################################
THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST)))
CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE)))
BUILDDIR := build
OUTPUTDIR := out
SOURCEDIR = fatal_handler
BDKDIR := bdk
GFXDIR := fatal_handler/gfx
TARGET := fatal_handler
BDKINC := -I$(BDKDIR)
GFXINC := -I$(GFXDIR)
GFX_INC := '"gfx.h"'
VPATH = $(dir ./$(SOURCEDIR)/) $(dir $(wildcard ./$(SOURCEDIR)/*/)) $(dir $(wildcard ./$(SOURCEDIR)/*/*/))
VPATH += $(dir $(wildcard ./$(BDKDIR)/)) $(dir $(wildcard ./$(BDKDIR)/*/)) $(dir $(wildcard ./$(BDKDIR)/*/*/))
OBJS = $(addprefix $(BUILDDIR)/$(TARGET)/, \
start.o exception_handlers.o irq.o main.o \
timer.o heap.o hw_init.o clock.o i2c.o gpio.o \
max7762x.o di.o util.o fuse.o pinmux.o \
secmon_exo.o gfx.o bpmp.o sdram.o minerva.o btn.o max77620-rtc.o se.o mc.o)
################################################################################
# BDK defines.
CUSTOMDEFINES := -DGFX_INC=$(GFX_INC)
CUSTOMDEFINES += -DBDK_MALLOC_NO_DEFRAG -DBDK_MC_ENABLE_AHB_REDIRECT -DBDK_EMUMMC_ENABLE
CUSTOMDEFINES += -DBDK_WATCHDOG_FIQ_ENABLE -DBDK_RESTART_BL_ON_WDT
INCDIRS := $(BDKINC) $(GFXINC)
WARNINGS := -Wall -Wsign-compare -Wno-array-bounds -Wno-stringop-overflow
ARCH := -march=armv4t -mtune=arm7tdmi -mthumb-interwork
CFLAGS = $(ARCH) -O2 -g -nostdlib -ffunction-sections -fdata-sections -fomit-frame-pointer -std=gnu11 $(WARNINGS) $(CUSTOMDEFINES)
LDFLAGS = $(ARCH) -nostartfiles -lgcc -Wl,--nmagic,--gc-sections -Xlinker --defsym=IPL_LOAD_ADDR=$(IPL_LOAD_ADDR)
################################################################################
BIN_TARGET := $(OUTPUTDIR)/$(TARGET).bin
BIN_OBJ := $(OUTPUTDIR)/$(TARGET).bin.o
BIN_HEADER := $(OUTPUTDIR)/$(TARGET)_bin.h
BIN_SYM := $(subst .,_,$(subst /,_,$(TARGET)))_bin
define bin2o
$(eval SYM := $(BIN_SYM))
$(eval BIN_SIZE := $(shell wc -c < $(BIN_TARGET)))
@$(OBJCOPY) --input-target binary --output-target elf32-littlearm --binary-architecture arm \
--redefine-sym _binary_$(shell echo $(BIN_TARGET) | tr '/.-' '___')_start=$(SYM) \
--redefine-sym _binary_$(shell echo $(BIN_TARGET) | tr '/.-' '___')_end=$(SYM)_end \
--redefine-sym _binary_$(shell echo $(BIN_TARGET) | tr '/.-' '___')_size=$(SYM)_size \
$(BIN_TARGET) $(BIN_OBJ)
@printf '#pragma once\n#include <stddef.h>\n#include <stdint.h>\nextern const uint8_t $(SYM)[];\nextern const uint8_t $(SYM)_end[];\n#if __cplusplus >= 201103L\nstatic constexpr size_t $(SYM)_size=$(BIN_SIZE);\n#else\nstatic const size_t $(SYM)_size=$(BIN_SIZE);\n#endif\n' > $(BIN_HEADER)
endef
################################################################################
.PHONY: all clean
all: $(BIN_OBJ) $(BIN_HEADER)
$(BIN_OBJ) $(BIN_HEADER): $(BIN_TARGET)
$(bin2o)
$(BIN_TARGET): $(BUILDDIR)/$(TARGET)/$(TARGET).elf
$(OBJCOPY) -S -O binary $< $(BIN_TARGET)
clean:
@rm -rf $(OBJS) $(BIN_OBJ) $(BIN_HEADER)
$(BUILDDIR)/$(TARGET)/$(TARGET).elf: $(OBJS)
@$(CC) $(LDFLAGS) -T $(SOURCEDIR)/link.ld $^ -o $@
$(BUILDDIR)/$(TARGET)/%.o: %.c
@$(CC) $(CFLAGS) $(INCDIRS) -c $< -o $@
$(BUILDDIR)/$(TARGET)/%.o: %.S
@$(CC) $(CFLAGS) -c $< -o $@
$(OBJS): $(BUILDDIR)/$(TARGET)
$(BUILDDIR)/$(TARGET):
@mkdir -p "$(BUILDDIR)"
@mkdir -p "$(BUILDDIR)/$(TARGET)"
@mkdir -p "$(OUTPUTDIR)"

View File

@@ -0,0 +1,558 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2022 CTCaer
*
* 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 <stdarg.h>
#include <string.h>
#include "gfx.h"
// Global gfx console and context.
gfx_ctxt_t gfx_ctxt;
gfx_con_t gfx_con;
static bool gfx_con_init_done = false;
static const u8 _gfx_font[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Char 032 ( )
0x00, 0x30, 0x30, 0x18, 0x18, 0x00, 0x0C, 0x00, // Char 033 (!)
0x00, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, // Char 034 (")
0x00, 0x66, 0x66, 0xFF, 0x66, 0xFF, 0x66, 0x66, // Char 035 (#)
0x00, 0x18, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x18, // Char 036 ($)
0x00, 0x46, 0x66, 0x30, 0x18, 0x0C, 0x66, 0x62, // Char 037 (%)
0x00, 0x3C, 0x66, 0x3C, 0x1C, 0xE6, 0x66, 0xFC, // Char 038 (&)
0x00, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, // Char 039 (')
0x00, 0x30, 0x18, 0x0C, 0x0C, 0x18, 0x30, 0x00, // Char 040 (()
0x00, 0x0C, 0x18, 0x30, 0x30, 0x18, 0x0C, 0x00, // Char 041 ())
0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // Char 042 (*)
0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, // Char 043 (+)
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 044 (,)
0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // Char 045 (-)
0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, // Char 046 (.)
0x00, 0x40, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, // Char 047 (/)
0x00, 0x3C, 0x66, 0x76, 0x6E, 0x66, 0x3C, 0x00, // Char 048 (0)
0x00, 0x18, 0x1C, 0x18, 0x18, 0x18, 0x7E, 0x00, // Char 049 (1)
0x00, 0x3C, 0x62, 0x30, 0x0C, 0x06, 0x7E, 0x00, // Char 050 (2)
0x00, 0x3C, 0x62, 0x38, 0x60, 0x66, 0x3C, 0x00, // Char 051 (3)
0x00, 0x6C, 0x6C, 0x66, 0xFE, 0x60, 0x60, 0x00, // Char 052 (4)
0x00, 0x7E, 0x06, 0x7E, 0x60, 0x66, 0x3C, 0x00, // Char 053 (5)
0x00, 0x3C, 0x06, 0x3E, 0x66, 0x66, 0x3C, 0x00, // Char 054 (6)
0x00, 0x7E, 0x30, 0x30, 0x18, 0x18, 0x18, 0x00, // Char 055 (7)
0x00, 0x3C, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00, // Char 056 (8)
0x00, 0x3C, 0x66, 0x7C, 0x60, 0x66, 0x3C, 0x00, // Char 057 (9)
0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, // Char 058 (:)
0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x0C, 0x00, // Char 059 (;)
0x00, 0x70, 0x1C, 0x06, 0x06, 0x1C, 0x70, 0x00, // Char 060 (<)
0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // Char 061 (=)
0x00, 0x0E, 0x38, 0x60, 0x60, 0x38, 0x0E, 0x00, // Char 062 (>)
0x00, 0x3C, 0x66, 0x30, 0x18, 0x00, 0x18, 0x00, // Char 063 (?)
0x00, 0x3C, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3C, // Char 064 (@)
0x00, 0x3C, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 065 (A)
0x00, 0x3E, 0x66, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 066 (B)
0x00, 0x3C, 0x66, 0x06, 0x06, 0x66, 0x3C, 0x00, // Char 067 (C)
0x00, 0x1E, 0x36, 0x66, 0x66, 0x36, 0x1E, 0x00, // Char 068 (D)
0x00, 0x7E, 0x06, 0x1E, 0x06, 0x06, 0x7E, 0x00, // Char 069 (E)
0x00, 0x3E, 0x06, 0x1E, 0x06, 0x06, 0x06, 0x00, // Char 070 (F)
0x00, 0x3C, 0x66, 0x06, 0x76, 0x66, 0x3C, 0x00, // Char 071 (G)
0x00, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00, // Char 072 (H)
0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 073 (I)
0x00, 0x78, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, // Char 074 (J)
0x00, 0x66, 0x36, 0x1E, 0x1E, 0x36, 0x66, 0x00, // Char 075 (K)
0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7E, 0x00, // Char 076 (L)
0x00, 0x46, 0x6E, 0x7E, 0x56, 0x46, 0x46, 0x00, // Char 077 (M)
0x00, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x66, 0x00, // Char 078 (N)
0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 079 (O)
0x00, 0x3E, 0x66, 0x3E, 0x06, 0x06, 0x06, 0x00, // Char 080 (P)
0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x70, 0x00, // Char 081 (Q)
0x00, 0x3E, 0x66, 0x3E, 0x1E, 0x36, 0x66, 0x00, // Char 082 (R)
0x00, 0x3C, 0x66, 0x0C, 0x30, 0x66, 0x3C, 0x00, // Char 083 (S)
0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, // Char 084 (T)
0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 085 (U)
0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 086 (V)
0x00, 0x46, 0x46, 0x56, 0x7E, 0x6E, 0x46, 0x00, // Char 087 (W)
0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00, // Char 088 (X)
0x00, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00, // Char 089 (Y)
0x00, 0x7E, 0x30, 0x18, 0x0C, 0x06, 0x7E, 0x00, // Char 090 (Z)
0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, // Char 091 ([)
0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // Char 092 (\)
0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, // Char 093 (])
0x00, 0x18, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, // Char 094 (^)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // Char 095 (_)
0x00, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, // Char 096 (`)
0x00, 0x00, 0x3C, 0x60, 0x7C, 0x66, 0x7C, 0x00, // Char 097 (a)
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3E, 0x00, // Char 098 (b)
0x00, 0x00, 0x3C, 0x06, 0x06, 0x06, 0x3C, 0x00, // Char 099 (c)
0x00, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x7C, 0x00, // Char 100 (d)
0x00, 0x00, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00, // Char 101 (e)
0x00, 0x38, 0x0C, 0x3E, 0x0C, 0x0C, 0x0C, 0x00, // Char 102 (f)
0x00, 0x00, 0x7C, 0x66, 0x7C, 0x40, 0x3C, 0x00, // Char 103 (g)
0x00, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x00, // Char 104 (h)
0x00, 0x18, 0x00, 0x1C, 0x18, 0x18, 0x3C, 0x00, // Char 105 (i)
0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x1E, 0x00, // Char 106 (j)
0x00, 0x06, 0x06, 0x36, 0x1E, 0x36, 0x66, 0x00, // Char 107 (k)
0x00, 0x1C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, // Char 108 (l)
0x00, 0x00, 0x66, 0xFE, 0xFE, 0xD6, 0xC6, 0x00, // Char 109 (m)
0x00, 0x00, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x00, // Char 110 (n)
0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00, // Char 111 (o)
0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x00, // Char 112 (p)
0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x00, // Char 113 (q)
0x00, 0x00, 0x3E, 0x66, 0x06, 0x06, 0x06, 0x00, // Char 114 (r)
0x00, 0x00, 0x7C, 0x06, 0x3C, 0x60, 0x3E, 0x00, // Char 115 (s)
0x00, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x70, 0x00, // Char 116 (t)
0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x00, // Char 117 (u)
0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, // Char 118 (v)
0x00, 0x00, 0xC6, 0xD6, 0xFE, 0x7C, 0x6C, 0x00, // Char 119 (w)
0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00, // Char 120 (x)
0x00, 0x00, 0x66, 0x66, 0x7C, 0x60, 0x3C, 0x00, // Char 121 (y)
0x00, 0x00, 0x7E, 0x30, 0x18, 0x0C, 0x7E, 0x00, // Char 122 (z)
0x00, 0x18, 0x08, 0x08, 0x04, 0x08, 0x08, 0x18, // Char 123 ({)
0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // Char 124 (|)
0x00, 0x0C, 0x08, 0x08, 0x10, 0x08, 0x08, 0x0C, // Char 125 (})
0x00, 0x00, 0x00, 0x4C, 0x32, 0x00, 0x00, 0x00 // Char 126 (~)
};
void gfx_clear_grey(u8 color)
{
memset(gfx_ctxt.fb, color, gfx_ctxt.width * gfx_ctxt.height * 4);
}
void gfx_clear_partial_grey(u8 color, u32 pos_x, u32 height)
{
memset(gfx_ctxt.fb + pos_x * gfx_ctxt.stride, color, height * 4 * gfx_ctxt.stride);
}
void gfx_clear_color(u32 color)
{
for (u32 i = 0; i < gfx_ctxt.width * gfx_ctxt.height; i++)
gfx_ctxt.fb[i] = color;
}
void gfx_init_ctxt(u32 *fb, u32 width, u32 height, u32 stride)
{
gfx_ctxt.fb = fb;
gfx_ctxt.width = width;
gfx_ctxt.height = height;
gfx_ctxt.stride = stride;
}
void gfx_con_init()
{
gfx_con.gfx_ctxt = &gfx_ctxt;
gfx_con.fntsz = 16;
gfx_con.x = 0;
gfx_con.y = 0;
gfx_con.savedx = 0;
gfx_con.savedy = 0;
gfx_con.fgcol = TXT_CLR_DEFAULT;
gfx_con.fillbg = 1;
gfx_con.bgcol = TXT_CLR_BG;
gfx_con.mute = 0;
gfx_con_init_done = true;
}
void gfx_con_setcol(u32 fgcol, int fillbg, u32 bgcol)
{
gfx_con.fgcol = fgcol;
gfx_con.fillbg = fillbg;
gfx_con.bgcol = bgcol;
}
void gfx_con_getpos(u32 *x, u32 *y)
{
*x = gfx_con.x;
*y = gfx_con.y;
}
void gfx_con_setpos(u32 x, u32 y)
{
gfx_con.x = x;
gfx_con.y = y;
}
void gfx_putc(char c)
{
// Duplicate code for performance reasons.
switch (gfx_con.fntsz)
{
case 16:
if (c >= 32 && c <= 126)
{
u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)];
u32 *fb = gfx_ctxt.fb + gfx_con.x + gfx_con.y * gfx_ctxt.stride;
for (u32 i = 0; i < 16; i += 2)
{
u8 v = *cbuf;
for (u32 k = 0; k < 2; k++)
{
for (u32 j = 0; j < 8; j++)
{
if (v & 1)
{
*fb = gfx_con.fgcol;
fb++;
*fb = gfx_con.fgcol;
}
else if (gfx_con.fillbg)
{
*fb = gfx_con.bgcol;
fb++;
*fb = gfx_con.bgcol;
}
else
fb++;
v >>= 1;
fb++;
}
fb += gfx_ctxt.stride - 16;
v = *cbuf;
}
cbuf++;
}
gfx_con.x += 16;
}
else if (c == '\n')
{
gfx_con.x = 0;
gfx_con.y += 16;
if (gfx_con.y > gfx_ctxt.height - 16)
gfx_con.y = 0;
}
break;
case 8:
default:
if (c >= 32 && c <= 126)
{
u8 *cbuf = (u8 *)&_gfx_font[8 * (c - 32)];
u32 *fb = gfx_ctxt.fb + gfx_con.x + gfx_con.y * gfx_ctxt.stride;
for (u32 i = 0; i < 8; i++)
{
u8 v = *cbuf++;
for (u32 j = 0; j < 8; j++)
{
if (v & 1)
*fb = gfx_con.fgcol;
else if (gfx_con.fillbg)
*fb = gfx_con.bgcol;
v >>= 1;
fb++;
}
fb += gfx_ctxt.stride - 8;
}
gfx_con.x += 8;
}
else if (c == '\n')
{
gfx_con.x = 0;
gfx_con.y += 8;
if (gfx_con.y > gfx_ctxt.height - 8)
gfx_con.y = 0;
}
break;
}
}
void gfx_puts(const char *s)
{
if (!s || !gfx_con_init_done || gfx_con.mute)
return;
for (; *s; s++)
gfx_putc(*s);
}
static void _gfx_putn(u32 v, int base, char fill, int fcnt)
{
static const char digits[] = "0123456789ABCDEF";
char *p;
char buf[65];
int c = fcnt;
bool negative = false;
if (base != 10 && base != 16)
return;
// Account for negative numbers.
if (base == 10 && v & 0x80000000)
{
negative = true;
v = (int)v * -1;
c--;
}
p = buf + 64;
*p = 0;
do
{
c--;
*--p = digits[v % base];
v /= base;
} while (v);
if (negative)
*--p = '-';
if (fill != 0)
{
while (c > 0 && p > buf)
{
*--p = fill;
c--;
}
}
gfx_puts(p);
}
void gfx_put_small_sep()
{
u8 prevFontSize = gfx_con.fntsz;
gfx_con.fntsz = 8;
gfx_putc('\n');
gfx_con.fntsz = prevFontSize;
}
void gfx_put_big_sep()
{
u8 prevFontSize = gfx_con.fntsz;
gfx_con.fntsz = 16;
gfx_putc('\n');
gfx_con.fntsz = prevFontSize;
}
void gfx_printf(const char *fmt, ...)
{
if (!gfx_con_init_done || gfx_con.mute)
return;
va_list ap;
int fill, fcnt;
va_start(ap, fmt);
while (*fmt)
{
if (*fmt == '%')
{
fmt++;
fill = 0;
fcnt = 0;
if ((*fmt >= '0' && *fmt <= '9') || *fmt == ' ')
{
fcnt = *fmt;
fmt++;
if (*fmt >= '0' && *fmt <= '9')
{
fill = fcnt;
fcnt = *fmt - '0';
fmt++;
}
else
{
fill = ' ';
fcnt -= '0';
}
}
switch(*fmt)
{
case 'c':
gfx_putc(va_arg(ap, u32));
break;
case 's':
gfx_puts(va_arg(ap, char *));
break;
case 'd':
_gfx_putn(va_arg(ap, u32), 10, fill, fcnt);
break;
case 'p':
case 'P':
case 'x':
case 'X':
_gfx_putn(va_arg(ap, u32), 16, fill, fcnt);
break;
case 'k':
gfx_con.fgcol = va_arg(ap, u32);
break;
case 'K':
gfx_con.bgcol = va_arg(ap, u32);
gfx_con.fillbg = 1;
break;
case '%':
gfx_putc('%');
break;
case '\0':
goto out;
default:
gfx_putc('%');
gfx_putc(*fmt);
break;
}
}
else
gfx_putc(*fmt);
fmt++;
}
out:
va_end(ap);
}
static void _gfx_cputs(u32 color, const char *s)
{
gfx_con.fgcol = color;
gfx_puts(s);
gfx_putc('\n');
gfx_con.fgcol = TXT_CLR_DEFAULT;
}
void gfx_wputs(const char *s) { _gfx_cputs(TXT_CLR_WARNING, s); }
void gfx_eputs(const char *s) { _gfx_cputs(TXT_CLR_ERROR, s); }
void gfx_hexdump(u32 base, const void *buf, u32 len)
{
if (!gfx_con_init_done || gfx_con.mute)
return;
u8 *buff = (u8 *)buf;
u8 prevFontSize = gfx_con.fntsz;
gfx_con.fntsz = 8;
for (u32 i = 0; i < len; i++)
{
if (i % 0x10 == 0)
{
if (i != 0)
{
gfx_puts("| ");
for (u32 j = 0; j < 0x10; j++)
{
u8 c = buff[i - 0x10 + j];
if (c >= 32 && c <= 126)
gfx_putc(c);
else
gfx_putc('.');
}
gfx_putc('\n');
}
gfx_printf("%08x: ", base + i);
}
gfx_printf("%02x ", buff[i]);
if (i == len - 1)
{
int ln = len % 0x10 != 0;
u32 k = 0x10 - 1;
if (ln)
{
k = (len & 0xF) - 1;
for (u32 j = 0; j < 0x10 - k; j++)
gfx_puts(" ");
}
gfx_puts("| ");
for (u32 j = 0; j < (ln ? k : k + 1); j++)
{
u8 c = buff[i - k + j];
if (c >= 32 && c <= 126)
gfx_putc(c);
else
gfx_putc('.');
}
gfx_putc('\n');
}
}
gfx_putc('\n');
gfx_con.fntsz = prevFontSize;
}
static int abs(int x)
{
if (x < 0)
return -x;
return x;
}
void gfx_set_pixel(u32 x, u32 y, u32 color)
{
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = color;
}
void gfx_line(int x0, int y0, int x1, int y1, u32 color)
{
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = (dx > dy ? dx : -dy) / 2, e2;
while (1)
{
gfx_set_pixel(x0, y0, color);
if (x0 == x1 && y0 == y1)
break;
e2 = err;
if (e2 >-dx)
{
err -= dy;
x0 += sx;
}
if (e2 < dy)
{
err += dx;
y0 += sy;
}
}
}
void gfx_set_rect_grey(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
{
u32 pos = 0;
for (u32 y = pos_y; y < (pos_y + size_y); y++)
{
for (u32 x = pos_x; x < (pos_x + size_x); x++)
{
memset(&gfx_ctxt.fb[x + y*gfx_ctxt.stride], buf[pos], 4);
pos++;
}
}
}
void gfx_set_rect_rgb(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
{
u32 pos = 0;
for (u32 y = pos_y; y < (pos_y + size_y); y++)
{
for (u32 x = pos_x; x < (pos_x + size_x); x++)
{
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = buf[pos + 2] | (buf[pos + 1] << 8) | (buf[pos] << 16);
pos+=3;
}
}
}
void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
{
u32 *ptr = (u32 *)buf;
for (u32 y = pos_y; y < (pos_y + size_y); y++)
for (u32 x = pos_x; x < (pos_x + size_x); x++)
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = *ptr++;
}
void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y)
{
for (u32 y = pos_y; y < (pos_y + size_y); y++)
{
for (u32 x = pos_x; x < (pos_x + size_x); x++)
gfx_ctxt.fb[x + y * gfx_ctxt.stride] = buf[(size_y + pos_y - 1 - y ) * size_x + x - pos_x];
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2021 CTCaer
* Copyright (c) 2018 M4xw
*
* 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/>.
*/
#ifndef _GFX_H_
#define _GFX_H_
#include <bdk.h>
#define TXT_CLR_BG 0xFF1B1B1B // Dark Grey.
#define TXT_CLR_DEFAULT 0xFFCCCCCC // Light Grey.
#define TXT_CLR_WARNING 0xFFFFDD00 // Yellow.
#define TXT_CLR_ERROR 0xFFFF0000 // Red.
#define TXT_CLR_CYAN_L 0xFF00CCFF // Light Cyan.
#define TXT_CLR_TURQUOISE 0xFF00FFCC // Turquoise.
#define TXT_CLR_ORANGE 0xFFFFBA00 // Orange.
#define TXT_CLR_GREENISH 0xFF96FF00 // Toxic Green.
#define TXT_CLR_GREEN_D 0xFF008800 // Dark Green.
#define TXT_CLR_RED_D 0xFF880000 // Dark Red.
#define TXT_CLR_GREY_D 0xFF303030 // Darkest Grey.
#define TXT_CLR_GREY_DM 0xFF444444 // Darker Grey.
#define TXT_CLR_GREY_M 0xFF555555 // Dark Grey.
#define TXT_CLR_GREY 0xFF888888 // Grey.
#define EPRINTF(text) gfx_eputs(text)
#define EPRINTFARGS(text, args...) gfx_printf("%k"text"%k\n", TXT_CLR_ERROR, args, TXT_CLR_DEFAULT)
#define WPRINTF(text) gfx_wputs(text)
#define WPRINTFARGS(text, args...) gfx_printf("%k"text"%k\n", TXT_CLR_WARNING, args, TXT_CLR_DEFAULT)
typedef struct _gfx_ctxt_t
{
u32 *fb;
u32 width;
u32 height;
u32 stride;
} gfx_ctxt_t;
typedef struct _gfx_con_t
{
gfx_ctxt_t *gfx_ctxt;
u32 fntsz;
u32 x;
u32 y;
u32 savedx;
u32 savedy;
u32 fgcol;
int fillbg;
u32 bgcol;
bool mute;
} gfx_con_t;
// Global gfx console and context.
extern gfx_ctxt_t gfx_ctxt;
extern gfx_con_t gfx_con;
void gfx_init_ctxt(u32 *fb, u32 width, u32 height, u32 stride);
void gfx_clear_grey(u8 color);
void gfx_clear_partial_grey(u8 color, u32 pos_x, u32 height);
void gfx_clear_color(u32 color);
void gfx_con_init();
void gfx_con_setcol(u32 fgcol, int fillbg, u32 bgcol);
void gfx_con_getpos(u32 *x, u32 *y);
void gfx_con_setpos(u32 x, u32 y);
void gfx_putc(char c);
void gfx_puts(const char *s);
void gfx_wputs(const char *s);
void gfx_eputs(const char *s);
void gfx_printf(const char *fmt, ...) /* __attribute__((format(printf, 1, 2))) */;
void gfx_hexdump(u32 base, const void *buf, u32 len);
void gfx_set_pixel(u32 x, u32 y, u32 color);
void gfx_line(int x0, int y0, int x1, int y1, u32 color);
void gfx_put_small_sep();
void gfx_put_big_sep();
void gfx_set_rect_grey(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
void gfx_set_rect_rgb(const u8 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
void gfx_set_rect_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
void gfx_render_bmp_argb(const u32 *buf, u32 size_x, u32 size_y, u32 pos_x, u32 pos_y);
#endif

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2018-2024 CTCaer
* Copyright (c) 2019 Atmosphère-NX
*
* 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 <bdk.h>
#include "secmon_exo.h"
// Atmosphère reboot-to-fatal-error.
typedef struct _atm_fatal_error_ctx
{
u32 magic;
u32 error_desc;
u64 title_id;
union
{
u64 gprs[32];
struct
{
u64 _gprs[29];
u64 fp;
u64 lr;
u64 sp;
};
};
u64 pc;
u64 module_base;
u32 pstate;
u32 afsr0;
u32 afsr1;
u32 esr;
u64 far;
u64 report_identifier; // Normally just system tick.
u64 stack_trace_size;
u64 stack_dump_size;
u64 stack_trace[0x20];
u8 stack_dump[0x100];
u8 tls[0x100];
} atm_fatal_error_ctx;
#define ATM_FATAL_ERR_CTX_ADDR 0x4003E000
#define ATM_FATAL_MAGIC 0x30454641 // AFE0
#define HOS_PID_BOOT2 0x8
static const char *get_error_desc(u32 error_desc)
{
switch (error_desc)
{
case 0x100:
return "IABRT"; // Instruction Abort.
case 0x101:
return "DABRT"; // Data Abort.
case 0x102:
return "IUA"; // Instruction Unaligned Access.
case 0x103:
return "DUA"; // Data Unaligned Access.
case 0x104:
return "UDF"; // Undefined Instruction.
case 0x106:
return "SYS"; // System Error.
case 0x301:
return "SVC"; // Bad arguments or unimplemented SVC.
case 0xF00:
return "KRNL"; // Kernel panic.
case 0xFFD:
return "SO"; // Stack Overflow.
case 0xFFE:
return "std::abort";
default:
return "UNK";
}
}
void secmon_exo_check_panic()
{
volatile atm_fatal_error_ctx *rpt = (atm_fatal_error_ctx *)ATM_FATAL_ERR_CTX_ADDR;
// Mask magic to maintain compatibility with any AFE version, thanks to additive struct members.
if ((rpt->magic & 0xF0FFFFFF) != ATM_FATAL_MAGIC)
return;
gfx_clear_grey(0x1B);
gfx_con_setpos(0, 0);
WPRINTF("Panic occurred while running Atmosphere.\n\n");
WPRINTFARGS("Title ID: %08X%08X", (u32)((u64)rpt->title_id >> 32), (u32)rpt->title_id);
WPRINTFARGS("Error: %s (0x%x)\n", get_error_desc(rpt->error_desc), rpt->error_desc);
// Check if mixed atmosphere sysmodules.
if ((u32)rpt->title_id == HOS_PID_BOOT2)
WPRINTF("Mismatched Atmosphere files?\n");
// Change magic to invalid, to prevent double-display of error/bootlooping.
rpt->magic = 0;
display_backlight_brightness(100, 1000);
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2021 CTCaer
*
* 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/>.
*/
#ifndef _SECMON_EXO_H_
#define _SECMON_EXO_H_
#include <bdk.h>
void secmon_exo_check_panic();
#endif

View File

@@ -0,0 +1,25 @@
ENTRY(_start)
SECTIONS {
PROVIDE(__ipl_start = IPL_LOAD_ADDR);
. = __ipl_start;
.text : {
*(.text._start);
KEEP(*(._boot_cfg));
KEEP(*(._ipl_version));
*(.text._irq_setup);
*(.text*);
}
.data : {
*(.data*);
*(.rodata*);
}
. = ALIGN(0x10);
__ipl_end = .;
.bss : {
__bss_start = .;
*(COMMON)
*(.bss*)
__bss_end = .;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2018 naehrwert
*
* Copyright (c) 2018-2024 CTCaer
*
* 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 <bdk.h>
#include "hos/secmon_exo.h"
typedef struct _log_ctx
{
u32 magic;
u32 sz;
u32 start;
u32 end;
char buf[];
} log_ctx_t;
#define IRAM_LOG_CTX_ADDR 0x4003C000
static void check_log(){
volatile log_ctx_t *log_ctx = (log_ctx_t*)IRAM_LOG_CTX_ADDR;
if(log_ctx->magic == 0xaabbccdd){
gfx_printf("\nLogs:\n");
gfx_printf((char*)log_ctx->buf);
}
}
volatile nyx_storage_t *nyx_str = (nyx_storage_t *)NYX_STORAGE_ADDR;
extern void pivot_stack(u32 stack_top);
void ipl_main()
{
// Do initial HW configuration. This is compatible with consecutive reruns without a reset.
hw_init();
// Pivot the stack under IPL. (Only max 4KB is needed).
pivot_stack(IPL_LOAD_ADDR);
// Place heap at a place outside of L4T/HOS configuration and binaries.
heap_init((void *)IPL_HEAP_START);
// Prep RTC regs for read. Needed for T210B01 R2C.
max77620_rtc_prep_read();
// Initialize display.
display_init();
u32 *fb = display_init_window_a_pitch();
gfx_init_ctxt(fb, 720, 1280, 720);
gfx_con_init();
// Initialize backlight PWM.
display_backlight_pwm_init();
display_backlight_brightness(100, 0);
// Show AMS errors
secmon_exo_check_panic();
check_log();
gfx_printf("\n\nPress POWER to power off\nPress VOLUME to boot RCM\n");
msleep(250);
do{
u8 btn = btn_read();
if(btn & BTN_POWER){
power_set_state(POWER_OFF);
}
if(btn & (BTN_VOL_DOWN | BTN_VOL_UP)){
power_set_state(REBOOT_RCM);
}
}while(true);
// Halt BPMP if we managed to get out of execution.
while (true)
bpmp_halt();
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2018 naehrwert
*
* 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 .text._start
.arm
.extern _reloc_ipl
.type _reloc_ipl, %function
.extern memset
.type memset, %function
.extern _irq_setup
.type _irq_setup, %function
.globl _start
.type _start, %function
_start:
ADR R0, _start
LDR R1, =__ipl_start
CMP R0, R1
BEQ _real_start
/* If we are not in the right location already, copy a relocator to upper IRAM. */
ADR R2, _reloc_ipl
LDR R3, =0x4003FF00
MOV R4, #(_real_start - _reloc_ipl)
_copy_loop:
LDMIA R2!, {R5}
STMIA R3!, {R5}
SUBS R4, #4
BNE _copy_loop
/* Use the relocator to copy ourselves into the right place. */
LDR R2, =__ipl_end
SUB R2, R2, R1
LDR R3, =_real_start
LDR R4, =0x4003FF00
BX R4
_reloc_ipl:
LDMIA R0!, {R4-R7}
STMIA R1!, {R4-R7}
SUBS R2, #0x10
BNE _reloc_ipl
/* Jump to the relocated entry. */
BX R3
_real_start:
/* Initially, we place our stack under relocator but will move it to under the payload. */
/* This depends on application scope. */
LDR SP, =0x4003FF00
LDR R0, =__bss_start
EOR R1, R1, R1
LDR R2, =__bss_end
SUB R2, R2, R0
BL memset
BL _irq_setup
B .
.globl pivot_stack
.type pivot_stack, %function
pivot_stack:
MOV SP, R0
BX LR

View File

@@ -33,19 +33,20 @@
#include <switch/types.h>
typedef enum
{
HocClkSocType_Erista = 0,
HocClkSocType_Mariko,
HocClkSocType_Erista = 0, // T210, found in Icosa and Copper
HocClkSocType_Mariko, // T214/T210B01, found in Hoag, Iowa, Calcio and Aula
// HocClkSocType_Drake, // T239, found in Switch 2. Maybe someday...
HocClkSocType_EnumMax
} HocClkSocType;
typedef enum
{
HocClkConsoleType_Icosa = 0,
HocClkConsoleType_Copper,
HocClkConsoleType_Hoag,
HocClkConsoleType_Iowa,
HocClkConsoleType_Calcio,
HocClkConsoleType_Aula,
HocClkConsoleType_Icosa = 0, // V1
HocClkConsoleType_Copper, // Unreleased Erista
HocClkConsoleType_Hoag, // Lite
HocClkConsoleType_Iowa, // V2
HocClkConsoleType_Calcio, // Unreleased Mariko
HocClkConsoleType_Aula, // OLED
HocClkConsoleType_EnumMax,
} HocClkConsoleType;
@@ -54,7 +55,7 @@ typedef enum {
HocClkVoltage_EMCVDD2,
HocClkVoltage_CPU,
HocClkVoltage_GPU,
HocClkVoltage_EMCVDDQ,
HocClkVoltage_EMCVDDQ, // Returns VDD2 on Erista
HocClkVoltage_Display,
HocClkVoltage_Battery,
HocClkVoltage_EnumMax,
@@ -63,10 +64,10 @@ typedef enum {
typedef enum
{
HocClkProfile_Handheld = 0,
HocClkProfile_HandheldCharging,
HocClkProfile_HandheldCharging, // Not a real profile, just a marker
HocClkProfile_HandheldChargingUSB,
HocClkProfile_HandheldChargingOfficial,
HocClkProfile_Docked,
HocClkProfile_Docked, // Not shown on Lites
HocClkProfile_EnumMax
} HocClkProfile;
@@ -86,11 +87,11 @@ typedef enum
HocClkThermalSensor_PCB,
HocClkThermalSensor_Skin,
HocClkThermalSensor_Battery,
HocClkThermalSensor_PMIC,
HocClkThermalSensor_PMIC, // Always return 50.0C, as thats the only reasonable value the PMIC sensor can generate
HocClkThermalSensor_CPU,
HocClkThermalSensor_GPU,
HocClkThermalSensor_MEM,
HocClkThermalSensor_PLLX,
HocClkThermalSensor_MEM, // Returns the PLLX sensor value on Mariko
HocClkThermalSensor_PLLX,
HocClkThermalSensor_EnumMax
} HocClkThermalSensor;
@@ -107,8 +108,12 @@ typedef enum
HocClkPartLoad_EMCCpu,
HocClkPartLoad_GPU,
HocClkPartLoad_CPUMax,
HocClkPartLoad_BAT,
HocClkPartLoad_BAT, // Battery raw charge percentage
HocClkPartLoad_FAN,
HocClkPartLoad_RamBWAll,
HocClkPartLoad_RamBWCpu,
HocClkPartLoad_RamBWGpu,
HocClkPartLoad_RamBWPeak,
HocClkPartLoad_EnumMax
} HocClkPartLoad;
@@ -158,6 +163,19 @@ typedef enum {
RamDisplayMode_EnumMax,
} RamDisplayMode;
typedef enum {
MemoryFrequencyMeasurementMode_Actmon = 0,
MemoryFrequencyMeasurementMode_PLL,
MemoryFrequencyMeasurementMode_EnumMax,
} MemoryFrequencyMeasurementMode;
typedef enum {
RamDisplayUnit_MHz = 0,
RamDisplayUnit_MTs,
RamDisplayUnit_MHzMTs,
RamDisplayUnit_EnumMax,
} RamDisplayUnit;
#define HOCCLK_ENUM_VALID(n, v) ((v) < n##_EnumMax)
// Packed u32
@@ -220,7 +238,7 @@ static inline const char* hocclkFormatThermalSensor(HocClkThermalSensor thermSen
case HocClkThermalSensor_PLLX:
return pretty ? "PLLX" : "pllx";
default:
return NULL;
return "unknown";
}
}
@@ -233,7 +251,7 @@ static inline const char* hocclkFormatPowerSensor(HocClkPowerSensor powSensor, b
case HocClkPowerSensor_Avg:
return pretty ? "Avg" : "avg";
default:
return NULL;
return "unknown";
}
}
@@ -252,7 +270,7 @@ static inline const char* hocclkFormatProfile(HocClkProfile profile, bool pretty
case HocClkProfile_HandheldChargingOfficial:
return pretty ? "PD Charger" : "handheld_charging_official";
default:
return NULL;
return "unknown";
}
}
@@ -274,6 +292,6 @@ static inline const char* hocClkFormatVoltage(HocClkVoltage voltage, bool pretty
case HocClkVoltage_Display:
return pretty ? "Display" : "display";
default:
return NULL;
return "unknown";
}
}

View File

@@ -12,9 +12,9 @@
*
* 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>
@@ -58,6 +58,9 @@ typedef struct
// FPS / Resolution
u8 fps;
u16 resolutionHeight;
// Reserved for future use
u8 reserved[0x428];
} HocClkContext;
typedef struct
@@ -70,4 +73,6 @@ typedef struct
#define HOCCLK_FREQ_LIST_MAX 32
#define GLOBAL_PROFILE_ID 0xA111111111111111
#define GLOBAL_PROFILE_ID 0xA111111111111111
static_assert(sizeof(HocClkContext) == 0x500);

View File

@@ -29,7 +29,7 @@
#include <stdint.h>
#include <stddef.h>
#include "board.h"
typedef enum {
HocClkConfigValue_PollingIntervalMs = 0,
HocClkConfigValue_TempLogIntervalMs,
@@ -68,6 +68,10 @@ typedef enum {
HocClkConfigValue_CpuGovernorMinimumFreq,
HocClkConfigValue_DisplayVoltage,
HocClkConfigValue_MemoryFrequencyMeasurementMode,
HocClkConfigValue_RamDisplayUnit,
HocClkConfigValue_IsFirstLoad,
KipConfigValue_custRev,
// KipConfigValue_mtcConf,
KipConfigValue_hpMode,
@@ -76,6 +80,8 @@ typedef enum {
KipConfigValue_eristaEmcMaxClock,
KipConfigValue_eristaEmcMaxClock1,
KipConfigValue_eristaEmcMaxClock2,
KipConfigValue_stepMode,
KipConfigValue_marikoEmcMaxClock,
KipConfigValue_marikoEmcVddqVolt,
KipConfigValue_emcDvbShift,
@@ -88,6 +94,23 @@ typedef enum {
KipConfigValue_t6_tRTW,
KipConfigValue_t7_tWTR,
KipConfigValue_t8_tREFI,
KipConfigValue_timingEmcTbreak,
KipConfigValue_low_t6_tRTW,
KipConfigValue_low_t7_tWTR,
KipConfigValue_t2_tRP_cap,
KipConfigValue_read_latency_1333,
KipConfigValue_read_latency_1600,
KipConfigValue_read_latency_1866,
KipConfigValue_read_latency_2133,
KipConfigValue_write_latency_1333,
KipConfigValue_write_latency_1600,
KipConfigValue_write_latency_1866,
KipConfigValue_write_latency_2133,
KipConfigValue_mem_burst_read_latency,
KipConfigValue_mem_burst_write_latency,
@@ -173,7 +196,6 @@ typedef enum {
KipConfigValue_t7_tWTR_fine_tune,
KipCrc32,
HocClkConfigValue_IsFirstLoad,
HocClkConfigValue_EnumMax,
} HocClkConfigValue;
@@ -257,6 +279,12 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case HocClkConfigValue_DisplayVoltage:
return pretty ? "Display Voltage" : "display_voltage";
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
return pretty ? "RAM Frequency Measurement Mode" : "mem_freq_measurement_mode";
case HocClkConfigValue_RamDisplayUnit:
return pretty ? "RAM Frequency Display Unit" : "RAM_display_unit";
// KIP config values
case KipConfigValue_custRev:
return pretty ? "Custom Revision" : "kip_cust_rev";
@@ -274,6 +302,8 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
return pretty ? "Erista EMC Max Clock 2" : "erista_emc_max_clock1";
case KipConfigValue_eristaEmcMaxClock2:
return pretty ? "Erista EMC Max Clock 3" : "erista_emc_max_clock2";
case KipConfigValue_stepMode:
return pretty ? "Step Mode:" : "step_mode";
case KipConfigValue_marikoEmcMaxClock:
return pretty ? "Mariko EMC Max Clock" : "mariko_emc_max_clock";
case KipConfigValue_marikoEmcVddqVolt:
@@ -298,6 +328,35 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
return pretty ? "t7 - tWTR" : "t7_twtr";
case KipConfigValue_t8_tREFI:
return pretty ? "t8 - tREFI" : "t8_trefi";
case KipConfigValue_timingEmcTbreak:
return pretty ? "Timing Emc Tbreak" : "timingEmcTbreak";
case KipConfigValue_low_t6_tRTW:
return pretty ? "Low T6 - tRTW" : "low_t6_tRTW";
case KipConfigValue_low_t7_tWTR:
return pretty ? "Low T7 - tWTR" : "low_t7_tWTR";
case KipConfigValue_t2_tRP_cap:
return pretty ? "t2 - trp 1333WL Cap" : "t2_tRP_cap";
case KipConfigValue_read_latency_1333:
return pretty ? "1333 Read Latency" : "read_latency_1333";
case KipConfigValue_read_latency_1600:
return pretty ? "1600 Read Latency" : "read_latency_1600";
case KipConfigValue_read_latency_1866:
return pretty ? "1866 Read Latency" : "read_latency_1866";
case KipConfigValue_read_latency_2133:
return pretty ? "2133 Read Latency" : "read_latency_2133";
case KipConfigValue_write_latency_1333:
return pretty ? "1333 Write Latency" : "write_latency_1333";
case KipConfigValue_write_latency_1600:
return pretty ? "1600 Write Latency" : "write_latency_1600";
case KipConfigValue_write_latency_1866:
return pretty ? "1866 Write Latency" : "write_latency_1866";
case KipConfigValue_write_latency_2133:
return pretty ? "2133 Write Latency" : "write_latency_2133";
case KipConfigValue_mem_burst_read_latency:
return pretty ? "Memory Burst Read Latency" : "mem_burst_read_latency";
case KipConfigValue_mem_burst_write_latency:
@@ -408,7 +467,7 @@ static inline const char* hocclkFormatConfigValue(HocClkConfigValue val, bool pr
case KipConfigValue_g_volt_e_998400: return pretty ? "Erista GPU Volt 998 MHz" : "g_volt_e_998400";
case KipConfigValue_g_volt_e_1036800: return pretty ? "Erista GPU Volt 1036 MHz" : "g_volt_e_1036800";
case KipConfigValue_g_volt_e_1075200: return pretty ? "Erista GPU Volt 1075 MHz" : "g_volt_e_1075200";
case KipConfigValue_t6_tRTW_fine_tune: return pretty ? "t6 - tRTW Fine Tune" : "t6_tRTW_fine_fune";
case KipConfigValue_t6_tRTW_fine_tune: return pretty ? "t6 - tRTW Fine Tune" : "t6_tRTW_fine_tune";
case KipConfigValue_t7_tWTR_fine_tune: return pretty ? "t7 - tWTR Fine Tune" : "t7_tWTR_fine_tune";
case KipCrc32:
return pretty ? "CRC32" : "crc32";
@@ -436,7 +495,10 @@ static inline uint64_t hocclkDefaultConfigValue(HocClkConfigValue val)
case HocClkConfigValue_GPUScheduling:
case HocClkConfigValue_LiveCpuUv:
case HocClkConfigValue_GPUSchedulingMethod:
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
return 0ULL;
case HocClkConfigValue_RamDisplayUnit:
return (uint64_t)RamDisplayUnit_MHz;
case HocClkConfigValue_EristaMaxCpuClock:
return 1785ULL;
@@ -500,6 +562,7 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case KipConfigValue_eristaEmcMaxClock:
case KipConfigValue_eristaEmcMaxClock1:
case KipConfigValue_eristaEmcMaxClock2:
case KipConfigValue_stepMode:
case KipConfigValue_marikoEmcMaxClock:
case KipConfigValue_marikoEmcVddqVolt:
case KipConfigValue_emcDvbShift:
@@ -511,6 +574,18 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case KipConfigValue_t6_tRTW:
case KipConfigValue_t7_tWTR:
case KipConfigValue_t8_tREFI:
case KipConfigValue_timingEmcTbreak:
case KipConfigValue_low_t6_tRTW:
case KipConfigValue_low_t7_tWTR:
case KipConfigValue_t2_tRP_cap:
case KipConfigValue_read_latency_1333:
case KipConfigValue_read_latency_1600:
case KipConfigValue_read_latency_1866:
case KipConfigValue_read_latency_2133:
case KipConfigValue_write_latency_1333:
case KipConfigValue_write_latency_1600:
case KipConfigValue_write_latency_1866:
case KipConfigValue_write_latency_2133:
case KipConfigValue_mem_burst_read_latency:
case KipConfigValue_mem_burst_write_latency:
case KipConfigValue_eristaCpuUV:
@@ -592,11 +667,13 @@ static inline uint64_t hocclkValidConfigValue(HocClkConfigValue val, uint64_t in
case HocClkConfigValue_GPUScheduling:
case HocClkConfigValue_RAMVoltDisplayMode:
case HocClkConfigValue_CpuGovernorMinimumFreq:
case HocClkConfigValue_MemoryFrequencyMeasurementMode:
case HocClkConfigValue_RamDisplayUnit:
return true;
case HocClkConfigValue_BatteryChargeCurrent:
return ((input >= 1024) && (input <= 3072)) || !input;
case HocClkConfigValue_DisplayVoltage:
return ((input >= 900) && (input <= 1325));
return ((input >= 800) && (input <= 1325));
default:
return false;

View File

@@ -31,7 +31,7 @@
#include "board.h"
#include "clock_manager.h"
#define HOCCLK_IPC_API_VERSION 1
#define HOCCLK_IPC_API_VERSION 2
#define HOCCLK_IPC_SERVICE_NAME "hoc:clk"
enum HocClkIpcCmd

View File

@@ -525,3 +525,24 @@
#define CL_DVFS_I2C_STS_0 0x48
#define CL_DVFS_INTR_STS_0 0x5C
#define CL_DVFS_I2C_CLK_DIVISOR_REGISTER_0 0x16C
#define CLK_SOURCE_EMC 0x19c
#define PLLC_BASE 0x080
#define PLLM_BASE 0x090
#define PLLP_BASE 0x0a0
#define PLLA_BASE 0x0b0
#define PLLU_BASE 0x0c0
#define _PLLD_BASE 0x0d0
#define PLLX_BASE 0x0e0
#define PLLE_BASE 0x0e8
#define PLLC2_BASE 0x4e8
#define PLLC3_BASE 0x4fc
#define PLLD2_BASE 0x4b8
#define PLLRE_BASE 0x4c4
#define PLLC4_BASE 0x5a4
#define PLLMB_BASE 0x5e8
#define PLLA1_BASE 0x6a4
#define PLLDP_BASE 0x590
#define OSC_HZ 38400000ULL

View File

@@ -27,7 +27,7 @@ INCLUDES := ../common/include
EXEFS_SRC := exefs_src
IS_MINIMAL := 0
APP_TITLE := Horizon OC Zeus
APP_TITLE := Horizon OC Gaea
NO_ICON := 1
@@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk
# version control constants
#---------------------------------------------------------------------------------
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
APP_VERSION := 1.2.1
APP_VERSION := 2.0.0
TARGET_VERSION := $(APP_VERSION)
#---------------------------------------------------------------------------------

View File

@@ -2,140 +2,140 @@
"Information": "Información",
"IDDQ:": "IDDQ:",
"Module: ": "Módulo:",
"sys-dock status:": "estado del sys-dock:",
"sys-dock status:": "Estado de sys-dock:",
"SaltyNX status:": "Estado de SaltyNX:",
"RR Display status:": "Estado de visualización RR:",
"Wafer Position:": "Posición de la oblea:",
"RR Display status:": "Estado de pantalla RR:",
"Wafer Position:": "Posición del wafer:",
"Credits": "Créditos",
"Developers": "Desarrolladores",
"Contributors": "Colaboradores",
"Testers": "Probadores",
"Special Thanks": "agradecimiento especial",
"Testers": "Testers",
"Special Thanks": "Agradecimientos especiales",
"Unknown": "Desconocido",
"Installed": "Instalado",
"Not Installed": "No instalado",
"X: %u Y: %u": "X: %u Y: %u",
"THE BEER-WARE LICENSE": "LA LICENCIA DE CERVEZA",
"THE BEER-WARE LICENSE": "LICENCIA BEER-WARE",
"Default": "Predeterminado",
"Do Not Override": "No anular",
"Disabled": "Discapacitado",
"Enabled": "Habilitado",
"Do Not Override": "No sobrescribir",
"Disabled": "Desactivado",
"Enabled": "Activado",
" \\ue0e3 Reset": "\\ue0e3 Restablecer",
"Display": "Pantalla",
"Application changed\\n\\n": "Aplicación modificada\\n\\n",
"The running application changed\\n\\n": "La aplicación en ejecución cambió\\n\\n",
"while editing was going on.": "mientras se realizaba la edición.",
"Board": "tablero",
"Application changed\\n\\n": "Aplicación cambiada\\n\\n",
"The running application changed\\n\\n": "La aplicación en ejecución ha cambiado\\n\\n",
"while editing was going on.": "mientras se estaba editando.",
"Board": "Placa",
"%u.%u%u mV": "%u.%u%u mV",
"Could not connect to hoc-clk sysmodule.\\n\\n": "No se pudo conectar al módulo del sistema hoc-clk.\\n\\n",
"Please make sure everything is\\n\\n": "Por favor asegúrese de que todo esté\\n\\n",
"correctly installed and enabled.": "correctamente instalado y habilitado.",
"Fatal error": "error fatal",
"Temporary Overrides ": "Anulaciones temporales",
"Sleep Mode": "Modo de suspensión",
"Stock": "Valores",
"Dev OC": "Desarrollador OC",
"Boost Mode": "Modo de impulso",
"Could not connect to hoc-clk sysmodule.\\n\\n": "No se pudo conectar al sysmodule hoc-clk.\\n\\n",
"Please make sure everything is\\n\\n": "Asegúrate de que todo esté\\n\\n",
"correctly installed and enabled.": "correctamente instalado y activado.",
"Fatal error": "Error fatal",
"Temporary Overrides ": "Ajustes temporales",
"Sleep Mode": "Modo reposo",
"Stock": "Valores de fábrica",
"Dev OC": "OC de desarrollo",
"Boost Mode": "Modo boost",
"Safe Max": "Máximo seguro",
"Unsafe Max": "Máximo inseguro",
"Unsafe Max": "Máximo no seguro",
"Absolute Max": "Máximo absoluto",
"Handheld Safe Max": "Caja fuerte de mano máx.",
"Enable": "Habilitar",
"Handheld Safe Max": "Máximo seguro en portátil",
"Enable": "Activar",
"Edit App Profile": "Editar perfil de aplicación",
"Edit Global Profile": "Editar perfil global",
"Temporary Overrides": "Anulaciones temporales",
"Temporary Overrides": "Ajustes temporales",
"Settings": "Configuración",
"About": "Acerca de",
"Compiling with minimal features": "Compilando con características mínimas",
"General Settings": "Configuraciones generales",
"Governor Settings": "Configuración del gobernador",
"Safety Settings": "Configuraciones de seguridad",
"Save KIP Settings": "Guardar configuración de KIP",
"Compiling with minimal features": "Compilado con funciones mínimas",
"General Settings": "Configuración general",
"Governor Settings": "Configuración del governor",
"Safety Settings": "Configuración de seguridad",
"Save KIP Settings": "Guardar configuración KIP",
"RAM Settings": "Configuración de RAM",
"CPU Settings": "Configuración de la CPU",
"CPU Settings": "Configuración de CPU",
"GPU Settings": "Configuración de GPU",
"Display Settings": "Configuración de pantalla",
"Experimental": "Experimental",
"GPU Scheduling Override Method": "Método de anulación de programación de GPU",
"can be dangerous and may cause": "puede ser peligroso y puede causar",
"damage to your battery or charger!": "¡Daños a su batería o cargador!",
"Charge Current Override": "Anulación de corriente de carga",
"GPU Scheduling Override Method": "Método de sobrescritura del scheduling de GPU",
"can be dangerous and may cause": "puede ser peligroso y causar",
"damage to your battery or charger!": "daños a la batería o al cargador.",
"Charge Current Override": "Sobrescritura de corriente de carga",
"RAM Voltage Display Mode": "Modo de visualización de voltaje de RAM",
"Polling Interval": "Intervalo de sondeo",
"CPU Governor Minimum Frequency": "Frecuencia mínima del gobernador de CPU",
"refresh rates may cause stress": "Las frecuencias de actualización pueden causar estrés.",
"or damage to your display! ": "o daños a su pantalla!",
"Proceed at your own risk!": Continúe bajo su propio riesgo!",
"Max Handheld Display": "Pantalla portátil máxima",
"Display Clock": "Reloj de pantalla",
"Official Rating": "Calificación oficial",
"CPU Governor Minimum Frequency": "Frecuencia mínima del governor de CPU",
"refresh rates may cause stress": "las tasas de refresco pueden causar estrés",
"or damage to your display! ": "o dañar la pantalla.",
"Proceed at your own risk!": Úsalo bajo tu propio riesgo!",
"Max Handheld Display": "Frecuencia máxima de pantalla en portátil",
"Display Clock": "Frecuencia de pantalla",
"Official Rating": "Valor oficial",
"TDP Threshold": "Umbral de TDP",
"Power": "poder",
"Thermal Throttle Limit": "Límite del acelerador térmico",
"HP Mode": "Modo HP",
"Power": "Potencia",
"Thermal Throttle Limit": "Límite de thermal throttling",
"HP Mode": "Modo alto rendimiento",
"Default (Mariko)": "Predeterminado (Mariko)",
"Default (Erista)": "Predeterminado (Erista)",
"Rating": "Calificación",
"Safe Max (Mariko)": "Max seguro (Mariko)",
"Safe Max (Erista)": "Safe Max (Erista)",
"RAM VDD2 Voltage": "Voltaje RAM VDD2",
"Voltage": "voltaje",
"RAM VDDQ Voltage": "Voltaje RAM VDDQ",
"RAM Frequency Editor": "Editor de frecuencia RAM",
"JEDEC.": "JEDEC.",
"High speedo needed!": "¡Se necesita alta velocidad!",
"3333MHz (Needs extreme Speedo/PLL)": "3333MHz (Necesita Speedo/PLL extremo)",
"3366MHz (Needs extreme Speedo/PLL)": "3366MHz (Necesita Speedo/PLL extremo)",
"3400MHz (Needs extreme Speedo/PLL)": "3400MHz (Necesita Speedo/PLL extremo)",
"3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (Necesita Speedo/PLL ridículo)",
"3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (Necesita Speedo/PLL ridículo)",
"3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz (Necesita Speedo/PLL ridículo)",
"Ram Max Clock": "Ram Max Reloj",
"RAM Latency Editor": "Editor de latencia de RAM",
"RAM Timing Reductions": "Reducciones de tiempo de RAM",
"Memory Timings": "Tiempos de memoria",
"Rating": "Valor",
"Safe Max (Mariko)": "Máximo seguro (Mariko)",
"Safe Max (Erista)": "Máximo seguro (Erista)",
"RAM VDD2 Voltage": "Voltaje VDD2 de RAM",
"Voltage": "Voltaje",
"RAM VDDQ Voltage": "Voltaje VDDQ de RAM",
"RAM Frequency Editor": "Editor de frecuencia de RAM",
"JEDEC.": "JEDEC",
"High speedo needed!": "¡Se necesita alto speedo!",
"3333MHz (Needs extreme Speedo/PLL)": "3333MHz (requiere Speedo/PLL extremo)",
"3366MHz (Needs extreme Speedo/PLL)": "3366MHz (requiere Speedo/PLL extremo)",
"3400MHz (Needs extreme Speedo/PLL)": "3400MHz (requiere Speedo/PLL extremo)",
"3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (requiere Speedo/PLL muy alto)",
"3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (requiere Speedo/PLL muy alto)",
"3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz (requiere Speedo/PLL muy alto)",
"Ram Max Clock": "Frecuencia máxima de RAM",
"RAM Latency Editor": "Editor de latencias de RAM",
"RAM Timing Reductions": "Reducción de timings de RAM",
"Memory Timings": "Timings de memoria",
"Advanced": "Avanzado",
"t6 tRTW Fine Tune": "t6 tRTW Ajuste fino",
"t6 tRTW Fine Tune": "Ajuste fino t6 tRTW",
"tRTW Fine Tune": "Ajuste fino tRTW",
"t7 tWTR Fine Tune": "t7 tWTR Ajuste fino",
"tWTR Fine Tune": "Ajuste fino de tWTR",
"Memory Latencies": "Latencias de la memoria",
"Read Latency": "Leer latencia",
"t7 tWTR Fine Tune": "Ajuste fino t7 tWTR",
"tWTR Fine Tune": "Ajuste fino tWTR",
"Memory Latencies": "Latencias de memoria",
"Read Latency": "Latencia de lectura",
"Write Latency": "Latencia de escritura",
"CPU Boost Clock": "Reloj de aumento de CPU",
"CPU UV": "procesador ultravioleta",
"CPU Boost Clock": "Frecuencia boost de CPU",
"CPU UV": "Undervolt de CPU",
"CPU Unlock": "Desbloqueo de CPU",
"CPU VMIN": "CPU VMIN",
"CPU Max Voltage": "Voltaje máximo de la CPU",
"CPU Max Clock": "Reloj máximo de CPU",
"Extreme UV Table": "Mesa UV extrema",
"CPU UV Table": "Tabla UV de CPU",
"CPU Low UV": "CPU baja radiación ultravioleta",
"CPU High UV": "CPU alta UV",
"CPU VMIN": "VMIN de CPU",
"CPU Max Voltage": "Voltaje máximo de CPU",
"CPU Max Clock": "Frecuencia máxima de CPU",
"Extreme UV Table": "Tabla de undervolt extrema",
"CPU UV Table": "Tabla de undervolt de CPU",
"CPU Low UV": "Undervolt bajo de CPU",
"CPU High UV": "Undervolt alto de CPU",
"CPU Low VMIN": "VMIN bajo de CPU",
"CPU High VMIN": "VMIN alto de CPU",
"No Undervolt": "Sin subvoltaje",
"SLT Table": "Mesa TR",
"No Undervolt": "Sin undervolt",
"SLT Table": "Tabla SLT",
"HiOPT Table": "Tabla HiOPT",
"GPU Undervolt Table": "Tabla de subvoltaje de GPU",
"GPU Undervolt Table": "Tabla de undervolt de GPU",
"GPU Minimum Voltage": "Voltaje mínimo de GPU",
"Calculate GPU Vmin": "Calcular GPU Vmin",
"GPU VMIN": "GPU VMIN",
"Calculate GPU Vmin": "Calcular Vmin de GPU",
"GPU VMIN": "VMIN de GPU",
"GPU Maximum Voltage": "Voltaje máximo de GPU",
"GPU Voltage Offset": "Compensación de voltaje de GPU",
"Do not override": "no anular",
"Enabled (Default)": "Habilitado (predeterminado)",
"96.6% limit": "límite del 96,6%",
"99.7% limit": "límite del 99,7%",
"GPU Scheduling Override": "Anulación de programación de GPU",
"Official Service": "Servicio Oficial",
"GPU DVFS Mode": "Modo GPU DVFS",
"GPU DVFS Offset": "Compensación DVFS de GPU",
"GPU Voltage Offset": "Offset de voltaje de GPU",
"Do not override": "No sobrescribir",
"Enabled (Default)": "Activado (predeterminado)",
"96.6% limit": "Límite 96,6%",
"99.7% limit": "Límite 99,7%",
"GPU Scheduling Override": "Sobrescritura de scheduling de GPU",
"Official Service": "Servicio oficial",
"GPU DVFS Mode": "Modo DVFS de GPU",
"GPU DVFS Offset": "Offset DVFS de GPU",
"GPU Voltage Table": "Tabla de voltaje de GPU",
"GPU Custom Table (mV)": "Tabla personalizada de GPU (mV)",
"1075MHz without UV, 1152MHz on SLT": "1075MHz sin UV, 1152MHz en SLT",
"or 1228MHz on HiOPT can cause ": "o 1228MHz en HiOPT pueden causar",
"permanent damage to your Switch!": Daño permanente a tu Switch!",
"921MHz without UV and 960MHz on": "921MHz sin UV y 960MHz encendido",
"SLT or HiOPT can cause ": "SLT o HiOPT pueden causar"
}
"1075MHz without UV, 1152MHz on SLT": "1075MHz sin undervolt, 1152MHz en SLT",
"or 1228MHz on HiOPT can cause ": "o 1228MHz en HiOPT pueden causar ",
"permanent damage to your Switch!": daño permanente a tu Switch!",
"921MHz without UV and 960MHz on": "921MHz sin undervolt y 960MHz en",
"SLT or HiOPT can cause ": "SLT o HiOPT pueden causar "
}

View File

@@ -30,6 +30,7 @@
#include <cstdio>
#include <string>
#include <cstdint>
#include <hocclk/board.h>
#define FREQ_DEFAULT_TEXT "Do not override"
@@ -44,4 +45,53 @@ static inline std::string formatListFreqMHz(std::uint32_t mhz)
return std::string(buf, snprintf(buf, sizeof(buf), "%u MHz", mhz));
}
static inline std::string formatListFreqHz(std::uint32_t hz) { return formatListFreqMHz(hz / 1000000); }
static inline std::string formatListFreqHz(uint32_t hz) { return formatListFreqMHz(hz / 1000000); }
static inline std::string formatListFreqMem(uint32_t mhz, RamDisplayUnit unit)
{
if(mhz == 0)
return FREQ_DEFAULT_TEXT;
uint32_t mts = mhz * 2;
char buf[24];
switch(unit)
{
case RamDisplayUnit_MHz:
snprintf(buf, sizeof(buf), "%u MHz", mhz);
break;
case RamDisplayUnit_MHzMTs:
snprintf(buf, sizeof(buf), "%u MHz (%u MT/s)", mhz, mts);
break;
case RamDisplayUnit_MTs:
default:
snprintf(buf, sizeof(buf), "%u MT/s", mts);
break;
}
return buf;
}
static inline std::string formatListFreqHzMem(uint32_t hz, RamDisplayUnit unit)
{
return formatListFreqMem(hz / 1000000, unit);
}
static inline std::string formatMemClockKhzLabel(uint32_t khz, RamDisplayUnit unit)
{
uint32_t mhz = khz / 1000;
uint32_t mts = khz / 500;
char buf[32];
switch(unit)
{
case RamDisplayUnit_MHz:
snprintf(buf, sizeof(buf), "%u MHz", mhz);
break;
case RamDisplayUnit_MHzMTs:
snprintf(buf, sizeof(buf), "%u MHz (%u MT/s)", mhz, mts);
break;
case RamDisplayUnit_MTs:
default:
snprintf(buf, sizeof(buf), "%u MT/s", mts);
break;
}
return buf;
}

View File

@@ -32,6 +32,10 @@ tsl::elm::ListItem* waferCordsItem = NULL;
tsl::elm::ListItem* ramVoltItem = NULL;
tsl::elm::ListItem* eristaPLLXItem = NULL;
tsl::elm::ListItem* dispVoltItem = NULL;
tsl::elm::ListItem* ramBWItemAll = NULL;
tsl::elm::ListItem* ramBWItemCpu = NULL;
tsl::elm::ListItem* ramBWItemGpu = NULL;
tsl::elm::ListItem* ramBWItemMax = NULL;
ImageElement* CatImage = NULL;
HideableCategoryHeader* CatHeader = NULL;
@@ -50,13 +54,15 @@ AboutGui::~AboutGui()
void AboutGui::listUI()
{
this->listElement->addItem(
new tsl::elm::CategoryHeader("Information")
new tsl::elm::CategoryHeader("Voltages and Temps")
);
ramVoltItem =
new tsl::elm::ListItem("RAM Voltage:");
this->listElement->addItem(ramVoltItem);
if(IsMariko()) {
this->listElement->addItem(ramVoltItem);
}
dispVoltItem =
new tsl::elm::ListItem("Display Voltage:");
this->listElement->addItem(dispVoltItem);
@@ -67,6 +73,30 @@ void AboutGui::listUI()
this->listElement->addItem(eristaPLLXItem);
}
this->listElement->addItem(
new tsl::elm::CategoryHeader("RAM Bandwidth")
);
ramBWItemMax =
new tsl::elm::ListItem("RAM BW (Peak):");
this->listElement->addItem(ramBWItemMax);
ramBWItemAll =
new tsl::elm::ListItem("RAM BW (All):");
this->listElement->addItem(ramBWItemAll);
ramBWItemCpu =
new tsl::elm::ListItem("RAM BW (CPU):");
this->listElement->addItem(ramBWItemCpu);
ramBWItemGpu =
new tsl::elm::ListItem("RAM BW (GPU):");
this->listElement->addItem(ramBWItemGpu);
this->listElement->addItem(
new tsl::elm::CategoryHeader("HW Info")
);
SpeedoItem =
new tsl::elm::ListItem("Speedo:");
this->listElement->addItem(SpeedoItem);
@@ -79,6 +109,20 @@ void AboutGui::listUI()
new tsl::elm::ListItem("Module: ");
this->listElement->addItem(DramModule);
waferCordsItem =
new tsl::elm::ListItem("Wafer Position:");
this->listElement->addItem(waferCordsItem);
if(IsHoag()) {
RETROStatusItem =
new tsl::elm::ListItem("RR Display status:");
this->listElement->addItem(RETROStatusItem);
}
this->listElement->addItem(
new tsl::elm::CategoryHeader("Software Info")
);
if(!IsHoag()) {
sysdockStatusItem =
new tsl::elm::ListItem("sys-dock status:");
@@ -88,16 +132,6 @@ void AboutGui::listUI()
saltyNXStatusItem =
new tsl::elm::ListItem("SaltyNX status:");
this->listElement->addItem(saltyNXStatusItem);
if(IsHoag()) {
RETROStatusItem =
new tsl::elm::ListItem("RR Display status:");
this->listElement->addItem(RETROStatusItem);
}
waferCordsItem =
new tsl::elm::ListItem("Wafer Position:");
this->listElement->addItem(waferCordsItem);
this->listElement->addItem(
new tsl::elm::CategoryHeader("Credits")
@@ -303,7 +337,7 @@ void AboutGui::update()
void AboutGui::refresh()
{
BaseMenuGui::refresh();
if (!this->context)
return;
// Format strings once per refresh
@@ -326,7 +360,7 @@ void AboutGui::refresh()
if(IsErista()) {
u32 millis = context->temps[HocClkThermalSensor_PLLX];
sprintf(strings[3], "%u.%u", millis / 1000U, (millis % 1000U) / 100U);
sprintf(strings[3], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
eristaPLLXItem->setValue(strings[3]);
}
@@ -336,4 +370,16 @@ void AboutGui::refresh()
sprintf(strings[5], "%u.%u mV", context->voltages[HocClkVoltage_Display] / 1000U, (context->voltages[HocClkVoltage_Display] % 1000U) / 100U);
dispVoltItem->setValue(strings[5]);
sprintf(strings[6], "%u MB/s", context->partLoad[HocClkPartLoad_RamBWAll]);
ramBWItemAll->setValue(strings[6]);
sprintf(strings[7], "%u MB/s", context->partLoad[HocClkPartLoad_RamBWCpu]);
ramBWItemCpu->setValue(strings[7]);
sprintf(strings[8], "%u MB/s", context->partLoad[HocClkPartLoad_RamBWGpu]);
ramBWItemGpu->setValue(strings[8]);
sprintf(strings[9], "%u MB/s", context->partLoad[HocClkPartLoad_RamBWPeak]);
ramBWItemMax->setValue(strings[9]);
}

View File

@@ -59,9 +59,11 @@ void AppProfileGui::openFreqChoiceGui(tsl::elm::ListItem* listItem, HocClkProfil
} else if (module == HocClkModule_GPU) {
labels = IsMariko() ? *(marikoUV[configList.values[KipConfigValue_marikoGpuUV]]) : *(eristaUV[configList.values[KipConfigValue_eristaGpuUV]]);
}
tsl::changeTo<FreqChoiceGui>(this->profileList->mhzMap[profile][module] * 1000000, hzList, hzCount, module, [this, listItem, profile, module](std::uint32_t hz) {
RamDisplayUnit memUnit = (RamDisplayUnit)configList.values[HocClkConfigValue_RamDisplayUnit];
tsl::changeTo<FreqChoiceGui>(this->profileList->mhzMap[profile][module] * 1000000, hzList, hzCount, module, [this, listItem, profile, module, memUnit](std::uint32_t hz) {
this->profileList->mhzMap[profile][module] = hz / 1000000;
listItem->setValue(formatListFreqMHz(this->profileList->mhzMap[profile][module]));
std::uint32_t mhz = this->profileList->mhzMap[profile][module];
listItem->setValue(module == HocClkModule_MEM ? formatListFreqMem(mhz, memUnit) : formatListFreqMHz(mhz));
Result rc = hocclkIpcSetProfiles(this->applicationId, this->profileList);
if(R_FAILED(rc))
{
@@ -104,8 +106,10 @@ void AppProfileGui::openValueChoiceGui(
void AppProfileGui::addModuleListItem(HocClkProfile profile, HocClkModule module)
{
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(hocclkFormatModule(module, true));
listItem->setValue(formatListFreqMHz(this->profileList->mhzMap[profile][module]));
listItem->setClickListener([this, listItem, profile, module](u64 keys) {
RamDisplayUnit memUnit = (RamDisplayUnit)configList.values[HocClkConfigValue_RamDisplayUnit];
std::uint32_t mhz = this->profileList->mhzMap[profile][module];
listItem->setValue(module == HocClkModule_MEM ? formatListFreqMem(mhz, memUnit) : formatListFreqMHz(mhz));
listItem->setClickListener([this, listItem, profile, module, memUnit](u64 keys) {
if((keys & HidNpadButton_A) == HidNpadButton_A)
{
this->openFreqChoiceGui(listItem, profile, module);
@@ -115,7 +119,7 @@ void AppProfileGui::addModuleListItem(HocClkProfile profile, HocClkModule module
{
// Reset to "Default" (0 MHz)
this->profileList->mhzMap[profile][module] = 0;
listItem->setValue(formatListFreqMHz(0));
listItem->setValue(module == HocClkModule_MEM ? formatListFreqMem(0, memUnit) : formatListFreqMHz(0));
Result rc = hocclkIpcSetProfiles(this->applicationId, this->profileList);
if(R_FAILED(rc))

View File

@@ -48,10 +48,10 @@ std::string getVersionString() {
return std::string(buf);
}
static constexpr tsl::Color dynamicLogoRGB1 = tsl::Color(0, 4, 8, 15);
static constexpr tsl::Color dynamicLogoRGB2 = tsl::Color(7, 15, 15, 15);
static constexpr tsl::Color STATIC_AQUA = tsl::Color(2, 10, 12, 15);
const std::string name = "Horizon OC Zeus";
static constexpr tsl::Color dynamicLogoRGB1 = tsl::Color(0, 15, 3, 15);
static constexpr tsl::Color dynamicLogoRGB2 = tsl::Color(0, 8, 1, 15);
static constexpr tsl::Color STATIC_GREEN = tsl::Color(0, 15, 0, 15);
const std::string name = "Horizon OC Gaea";
static s32 drawDynamicUltraText(
tsl::gfx::Renderer* renderer,
@@ -100,12 +100,12 @@ static s32 drawDynamicUltraText(
g = std::clamp<u8>(g, 0, 15);
b = std::clamp<u8>(b, 0, 15);
bool lightning = (fmod(timeNow, 5.0) < 0.15);
if (lightning) {
r = std::min<u8>(r + 4, 15);
g = std::min<u8>(g + 4, 15);
b = std::min<u8>(b + 15, 15);
}
// bool lightning = (fmod(timeNow, 5.0) < 0.15);
// if (lightning) {
// r = std::min<u8>(r + 4, 15);
// g = std::min<u8>(g + 4, 15);
// b = std::min<u8>(b + 15, 15);
// }
tsl::Color color(r, g, b, 15);
@@ -126,7 +126,7 @@ void BaseGui::preDraw(tsl::gfx::Renderer* renderer) {
LOGO_X,
LOGO_Y,
LOGO_LABEL_FONT_SIZE,
STATIC_AQUA,
STATIC_GREEN,
false
);
}

View File

@@ -27,6 +27,7 @@
#include "base_menu_gui.h"
#include "fatal_gui.h"
#include "../format.h"
// Cache hardware model to avoid repeated syscalls
@@ -57,8 +58,8 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
if(!this->context) [[unlikely]] return;
// All constants pre-calculated and cached
static constexpr const char* const labels[] = {
"App ID", "Profile", "CPU", "GPU", "MEM", "SoC", "Board", "Skin", "Now", "Avg", "BAT", "PMIC", "FAN", "DISP", "FPS", "RES"
const char* labels[] = {
"App ID", "Profile", "CPU", "GPU", "MEM", "SoC", "Board", "Skin", "Now", "Avg", "BAT", "PMIC", "Fan", IsAula() ? "OLED" : "LCD", "FPS", "RES"
};
static constexpr u32 dataPositions[6] = {63-3+3, 200-1, 344-1-3, 200-1, 342-1, 321-1};
@@ -121,7 +122,7 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
// === VOLTAGES ===
renderer->drawString(displayStrings[8], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU voltage
renderer->drawString(displayStrings[9], false, dataPositions[1] + 9, y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU voltage
renderer->drawString(displayStrings[9], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU voltage
renderer->drawStringWithColoredSections(displayStrings[10], false, {""}, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor, tsl::separatorColor);
@@ -139,8 +140,8 @@ void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
// Temperatures with color - use pre-computed colors
renderer->drawString(displayStrings[11], false, dataPositions[0] - 1, y, SMALL_TEXT_SIZE, tempColors[HocClkThermalSensor_SOC]); // SOC
renderer->drawString(displayStrings[12], false, dataPositions[1] + 5, y, SMALL_TEXT_SIZE, tempColors[HocClkThermalSensor_PCB]); // PCB
renderer->drawString(displayStrings[13], false, dataPositions[2] + 6, y, SMALL_TEXT_SIZE, tempColors[HocClkThermalSensor_Skin]); // Skin
renderer->drawString(displayStrings[12], false, dataPositions[1], y, SMALL_TEXT_SIZE, tempColors[HocClkThermalSensor_PCB]); // PCB
renderer->drawString(displayStrings[13], false, dataPositions[2], y, SMALL_TEXT_SIZE, tempColors[HocClkThermalSensor_Skin]); // Skin
y += 20; // Direct assignment (191 + 20)
@@ -229,7 +230,20 @@ void BaseMenuGui::refresh()
sprintf(displayStrings[3], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->freqs[HocClkModule_MEM]; // MEM
sprintf(displayStrings[4], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
std::uint32_t unit = configList.values[HocClkConfigValue_RamDisplayUnit];
std::uint32_t mhz = hz / 1000000U;
std::uint32_t mts = mhz * 2;
std::uint32_t tenth = (hz / 100000U) % 10U;
if(unit == RamDisplayUnit_MTs)
sprintf(displayStrings[4], "%u MT/s", mts);
else if(unit == RamDisplayUnit_MHz)
sprintf(displayStrings[4], "%u.%u MHz", mhz, tenth);
else if(unit == RamDisplayUnit_MHzMTs) {
hz = context->realFreqs[HocClkModule_MEM];
mhz = hz / 1000000U;
tenth = (hz / 100000U) % 10U;
sprintf(displayStrings[4], "%u.%u MHz", mhz, tenth);
}
// Real frequencies
hz = context->realFreqs[HocClkModule_CPU]; // CPU
@@ -239,7 +253,14 @@ void BaseMenuGui::refresh()
sprintf(displayStrings[6], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->realFreqs[HocClkModule_MEM]; // MEM
sprintf(displayStrings[7], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
unit = configList.values[HocClkConfigValue_RamDisplayUnit];
mhz = hz / 1000000U;
mts = mhz * 2;
tenth = (hz / 100000U) % 10U;
if(unit == RamDisplayUnit_MTs || unit == RamDisplayUnit_MHzMTs)
sprintf(displayStrings[7], "%u MT/s", mts);
else
sprintf(displayStrings[7], "%u.%u MHz", mhz, tenth);
// Voltages
sprintf(displayStrings[8], "%.1f mV", context->voltages[HocClkVoltage_CPU] / 1000.0);

View File

@@ -55,7 +55,11 @@ FreqChoiceGui::~FreqChoiceGui()
tsl::elm::ListItem* FreqChoiceGui::createFreqListItem(std::uint32_t hz, bool selected, int safety)
{
std::string text = formatListFreqHz(hz);
std::string text;
if(module == HocClkModule_MEM)
text = formatListFreqHzMem(hz, (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit]);
else
text = formatListFreqHz(hz);
std::string rightText = "";
auto it = labels.find(hz);

View File

@@ -246,7 +246,8 @@ void GlobalOverrideGui::addModuleListItem(HocClkModule module)
{
tsl::elm::ListItem *listItem =
new tsl::elm::ListItem(hocclkFormatModule(module, true));
listItem->setValue(formatListFreqMHz(0));
RamDisplayUnit memUnit = (RamDisplayUnit)configList.values[HocClkConfigValue_RamDisplayUnit];
listItem->setValue(module == HocClkModule_MEM ? formatListFreqMem(0, memUnit) : formatListFreqMHz(0));
listItem->setClickListener([this, module](u64 keys) {
if ((keys & HidNpadButton_A) == HidNpadButton_A) {
this->openFreqChoiceGui(module);
@@ -262,7 +263,7 @@ void GlobalOverrideGui::addModuleListItem(HocClkModule module)
this->context->overrideFreqs[module] = 0;
this->listHz[module] = 0;
this->listItems[module]->setValue(formatListFreqHz(0));
this->listItems[module]->setValue(module == HocClkModule_MEM ? formatListFreqMem(0, (RamDisplayUnit)configList.values[HocClkConfigValue_RamDisplayUnit]) : formatListFreqHz(0));
return true;
}
@@ -409,7 +410,9 @@ void GlobalOverrideGui::refresh()
}
} else {
this->listItems[m]->setValue(
formatListFreqHz(this->context->overrideFreqs[m]));
m == HocClkModule_MEM
? formatListFreqHzMem(this->context->overrideFreqs[m], (RamDisplayUnit)configList.values[HocClkConfigValue_RamDisplayUnit])
: formatListFreqHz(this->context->overrideFreqs[m]));
}
this->listHz[m] = this->context->overrideFreqs[m];

File diff suppressed because it is too large Load Diff

View File

@@ -41,26 +41,29 @@ protected:
std::map<HocClkConfigValue, std::tuple<tsl::elm::TrackBar*, tsl::elm::ListItem*, std::vector<uint64_t>>> configTrackbars;
std::set<HocClkConfigValue> configButtonSKeys;
std::map<HocClkConfigValue, std::string> configButtonSSubtext;
std::set<HocClkConfigValue> emcClockConfigs;
void addConfigToggle(HocClkConfigValue configVal, const char* altName);
void addConfigButton(HocClkConfigValue configVal,
const char* altName,
const ValueRange& range,
const std::string& categoryName,
const ValueThresholds* thresholds,
const std::map<uint32_t, std::string>& labels = {},
const std::vector<NamedValue>& namedValues = {},
bool showDefaultValue = true);
void addConfigButtonS(HocClkConfigValue configVal,
const char* altName,
void addConfigToggle(HocClkConfigValue configVal, const char* altName, bool kip = false);
void addConfigButton(HocClkConfigValue configVal,
const char* altName,
const ValueRange& range,
const std::string& categoryName,
const ValueThresholds* thresholds,
const std::map<uint32_t, std::string>& labels = {},
const std::vector<NamedValue>& namedValues = {},
bool showDefaultValue = true,
const char* subText = nullptr);
bool kip = false);
void addConfigButtonS(HocClkConfigValue configVal,
const char* altName,
const ValueRange& range,
const std::string& categoryName,
const ValueThresholds* thresholds,
const std::map<uint32_t, std::string>& labels = {},
const std::vector<NamedValue>& namedValues = {},
bool showDefaultValue = true,
const char* subText = nullptr,
bool kip = false);
void addFreqButton(HocClkConfigValue configVal,
const char* altName,
HocClkModule module,

View File

@@ -40,15 +40,16 @@ DEFINES := -DDISABLE_IPC -DTARGET="\"$(TARGET)\"" -DTARGET_VERSION="\"$(TARGET_V
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -Os -ffunction-sections \
$(ARCH) $(DEFINES) -ffast-math
CFLAGS := -g -Wall -Os \
-ffast-math -ffunction-sections -fdata-sections -fno-asynchronous-unwind-tables -fno-unwind-tables \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__
CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++23
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++23
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) -Wl,-wrap,__cxa_throw -Wl,-wrap,_Unwind_Resume -Wl,-wrap,__gxx_personality_v0
LIBS := $(foreach lib,$(LIBNAMES),-l$(lib)) -lnx

View File

@@ -39,6 +39,10 @@ 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
}

View File

@@ -117,6 +117,9 @@ 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)
{
@@ -256,6 +259,10 @@ static void _clock_update_freqs(void)
// 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);
g_emc_bw_all = (u64)emc_freq * 16 * g_emc_lall / 1000000;
g_emc_bw_cpu = (u64)emc_freq * 16 * g_emc_lcpu / 1000000;
g_emc_bw_gpu = g_emc_bw_all > g_emc_bw_cpu ? g_emc_bw_all - g_emc_bw_cpu : 0;
}
@@ -288,3 +295,27 @@ 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;
}

View File

@@ -3,7 +3,7 @@
"title_id": "0x00FF0000636C6BFF",
"title_id_range_min": "0x00FF0000636C6BFF",
"title_id_range_max": "0x00FF0000636C6BFF",
"main_thread_stack_size": "0x0000D000",
"main_thread_stack_size": "0x0000F000",
"main_thread_priority": 16,
"default_cpu_id": 3,
"process_category": 1,
@@ -86,7 +86,8 @@
"is_ro": false,
"is_io": true
}
}, {
},
{
"type": "syscalls",
"value": {
"svcUnknown": "0x00",

View File

@@ -44,6 +44,8 @@
#include "../file_utils.hpp"
namespace board {
u64 clkVirtAddr, dsiVirtAddr;
HocClkSocType gSocType;
u8 gDramID;
HocClkConsoleType gConsoleType = HocClkConsoleType_Iowa;
@@ -145,14 +147,12 @@ namespace board {
StartMiscThread(pwmCheck, &iCon);
u64 clkVirtAddr, dsiVirtAddr;
rc = QueryMemoryMapping(&clkVirtAddr, 0x60006000, 0x1000);
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (clk)");
rc = QueryMemoryMapping(&dsiVirtAddr, 0x54300000, 0x40000);
ASSERT_RESULT_OK(rc, "QueryMemoryMapping (dsi)");
display::DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr, .isLite = (GetConsoleType() == HocClkConsoleType_Hoag), .isRetroSUPER = integrations::GetRETROSuperStatus()};
display::Initialize(&cfg);

View File

@@ -40,7 +40,7 @@
#define HOSSVC_HAS_TC (hosversionAtLeast(5,0,0))
namespace board {
extern u64 clkVirtAddr, dsiVirtAddr;
void Initialize();
void Exit();
HocClkSocType GetSocType();

View File

@@ -31,7 +31,8 @@
#include "board.hpp"
#include "board_name.hpp"
#include "../errors.hpp"
#include "pllmb.hpp"
#include "../config.hpp"
namespace board {
PcvModule GetPcvModule(HocClkModule hocclkModule) {
@@ -87,7 +88,7 @@ namespace board {
/* Voltage bug workaround. */
if (module == HocClkModule_CPU) {
svcSleepThread(250'000);
svcSleepThread(300'000);
ClkrstSetHz(session, hz);
}
@@ -96,7 +97,7 @@ namespace board {
PcvSetHz(GetPcvModule(module), hz);
if (module == HocClkModule_CPU) {
svcSleepThread(250'000);
svcSleepThread(300'000);
PcvSetHz(GetPcvModule(module), hz);
}
}
@@ -141,10 +142,9 @@ namespace board {
case HocClkModule_GPU:
return t210ClkGpuFreq();
case HocClkModule_MEM:
return t210ClkMemFreq();
return config::GetConfigValue(HocClkConfigValue_MemoryFrequencyMeasurementMode) == MemoryFrequencyMeasurementMode_PLL ? pllmb::getRamClockRatePLLMB() : t210ClkMemFreq();
case HocClkModule_Display:
return GetDisplayRate(hz);
return hz;
default:
ASSERT_ENUM_VALID(HocClkModule, module);
}

View File

@@ -120,6 +120,14 @@ namespace board {
return info.RawBatteryCharge;
case HocClkPartLoad_FAN:
return GetFanLevel();
case HocClkPartLoad_RamBWAll:
return t210EmcBwAll();
case HocClkPartLoad_RamBWCpu:
return t210EmcBwCpu();
case HocClkPartLoad_RamBWGpu:
return t210EmcBwGpu();
case HocClkPartLoad_RamBWPeak:
return t210EmcBwPeak();
default:
ASSERT_ENUM_VALID(HocClkPartLoad, loadSource);
}

View File

@@ -0,0 +1,132 @@
#include "pllmb.hpp"
namespace pllmb {
static const u8 qlin_hw_to_pdiv[17] = {
1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 30, 32
};
enum pdiv_type {
PDIV_QLIN,
PDIV_POW2
};
struct pll_desc_t {
u32 base_offset;
u8 divm_shift;
u8 divm_width;
u8 divn_shift;
u8 divn_width;
u8 divp_shift;
u8 divp_width;
pdiv_type ptype;
};
static const pll_desc_t pll_table[] = {
{ PLLM_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLMB_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLP_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
{ PLLA_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
{ PLLU_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
{ _PLLD_BASE, 0, 8, 8, 8, 20, 5, PDIV_POW2 },
{ PLLX_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLA1_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLDP_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLD2_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLC4_BASE, 0, 8, 8, 8, 20, 5, PDIV_QLIN },
{ PLLRE_BASE, 0, 8, 8, 8, 16, 4, PDIV_QLIN },
{ PLLC_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
{ PLLC2_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
{ PLLC3_BASE, 0, 8, 10, 8, 20, 5, PDIV_QLIN },
};
static inline u32 clk_read32(u32 offset)
{
return *(volatile u32 *)(uintptr_t)(board::clkVirtAddr + offset);
}
static inline u32 extract(u32 val, u8 shift, u8 width)
{
return (val >> shift) & ((1u << width) - 1u);
}
static u64 pll_rate_from_desc(const pll_desc_t &pll, u64 osc_hz,
bool undivided)
{
u32 base = clk_read32(pll.base_offset);
u32 divm = extract(base, pll.divm_shift, pll.divm_width);
u32 divn = extract(base, pll.divn_shift, pll.divn_width);
if (divm == 0 || divn == 0)
return 0;
u64 vco = osc_hz * divn / divm;
if (undivided)
return vco;
u32 hw_p = extract(base, pll.divp_shift, pll.divp_width);
u32 pdiv;
if (pll.ptype == PDIV_QLIN)
pdiv = (hw_p < 17) ? qlin_hw_to_pdiv[hw_p] : 1;
else
pdiv = 1u << hw_p;
return vco / pdiv;
}
static u64 pll_rate_by_offset(u32 base_offset, u64 osc_hz,
bool undivided)
{
for (const auto &pll : pll_table) {
if (pll.base_offset == base_offset)
return pll_rate_from_desc(pll, osc_hz, undivided);
}
return 0;
}
u64 getRamClockRatePLLMB()
{
u32 clk_src = clk_read32(CLK_SOURCE_EMC);
u32 src = (clk_src >> 29) & 0x7;
u32 div = (clk_src >> 0) & 0xff;
u32 pll_off;
bool undivided = false;
switch (src) {
case EMC_SRC_PLLM:
pll_off = PLLM_BASE;
break;
case EMC_SRC_PLLM_UD:
pll_off = PLLM_BASE;
undivided = true;
break;
case EMC_SRC_PLLMB:
pll_off = PLLMB_BASE;
break;
case EMC_SRC_PLLMB_UD:
pll_off = PLLMB_BASE;
undivided = true;
break;
case EMC_SRC_PLLP:
pll_off = PLLP_BASE;
break;
case EMC_SRC_PLLP_UD:
pll_off = PLLP_BASE;
undivided = true;
break;
case EMC_SRC_PLLC:
pll_off = PLLC_BASE;
break;
case EMC_SRC_CLK_M:
return OSC_HZ;
default:
return 0;
}
u64 pll_hz = pll_rate_by_offset(pll_off, OSC_HZ, undivided);
return pll_hz / (div + 2) * 2;
}
}

View File

@@ -0,0 +1,21 @@
#pragma once
#include <cstdint>
#include <switch.h>
#include <hocclk.h>
#include "board.hpp"
#include <registers.h>
namespace pllmb {
typedef enum PLLSource {
EMC_SRC_PLLM = 0,
EMC_SRC_PLLC = 1,
EMC_SRC_PLLP = 2,
EMC_SRC_CLK_M = 3,
EMC_SRC_PLLM_UD = 4,
EMC_SRC_PLLMB_UD = 5,
EMC_SRC_PLLMB = 6,
EMC_SRC_PLLP_UD = 7
} PLLSource;
u64 getRamClockRatePLLMB();
}

View File

@@ -230,7 +230,11 @@ namespace clockManager {
void DVFSBeforeSet(u32 targetHz)
{
s32 dvfsOffset = config::GetConfigValue(HocClkConfigValue_DVFSOffset);
u32 vmin = board::GetMinimumGpuVmin(targetHz / 1000000, board::GetGpuSpeedoBracket()) + dvfsOffset;
u32 vmin = board::GetMinimumGpuVmin(targetHz / 1000000, board::GetGpuSpeedoBracket());
if (vmin) {
vmin += dvfsOffset;
}
board::PcvHijackGpuVolts(vmin);
@@ -545,8 +549,6 @@ namespace clockManager {
void Initialize()
{
config::Initialize();
gContext = {};
gContext.applicationId = 0;
gContext.profile = HocClkProfile_Handheld;
@@ -592,7 +594,6 @@ namespace clockManager {
void Exit()
{
governor::exitThreads();
config::Exit();
}
HocClkContext GetCurrentContext()
@@ -621,7 +622,6 @@ namespace clockManager {
void Tick()
{
fileUtils::LogLine("CPU Temp: %d", board::GetTemperatureMilli(HocClkThermalSensor_CPU));
std::scoped_lock lock{gContextMutex};
std::uint32_t mode = 0;
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);

View File

@@ -33,6 +33,7 @@ namespace fileUtils {
namespace {
u64 bootTimeS;
LockableMutex g_log_mutex;
LockableMutex g_csv_mutex;
std::atomic_bool g_has_initialized = false;
@@ -82,10 +83,10 @@ namespace fileUtils {
FILE* file = fopen(FILE_LOG_FILE_PATH, "a");
if (file) {
struct timespec now;
timespec now = {};
clock_gettime(CLOCK_REALTIME, &now);
fprintf(file, "[%lu] ", armGetSystemTick());
fprintf(file, "[%luls] ", now.tv_sec - bootTimeS);
vfprintf(file, format, args);
fprintf(file, "\n");
fclose(file);
@@ -150,6 +151,12 @@ namespace fileUtils {
}
}
void SetBootTime() {
timespec bootTime = {};
clock_gettime(CLOCK_REALTIME, &bootTime);
bootTimeS = bootTime.tv_sec;
}
void InitializeAsync() {
Thread initThread = {0};
threadCreate(&initThread, InitializeThreadFunc, NULL, NULL, 0x4000, 0x15, 0);
@@ -165,6 +172,7 @@ namespace fileUtils {
__libnx_init_time();
timeExit();
SetBootTime();
if (R_SUCCEEDED(rc)) {
rc = fsInitialize();

View File

@@ -261,7 +261,7 @@ namespace ipcService {
ASSERT_RESULT_OK(rc, "svcGetThreadPriority");
rc = ipcServerInit(&gServer, HOCCLK_IPC_SERVICE_NAME, 42);
ASSERT_RESULT_OK(rc, "ipcServerInit");
rc = threadCreate(&gThread, &ProcessThreadFunc, nullptr, NULL, 0x2000, priority, -2);
rc = threadCreate(&gThread, &ProcessThreadFunc, nullptr, NULL, 0x4000, priority, -2);
ASSERT_RESULT_OK(rc, "threadCreate");
gRunning = false;
}

View File

@@ -17,11 +17,13 @@
#include "kip.hpp"
#include "board/board.hpp"
#include "file_utils.hpp"
#define CUST_REV 2
namespace kip {
bool kipAvailable = false;
void SetKipData()
{
// TODO: figure out if this REALLY causes issues (i doubt it)
@@ -45,11 +47,17 @@ namespace kip {
}
if (!cust_read_and_cache("sdmc:/atmosphere/kips/hoc.kip", &table)) {
fileUtils::LogLine("[clock_manager] Failed to read KIP file");
fileUtils::LogLine("[kip] Failed to read KIP file");
notification::writeNotification("Horizon OC\nKip read failed");
return;
}
// if(cust_get_cust_rev(&table) != CUST_REV) {
// fileUtils::LogLine("Revision: %u", cust_get_cust_rev(&table));
// notification::writeNotification("Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC");
// return;
// }
CUST_WRITE_FIELD_BATCH(&table, custRev, config::GetConfigValue(KipConfigValue_custRev));
// CUST_WRITE_FIELD_BATCH(&table, mtcConf, config::GetConfigValue(KipConfigValue_mtcConf));
CUST_WRITE_FIELD_BATCH(&table, hpMode, config::GetConfigValue(KipConfigValue_hpMode));
@@ -70,6 +78,23 @@ namespace kip {
CUST_WRITE_FIELD_BATCH(&table, t6_tRTW, config::GetConfigValue(KipConfigValue_t6_tRTW));
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR, config::GetConfigValue(KipConfigValue_t7_tWTR));
CUST_WRITE_FIELD_BATCH(&table, t8_tREFI, config::GetConfigValue(KipConfigValue_t8_tREFI));
CUST_WRITE_FIELD_BATCH(&table, stepMode, config::GetConfigValue(KipConfigValue_stepMode));
CUST_WRITE_FIELD_BATCH(&table, timingEmcTbreak, config::GetConfigValue(KipConfigValue_timingEmcTbreak));
CUST_WRITE_FIELD_BATCH(&table, low_t6_tRTW, config::GetConfigValue(KipConfigValue_low_t6_tRTW));
CUST_WRITE_FIELD_BATCH(&table, low_t7_tWTR, config::GetConfigValue(KipConfigValue_low_t7_tWTR));
CUST_WRITE_FIELD_BATCH(&table, t2_tRP_cap, config::GetConfigValue(KipConfigValue_t2_tRP_cap));
CUST_WRITE_FIELD_BATCH(&table, readLatency1333, config::GetConfigValue(KipConfigValue_read_latency_1333));
CUST_WRITE_FIELD_BATCH(&table, readLatency1600, config::GetConfigValue(KipConfigValue_read_latency_1600));
CUST_WRITE_FIELD_BATCH(&table, readLatency1866, config::GetConfigValue(KipConfigValue_read_latency_1866));
CUST_WRITE_FIELD_BATCH(&table, readLatency2133, config::GetConfigValue(KipConfigValue_read_latency_2133));
CUST_WRITE_FIELD_BATCH(&table, writeLatency1333, config::GetConfigValue(KipConfigValue_write_latency_1333));
CUST_WRITE_FIELD_BATCH(&table, writeLatency1600, config::GetConfigValue(KipConfigValue_write_latency_1600));
CUST_WRITE_FIELD_BATCH(&table, writeLatency1866, config::GetConfigValue(KipConfigValue_write_latency_1866));
CUST_WRITE_FIELD_BATCH(&table, writeLatency2133, config::GetConfigValue(KipConfigValue_write_latency_2133));
CUST_WRITE_FIELD_BATCH(&table, mem_burst_read_latency, config::GetConfigValue(KipConfigValue_mem_burst_read_latency));
CUST_WRITE_FIELD_BATCH(&table, mem_burst_write_latency, config::GetConfigValue(KipConfigValue_mem_burst_write_latency));
CUST_WRITE_FIELD_BATCH(&table, eristaCpuUV, config::GetConfigValue(KipConfigValue_eristaCpuUV));
@@ -110,7 +135,7 @@ namespace kip {
CUST_WRITE_FIELD_BATCH(&table, t7_tWTR_fine_tune, config::GetConfigValue(KipConfigValue_t7_tWTR_fine_tune));
if (!cust_write_table("sdmc:/atmosphere/kips/hoc.kip", &table)) {
fileUtils::LogLine("[clock_manager] Failed to write KIP file");
fileUtils::LogLine("[kip] Failed to write KIP file");
notification::writeNotification("Horizon OC\nKip write failed");
}
@@ -120,9 +145,12 @@ namespace kip {
configValues.values[KipCrc32] = (u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum
if (config::SetConfigValues(&configValues, false)) {
fileUtils::LogLine("[clock_manager] Successfully loaded KIP data into config");
fileUtils::LogLine("[kip] KIP data set. CRC32: %ld (Cust Rev %ld)", configValues.values[KipCrc32], configValues.values[KipConfigValue_custRev]);
for (u64 i = KipConfigValue_hpMode; i < HocClkConfigValue_EnumMax; i++) {
fileUtils::LogLine("%s: %ld", hocclkFormatConfigValue((HocClkConfigValue)i, false), configValues.values[i]);
}
} else {
fileUtils::LogLine("[clock_manager] Warning: Failed to set config values from KIP");
fileUtils::LogLine("[kip] Warning: Failed to set config values from KIP");
notification::writeNotification("Horizon OC\nKip config set failed");
}
}
@@ -148,25 +176,27 @@ namespace kip {
config::GetConfigValues(&configValues);
CustomizeTable table;
if (!cust_read_and_cache("sdmc:/atmosphere/kips/hoc.kip", &table)) {
fileUtils::LogLine("[clock_manager] Failed to read KIP file for GetKipData");
fileUtils::LogLine("[kip] Failed to read KIP file for GetKipData");
notification::writeNotification("Horizon OC\nKip read failed");
return;
}
// if(cust_get_cust_rev(&table) != CUST_REV) {
// notification::writeNotification("Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC");
// return;
// }
if ((u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip") != config::GetConfigValue(KipCrc32) && !config::GetConfigValue(HocClkConfigValue_IsFirstLoad)) {
SetKipData();
notification::writeNotification("Horizon OC\nKIP has been updated");
notification::writeNotification("Horizon OC\nPlease reboot your console");
notification::writeNotification("Horizon OC\nto complete the update");
notification::writeNotification("Horizon OC\nKIP has been updated\nPlease reboot your console");
return;
}
if (config::GetConfigValue(HocClkConfigValue_IsFirstLoad) == true) {
configValues.values[HocClkConfigValue_IsFirstLoad] = (u64)false;
notification::writeNotification("Horizon OC has been installed");
}
configValues.values[KipCrc32] = (u64)crc32::checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum
// configValues.values[KipConfigValue_mtcConf] = cust_get_mtc_conf(&table);
configValues.values[KipConfigValue_hpMode] = cust_get_hp_mode(&table);
@@ -187,6 +217,23 @@ namespace kip {
configValues.values[KipConfigValue_t6_tRTW] = cust_get_tRTW(&table);
configValues.values[KipConfigValue_t7_tWTR] = cust_get_tWTR(&table);
configValues.values[KipConfigValue_t8_tREFI] = cust_get_tREFI(&table);
configValues.values[KipConfigValue_stepMode] = cust_get_step_mode(&table);
configValues.values[KipConfigValue_timingEmcTbreak] = cust_get_timing_emc_tbreak(&table);
configValues.values[KipConfigValue_low_t6_tRTW] = cust_get_low_t6_tRTW(&table);
configValues.values[KipConfigValue_low_t7_tWTR] = cust_get_low_t7_tWTR(&table);
configValues.values[KipConfigValue_t2_tRP_cap] = cust_get_tRP_cap(&table);
configValues.values[KipConfigValue_read_latency_1333] = cust_get_read_latency_1333(&table);
configValues.values[KipConfigValue_read_latency_1600] = cust_get_read_latency_1600(&table);
configValues.values[KipConfigValue_read_latency_1866] = cust_get_read_latency_1866(&table);
configValues.values[KipConfigValue_read_latency_2133] = cust_get_read_latency_2133(&table);
configValues.values[KipConfigValue_write_latency_1333] = cust_get_write_latency_1333(&table);
configValues.values[KipConfigValue_write_latency_1600] = cust_get_write_latency_1600(&table);
configValues.values[KipConfigValue_write_latency_1866] = cust_get_write_latency_1866(&table);
configValues.values[KipConfigValue_write_latency_2133] = cust_get_write_latency_2133(&table);
configValues.values[KipConfigValue_mem_burst_read_latency] = cust_get_burst_read_lat(&table);
configValues.values[KipConfigValue_mem_burst_write_latency] = cust_get_burst_write_lat(&table);
@@ -224,22 +271,27 @@ namespace kip {
configValues.values[KipConfigValue_t7_tWTR_fine_tune] = cust_get_tWTR_fine_tune(&table);
configValues.values[KipConfigValue_t6_tRTW_fine_tune] = cust_get_tRTW_fine_tune(&table);
// if(cust_get_cust_rev(&table) == KIP_CUST_REV)
// return;
if (sizeof(HocClkConfigValueList) <= sizeof(configValues)) {
if (config::SetConfigValues(&configValues, false)) {
fileUtils::LogLine("[clock_manager] Successfully loaded KIP data into config");
fileUtils::LogLine("[kip] KIP loaded. CRC32: %ld (Cust Rev %ld)", configValues.values[KipCrc32], configValues.values[KipConfigValue_custRev]);
for (u64 i = KipConfigValue_hpMode; i < HocClkConfigValue_EnumMax; i++) {
fileUtils::LogLine("%s: %ld", hocclkFormatConfigValue((HocClkConfigValue)i, false), configValues.values[i]);
}
} else {
fileUtils::LogLine("[clock_manager] Warning: Failed to set config values from KIP");
fileUtils::LogLine("[kip] Warning: Failed to set config values from KIP");
notification::writeNotification("Horizon OC\nKip config set failed");
}
} else {
fileUtils::LogLine("[clock_manager] Error: Config value list buffer size mismatch");
fileUtils::LogLine("[kip] Error: Config value list buffer size mismatch");
notification::writeNotification("Horizon OC\nConfig Buffer Mismatch");
}
} else {
fileUtils::LogLine("[clock_manager] Config refresh error in GetKipData!");
fileUtils::LogLine("[kip] Config refresh error in GetKipData!");
notification::writeNotification("Horizon OC\nConfig refresh failed");
}
}

View File

@@ -30,7 +30,7 @@
namespace kip {
extern bool kipAvailable;
typedef struct {
u8 cust[4];
u32 custRev;
@@ -40,6 +40,7 @@ namespace kip {
u32 eristaEmcMaxClock;
u32 eristaEmcMaxClock1;
u32 eristaEmcMaxClock2;
u32 stepMode;
u32 marikoEmcMaxClock;
u32 marikoEmcVddqVolt;
u32 emcDvbShift;
@@ -53,6 +54,16 @@ namespace kip {
u32 t7_tWTR;
u32 t8_tREFI;
u32 t2_tRP_cap;
u32 timingEmcTbreak;
u32 low_t6_tRTW;
u32 low_t7_tWTR;
/* These latencies are arrays in loader, but it's easier to handle it this way in the configurator. */
u32 readLatency1333, readLatency1600, readLatency1866, readLatency2133;
u32 writeLatency1333, writeLatency1600, writeLatency1866, writeLatency2133;
u32 mem_burst_read_latency;
u32 mem_burst_write_latency;
@@ -201,6 +212,7 @@ namespace kip {
static inline bool cust_set_erista_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock, v); }
static inline bool cust_set_erista_emc_max1(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock1, v); }
static inline bool cust_set_erista_emc_max2(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaEmcMaxClock2, v); }
static inline bool cust_set_step_mode(const char* p, u32 v) { CUST_WRITE_FIELD(p, stepMode, v); }
static inline bool cust_set_mariko_emc_max(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcMaxClock, v); }
static inline bool cust_set_mariko_emc_vddq(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoEmcVddqVolt, v); }
static inline bool cust_set_emc_dvb_shift(const char* p, u32 v) { CUST_WRITE_FIELD(p, emcDvbShift, v); }
@@ -213,8 +225,23 @@ namespace kip {
static inline bool cust_set_tRTW(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW, v); }
static inline bool cust_set_tWTR(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR, v); }
static inline bool cust_set_tREFI(const char* p, u32 v) { CUST_WRITE_FIELD(p, t8_tREFI, v); }
static inline bool cust_set_tRP_cap(const char* p, u32 v) { CUST_WRITE_FIELD(p, t2_tRP_cap, v); }
static inline bool cust_set_timing_emc_tbreak(const char* p, u32 v) { CUST_WRITE_FIELD(p, timingEmcTbreak, v); }
static inline bool cust_set_low_tRTW(const char* p, u32 v) { CUST_WRITE_FIELD(p, low_t6_tRTW, v); }
static inline bool cust_set_low_tWTR(const char* p, u32 v) { CUST_WRITE_FIELD(p, low_t7_tWTR, v); }
static inline bool cust_set_tRTW_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t6_tRTW_fine_tune, v); }
static inline bool cust_set_tWTR_fine_tune(const char* p, u32 v) { CUST_WRITE_FIELD(p, t7_tWTR_fine_tune, v); }
static inline bool cust_set_read_latency_1333(const char* p, u32 v) { CUST_WRITE_FIELD(p, readLatency1333, v); }
static inline bool cust_set_read_latency_1600(const char* p, u32 v) { CUST_WRITE_FIELD(p, readLatency1600, v); }
static inline bool cust_set_read_latency_1866(const char* p, u32 v) { CUST_WRITE_FIELD(p, readLatency1866, v); }
static inline bool cust_set_read_latency_2133(const char* p, u32 v) { CUST_WRITE_FIELD(p, readLatency2133, v); }
static inline bool cust_set_write_latency_1333(const char* p, u32 v) { CUST_WRITE_FIELD(p, writeLatency1333, v); }
static inline bool cust_set_write_latency_1600(const char* p, u32 v) { CUST_WRITE_FIELD(p, writeLatency1600, v); }
static inline bool cust_set_write_latency_1866(const char* p, u32 v) { CUST_WRITE_FIELD(p, writeLatency1866, v); }
static inline bool cust_set_write_latency_2133(const char* p, u32 v) { CUST_WRITE_FIELD(p, writeLatency2133, v); }
static inline bool cust_set_burst_read_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_read_latency, v); }
static inline bool cust_set_burst_write_lat(const char* p, u32 v) { CUST_WRITE_FIELD(p, mem_burst_write_latency, v); }
@@ -272,6 +299,7 @@ namespace kip {
static inline u32 cust_get_erista_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock); }
static inline u32 cust_get_erista_emc_max1(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock1); }
static inline u32 cust_get_erista_emc_max2(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaEmcMaxClock2); }
static inline u32 cust_get_step_mode(const CustomizeTable* t) { return CUST_GET_FIELD(t, stepMode); }
static inline u32 cust_get_mariko_emc_max(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcMaxClock); }
static inline u32 cust_get_mariko_emc_vddq(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoEmcVddqVolt); }
static inline u32 cust_get_emc_dvb_shift(const CustomizeTable* t) { return CUST_GET_FIELD(t, emcDvbShift); }
@@ -284,8 +312,23 @@ namespace kip {
static inline u32 cust_get_tRTW(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW); }
static inline u32 cust_get_tWTR(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR); }
static inline u32 cust_get_tREFI(const CustomizeTable* t) { return CUST_GET_FIELD(t, t8_tREFI); }
static inline u32 cust_get_tRP_cap(const CustomizeTable* t) { return CUST_GET_FIELD(t, t2_tRP_cap); }
static inline u32 cust_get_timing_emc_tbreak(const CustomizeTable* t) { return CUST_GET_FIELD(t, timingEmcTbreak); }
static inline u32 cust_get_low_t6_tRTW(const CustomizeTable* t) { return CUST_GET_FIELD(t, low_t6_tRTW); }
static inline u32 cust_get_low_t7_tWTR(const CustomizeTable* t) { return CUST_GET_FIELD(t, low_t7_tWTR); }
static inline u32 cust_get_tRTW_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t6_tRTW_fine_tune); }
static inline u32 cust_get_tWTR_fine_tune(const CustomizeTable* t) { return CUST_GET_FIELD(t, t7_tWTR_fine_tune); }
static inline u32 cust_get_read_latency_1333(const CustomizeTable* t) { return CUST_GET_FIELD(t, readLatency1333); }
static inline u32 cust_get_read_latency_1600(const CustomizeTable* t) { return CUST_GET_FIELD(t, readLatency1600); }
static inline u32 cust_get_read_latency_1866(const CustomizeTable* t) { return CUST_GET_FIELD(t, readLatency1866); }
static inline u32 cust_get_read_latency_2133(const CustomizeTable* t) { return CUST_GET_FIELD(t, readLatency2133); }
static inline u32 cust_get_write_latency_1333(const CustomizeTable* t) { return CUST_GET_FIELD(t, writeLatency1333); }
static inline u32 cust_get_write_latency_1600(const CustomizeTable* t) { return CUST_GET_FIELD(t, writeLatency1600); }
static inline u32 cust_get_write_latency_1866(const CustomizeTable* t) { return CUST_GET_FIELD(t, writeLatency1866); }
static inline u32 cust_get_write_latency_2133(const CustomizeTable* t) { return CUST_GET_FIELD(t, writeLatency2133); }
static inline u32 cust_get_burst_read_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_read_latency); }
static inline u32 cust_get_burst_write_lat(const CustomizeTable* t) { return CUST_GET_FIELD(t, mem_burst_write_latency); }

View File

@@ -38,7 +38,7 @@
#include "ipc_service.hpp"
#include "config.hpp"
#define INNER_HEAP_SIZE 0x45000
#define INNER_HEAP_SIZE 0x4A000
extern "C"
{
@@ -55,7 +55,7 @@ extern "C"
NvServiceType __nx_nv_service_type = NvServiceType_Factory;
// Ty to MasaGratoR for this!
//This is done to save some space as they have no practical use in our case
// This is done to save some space as they have no practical use in our case
void* __real___cxa_throw(void *thrown_exception, void *pvar, void (*dest)(void *));
void* __real__Unwind_Resume();
void* __real___gxx_personality_v0();
@@ -134,6 +134,7 @@ int main(int argc, char** argv)
fatalThrow(rc);
return 1;
}
config::Initialize();
board::Initialize();
processManagement::Initialize();
@@ -164,7 +165,7 @@ int main(int argc, char** argv)
clockManager::Exit();
processManagement::Exit();
board::Exit();
config::Exit();
fileUtils::LogLine("Exit");
svcSleepThread(1000000ULL);
fileUtils::Exit();

View File

@@ -1 +1 @@
1.11.0
1.11.1

View File

@@ -8,7 +8,7 @@ mkdir -p "$DEST"
cp -r "$SRC"/. "$DEST"/
cd build/stratosphere/loader || exit 1
make -j"$(nproc)"
make -j8
hactool -t kip1 out/nintendo_nx_arm64_armv8a/release/loader.kip --uncompress=hoc.kip
cd ../../../ # exit
cp build/stratosphere/loader/hoc.kip dist/atmosphere/kips/hoc.kip
@@ -20,5 +20,5 @@ cp -r dist/ ../../
cd ../../
cd Source/Horizon-OC-Monitor/
make -j"$(nproc)"
make -j8
cp Horizon-OC-Monitor.ovl ../../dist/switch/.overlays/Horizon-OC-Monitor.ovl

46
dist/README.md vendored
View File

@@ -32,7 +32,7 @@ It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration to
---
## Features
## Default clocks
* **CPU:** Up to 1963MHz (Mariko) / 1785MHz (Erista)
* **GPU:** Up to 1075MHz (Mariko) / 921MHz (Erista)
@@ -93,7 +93,8 @@ Refer to COMPILATION.md
* 665
### CPU clocks
* 2601 → mariko absolute max, very dangerous
* 2703 → mariko absolute max, dangerous
* 2601 → unsafe
* 2499
* 2397 → mariko safe max with UV (low speedo)
* 2295
@@ -113,9 +114,6 @@ Refer to COMPILATION.md
* 816
* 714
* 612 → sleep mode
**Notes:**
1. On Erista, CPU in handheld is capped to 1581MHz
### GPU clocks
* 1536 → absolute max clock on mariko. very dangerous
@@ -142,27 +140,33 @@ Refer to COMPILATION.md
* 76 → boost mode
**Notes:**
1. GPU overclock is capped at 460MHz on erista in handheld
2. On Mariko, cap with No uv is 614MHz, with SLT it is 691MHz and with HiOPT it's 768MHz
3. Clocks higher than 768MHz on erista need the official charger is plugged in.
4. On Mariko, cap with No uv is 844MHz, with SLT it is 921MHz and with HiOPT it's 998MHz
1. On Erista, CPU in handheld is capped to 1581MHz
2. GPU overclock is capped at 460MHz on erista in handheld
3. On Mariko, cap with No uv is 614MHz, with SLT it is 691MHz and with HiOPT it's 768MHz
4. Clocks higher than 768MHz on erista need the official charger is plugged in.
5. On Mariko, cap with No uv is 844MHz, with SLT it is 921MHz and with HiOPT it's 998MHz
---
## Credits
* **Lightos's Cat** - Cat
* **Souldbminer** hoc-clk and loader development
* **Lightos** loader patches development
* **Souldbminer** - hoc-clk and loader development
* **Lightos** - Loader patches development, hoc-clk development, guides
* **SciresM** - Atmosphere CFW
* **CTCaer** - L4T, Hekate, perfect ram timings
* **KazushiMe** Switch OC Suite
* **hanai3bi (meha)** Switch OC Suite, EOS, sys-clk-eos
* **NaGaa95** L4T-OC-kernel
* **B3711 (halop)** EOS
* **sys-clk team (m4xw, p-sam, natinusala)** sys-clk
* **b0rd2death** Ultrahand sys-clk & Status Monitor fork
* **CTCaer** - L4T, Hekate, proper RAM timings
* **KazushiMe** - Switch OC Suite
* **Hanai3bi (Meha)** - Switch OC Suite, EOS, sys-clk-eos
* **NaGaa95** - L4T-OC kernel, Status Monitor fork
* **B3711 (halop)** - EOS
* **sys-clk team (m4xw, p-sam, natinusala)** - sys-clk
* **Dominatorul** - Soctherm driver, guides, general help
* **b0rd2death** - Ultrahand sys-clk & Status Monitor fork
* **MasaGratoR and ZachyCatGames** - General help
* **MasaGratoR** - Status Monitor & Display Refresh Rate Driver
* **Dom, Samybigio, Arcdelta, Miki, Happy, Flopsider, Winnerboi77, Blaise, Alvise, TDRR, agjeococh, frost, letum00 and Xenshen** - Testing
* **Samybigio2011** - Italian translations
* **MasaGratoR** - Status Monitor & Display Refresh Rate driver
* **Dominatorul, Samybigio, Arcdelta, Miki, Happy, Flopsider, Winnerboi77, Blaise, Alvise, TDRR, agjeococh, frost, letum00, and Xenshen** - Testing
* **Samybigio2011, Miki** - Italian translations
* **angelblaster** - Korean translations
* **q1332348216-glitch** - Chinese translations
* **Nvidia** - [Tegra X1 Technical Reference Manual](https://developer.nvidia.com/embedded/dlc/tegra-x1-technical-reference-manual), soctherm driver, L4T

Binary file not shown.

View File

@@ -2,140 +2,140 @@
"Information": "Informazioni",
"IDDQ:": "IDDQ:",
"Module: ": "Modulo:",
"sys-dock status:": "stato del dock di sistema:",
"sys-dock status:": "stato di sys-dock",
"SaltyNX status:": "Stato di SaltyNX:",
"RR Display status:": "Stato di visualizzazione RR:",
"Wafer Position:": "Posizione del wafer:",
"RR Display status:": "Stato del RR:",
"Wafer Position:": "Posizione nel Wafer:",
"Credits": "Crediti",
"Developers": "Sviluppatori",
"Contributors": "Collaboratori",
"Testers": "Tester",
"Special Thanks": "Un ringraziamento speciale",
"Special Thanks": "Un Ringraziamento Speciale",
"Unknown": "Sconosciuto",
"Installed": "Installato",
"Not Installed": "Non installato",
"X: %u Y: %u": "X: %u Y: %u",
"THE BEER-WARE LICENSE": "LA LICENZA PER GLI ARTICOLI DI BIRRA",
"THE BEER-WARE LICENSE": "THE BEER-WARE LICENSE",
"Default": "Predefinito",
"Do Not Override": "Non sovrascrivere",
"Do Not Override": "Non Sovrascrivere",
"Disabled": "Disabilitato",
"Enabled": "Abilitato",
" \\ue0e3 Reset": "\\ue0e3 Ripristina",
"Display": "Visualizzazione",
"Display": "Schermo",
"Application changed\\n\\n": "Applicazione modificata\\n\\n",
"The running application changed\\n\\n": "L'applicazione in esecuzione è cambiata\\n\\n",
"while editing was going on.": "mentre era in corso la modifica.",
"Board": "Consiglio",
"Board": "Scheda",
"%u.%u%u mV": "%u.%u%u mV",
"Could not connect to hoc-clk sysmodule.\\n\\n": "Impossibile connettersi al modulo di sistema hoc-clk.\\n\\n",
"Could not connect to hoc-clk sysmodule.\\n\\n": "Impossibile connettersi al sysmodule hoc-clk.\\n\\n",
"Please make sure everything is\\n\\n": "Assicurati che tutto sia\\n\\n",
"correctly installed and enabled.": "correttamente installato e abilitato.",
"Fatal error": "Errore fatale",
"Temporary Overrides ": "Sostituzioni temporanee",
"Sleep Mode": "Modalità di sospensione",
"Stock": "Magazzino",
"Dev OC": "OC di sviluppo",
"Boost Mode": "Modalità potenziamento",
"Safe Max": "Sicuro massimo",
"Unsafe Max": "Non sicuro Max",
"Absolute Max": "Massimo assoluto",
"Handheld Safe Max": "Cassaforte portatile max",
"Temporary Overrides ": "Sostituzioni Temporanee",
"Sleep Mode": "Modalità di Sospensione",
"Stock": "Originale",
"Dev OC": "OC dev",
"Boost Mode": "Modalità Boost",
"Safe Max": "Massimo Sicuro",
"Unsafe Max": "Massimo Non Sicuro",
"Absolute Max": "Massimo Assoluto",
"Handheld Safe Max": "Massimo Sicuro Modalità Portatile",
"Enable": "Abilita",
"Edit App Profile": "Modifica profilo dell'app",
"Edit Global Profile": "Modifica profilo globale",
"Temporary Overrides": "Sostituzioni temporanee",
"Edit App Profile": "Modifica Profilo Dell'App",
"Edit Global Profile": "Modifica Profilo Globale",
"Temporary Overrides": "Sostituzioni Temporanee",
"Settings": "Impostazioni",
"About": "Circa",
"About": "A Riguardo Di",
"Compiling with minimal features": "Compilazione con funzionalità minime",
"General Settings": "Impostazioni generali",
"Governor Settings": "Impostazioni del governatore",
"Safety Settings": "Impostazioni di sicurezza",
"Save KIP Settings": "Salva le impostazioni KIP",
"General Settings": "Impostazioni Generali",
"Governor Settings": "Impostazioni Del Governor",
"Safety Settings": "Impostazioni Di Sicurezza",
"Save KIP Settings": "Salva le impostazioni del KIP",
"RAM Settings": "Impostazioni della RAM",
"CPU Settings": "Impostazioni della CPU",
"GPU Settings": "Impostazioni della GPU",
"Display Settings": "Impostazioni di visualizzazione",
"Display Settings": "Impostazioni dello Schermo",
"Experimental": "Sperimentale",
"GPU Scheduling Override Method": "Metodo di override della pianificazione GPU",
"GPU Scheduling Override Method": "Metodo di override dello scheduling GPU",
"can be dangerous and may cause": "può essere pericoloso e può causare",
"damage to your battery or charger!": "danni alla batteria o al caricabatterie!",
"Charge Current Override": "Override della corrente di carica",
"RAM Voltage Display Mode": "Modalità di visualizzazione della tensione RAM",
"Charge Current Override": "Override della Corrente di Carica",
"RAM Voltage Display Mode": "Modalità di Visualizzazione della Tensione RAM",
"Polling Interval": "Intervallo di polling",
"CPU Governor Minimum Frequency": "Frequenza minima del governatore della CPU",
"CPU Governor Minimum Frequency": "Frequenza minima del Governor della CPU",
"refresh rates may cause stress": "le frequenze di aggiornamento possono causare stress",
"or damage to your display! ": "o danni al display!",
"Proceed at your own risk!": "Procedi a tuo rischio e pericolo!",
"Max Handheld Display": "Display portatile massimo",
"Display Clock": "Visualizza orologio",
"Official Rating": "Valutazione ufficiale",
"Max Handheld Display": "Display Massimo in Modalità Portatile",
"Display Clock": "Frequenza del Display",
"Official Rating": "Rating Ufficiale",
"TDP Threshold": "Soglia TDP",
"Power": "Potenza",
"Thermal Throttle Limit": "Limite della valvola termica",
"Thermal Throttle Limit": "Limite Termico",
"HP Mode": "Modalità HP",
"Default (Mariko)": "Predefinito (Mariko)",
"Default (Erista)": "Predefinito (Erista)",
"Rating": "Valutazione",
"Safe Max (Mariko)": "Safe Max (Mariko)",
"Safe Max (Erista)": "Safe Max (Erista)",
"Safe Max (Mariko)": "Massimo Sicuro (Mariko)",
"Safe Max (Erista)": "Massimo Sicuro (Erista)",
"RAM VDD2 Voltage": "Tensione RAM VDD2",
"Voltage": "Voltaggio",
"RAM VDDQ Voltage": "Voltaggio VDDQ della RAM",
"RAM Frequency Editor": "Editor della frequenza RAM",
"JEDEC.": "JEDEC.",
"High speedo needed!": "È necessaria l'alta velocità!",
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (richiede Speedo/PLL estremo)",
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (richiede Speedo/PLL estremo)",
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (richiede Speedo/PLL estremo)",
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 MHz (è necessario un ridicolo Speedo/PLL)",
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (è necessario un ridicolo Speedo/PLL)",
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (è necessario un ridicolo Speedo/PLL)",
"Ram Max Clock": "Orologio Ram Max",
"RAM Latency Editor": "Editor della latenza RAM",
"RAM Timing Reductions": "Riduzioni della temporizzazione della RAM",
"Memory Timings": "Tempi di memoria",
"High speedo needed!": "Alto Valore Speedo Necessario!",
"3333MHz (Needs extreme Speedo/PLL)": "3333 MHz (richiede Speedo/PLL altissimo)",
"3366MHz (Needs extreme Speedo/PLL)": "3366 MHz (richiede Speedo/PLL altissimo)",
"3400MHz (Needs extreme Speedo/PLL)": "3400 MHz (richiede Speedo/PLL altissimo)",
"3433MHz (Needs ridiculous Speedo/PLL)": "3433 MHz (richiede Speedo/PLL estremo)",
"3466MHz (Needs ridiculous Speedo/PLL)": "3466 MHz (richiede Speedo/PLL estremo)",
"3500MHz (Needs ridiculous Speedo/PLL)": "3500 MHz (richiede Speedo/PLL estremo)",
"Ram Max Clock": "Frequenza Massima Ram",
"RAM Latency Editor": "Editor della Latenza RAM",
"RAM Timing Reductions": "Riduzioni dei Timing della RAM",
"Memory Timings": "Timing di Memoria",
"Advanced": "Avanzato",
"t6 tRTW Fine Tune": "t6 tRTW Sintonia fine",
"tRTW Fine Tune": "tRTW Sintonia fine",
"t7 tWTR Fine Tune": "t7 tWTR Sintonia fine",
"tWTR Fine Tune": "tWTR Sintonia fine",
"Memory Latencies": "Latenza della memoria",
"Read Latency": "Leggi latenza",
"Write Latency": "Scrivi latenza",
"CPU Boost Clock": "Orologio di potenziamento della CPU",
"CPU UV": "UV della CPU",
"t6 tRTW Fine Tune": "Regolazione Fine t6 tRTW",
"tRTW Fine Tune": "Regolazione Fine tRTW",
"t7 tWTR Fine Tune": "Regolazione Fine t7 tWTR",
"tWTR Fine Tune": "Regolazione Fine tWTR",
"Memory Latencies": "Latenza della Memoria",
"Read Latency": "Latenza di Lettura",
"Write Latency": "Latenza di Scrittura",
"CPU Boost Clock": "Frequenza CPU in Boost",
"CPU UV": "Undervolt CPU",
"CPU Unlock": "Sblocco della CPU",
"CPU VMIN": "CPUVMIN",
"CPU VMIN": "CPU VMIN",
"CPU Max Voltage": "Voltaggio massimo della CPU",
"CPU Max Clock": "Orologio massimo della CPU",
"Extreme UV Table": "Tavolo UV estremo",
"CPU Max Clock": "Frequenza massima della CPU",
"Extreme UV Table": "Tabella UV estremo",
"CPU UV Table": "Tabella UV della CPU",
"CPU Low UV": "CPU con raggi UV bassi",
"CPU High UV": "UV elevato della CPU",
"CPU Low VMIN": "VMIN CPU basso",
"CPU High VMIN": "CPU alta VMIN",
"No Undervolt": "Nessuna sottotensione",
"CPU Low UV": "CPU UV Bassa Frequenza",
"CPU High UV": "CPU UV Alta Frequenza",
"CPU Low VMIN": "CPU VMIN Bassa Frequenza",
"CPU High VMIN": "CPU VMIN Alta Frequenza",
"No Undervolt": "Nessun Undervolt",
"SLT Table": "Tabella SLT",
"HiOPT Table": "Tabella HiOPT",
"GPU Undervolt Table": "Tabella di sottotensione GPU",
"GPU Minimum Voltage": "Voltaggio minimo della GPU",
"GPU Undervolt Table": "Tabella di Undervolt GPU",
"GPU Minimum Voltage": "Voltaggio Minimo della GPU",
"Calculate GPU Vmin": "Calcola GPU Vmin",
"GPU VMIN": "GPUVMIN",
"GPU VMIN": "GPU VMIN",
"GPU Maximum Voltage": "Voltaggio massimo della GPU",
"GPU Voltage Offset": "Offset di tensione della GPU",
"GPU Voltage Offset": "Offset di Voltaggio della GPU",
"Do not override": "Non sovrascrivere",
"Enabled (Default)": "Abilitato (impostazione predefinita)",
"96.6% limit": "Limite del 96,6%.",
"99.7% limit": "Limite del 99,7%.",
"GPU Scheduling Override": "Override della pianificazione GPU",
"GPU Scheduling Override": "Override dello Scheduling GPU",
"Official Service": "Servizio ufficiale",
"GPU DVFS Mode": "Modalità DVFS GPU",
"GPU DVFS Offset": "Offset DVFS della GPU",
"GPU Voltage Table": "Tabella delle tensioni della GPU",
"GPU Custom Table (mV)": "Tabella personalizzata GPU (mV)",
"GPU Voltage Table": "Tabella delle Tensioni della GPU",
"GPU Custom Table (mV)": "Tabella GPU Personalizzata (mV)",
"1075MHz without UV, 1152MHz on SLT": "1075 MHz senza UV, 1152 MHz su SLT",
"or 1228MHz on HiOPT can cause ": "o 1228 MHz su HiOPT possono causare",
"permanent damage to your Switch!": "danni permanenti al tuo Switch!",
"921MHz without UV and 960MHz on": "921 MHz senza UV e 960 MHz attivi",
"permanent damage to your Switch!": "danni permanenti alla tua Switch!",
"921MHz without UV and 960MHz on": "921 MHz senza UV e 960 MHz su",
"SLT or HiOPT can cause ": "SLT o HiOPT possono causare"
}

Binary file not shown.

Binary file not shown.