diff --git a/Source/rewrite-hoc-clk/.gitignore b/Source/rewrite-hoc-clk/.gitignore new file mode 100644 index 00000000..cc543c0e --- /dev/null +++ b/Source/rewrite-hoc-clk/.gitignore @@ -0,0 +1,5 @@ +/dist +.DS_Store +Thumbs.db +desktop.ini +.vscode \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/.gitlab-ci.yml b/Source/rewrite-hoc-clk/.gitlab-ci.yml new file mode 100644 index 00000000..80ebec89 --- /dev/null +++ b/Source/rewrite-hoc-clk/.gitlab-ci.yml @@ -0,0 +1,17 @@ +image: $CI_SERVER_HOST:4567/libretro/infrastructure/libretro-build-libnx-devkitpro:latest + +variables: + PACKAGE_FOLDER: "sys-clk" + +stages: + - package + +nightly: + stage: package + script: + - bash build.sh $PACKAGE_FOLDER + artifacts: + name: $PACKAGE_FOLDER + expire_in: 24 hours + paths: + - $PACKAGE_FOLDER diff --git a/Source/rewrite-hoc-clk/.gitmodules b/Source/rewrite-hoc-clk/.gitmodules new file mode 100644 index 00000000..3db71a4d --- /dev/null +++ b/Source/rewrite-hoc-clk/.gitmodules @@ -0,0 +1,3 @@ +[submodule "overlay/lib/libultrahand"] + path = overlay/lib/libultrahand + url = https://github.com/ppkantorski/libultrahand diff --git a/Source/rewrite-hoc-clk/LICENSE b/Source/rewrite-hoc-clk/LICENSE new file mode 100644 index 00000000..72648e0e --- /dev/null +++ b/Source/rewrite-hoc-clk/LICENSE @@ -0,0 +1,7 @@ +-------------------------------------------------------------------------- +"THE BEER-WARE LICENSE" (Revision 42): +, , +wrote this file. As long as you retain this notice you can do whatever you +want with this stuff. If you meet any of us some day, and you think this +stuff is worth it, you can buy us a beer in return. - The sys-clk authors +-------------------------------------------------------------------------- diff --git a/Source/rewrite-hoc-clk/README.md b/Source/rewrite-hoc-clk/README.md new file mode 100644 index 00000000..aeb88c8c --- /dev/null +++ b/Source/rewrite-hoc-clk/README.md @@ -0,0 +1,6 @@ +# hoc-clk + +Switch sysmodule allowing you to set cpu/gpu/mem clocks according to the running application and docked state. +Modified for Horizon OC + +Support is only provided for FW 16.0.0+. This MAY work on older firmwares but support is NOT guaranteed \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/bitmap.py b/Source/rewrite-hoc-clk/bitmap.py new file mode 100644 index 00000000..bdf81362 --- /dev/null +++ b/Source/rewrite-hoc-clk/bitmap.py @@ -0,0 +1,53 @@ +from PIL import Image +import argparse +import os + +def image_to_rgba8888_array(image_path, output_path): + # Open and convert to RGBA + img = Image.open(image_path).convert('RGBA') + width, height = img.size + + # Get pixel data + pixels = img.tobytes() + + # Write as C header file + with open(output_path, 'w') as f: + f.write('// This is a automatically generated file, do not edit manually.\n') + f.write(f'// {os.path.basename(image_path)} - {width}x{height}\n') + f.write(f'const unsigned int IMG_WIDTH = {width};\n') + f.write(f'const unsigned int IMG_HEIGHT = {height};\n') + f.write('const unsigned char IMG_DATA[] = {\n ') + + for i, byte in enumerate(pixels): + f.write(f'0x{byte:02X}') + if i < len(pixels) - 1: + f.write(', ') + if (i + 1) % 12 == 0: + f.write('\n ') + + f.write('\n};\n') + + print(f'Converted: {width}x{height} -> {len(pixels)} bytes') + print(f'Output: {output_path}') + +def main(): + parser = argparse.ArgumentParser( + description='PNG to RGB8888 script' + ) + parser.add_argument('input', help='Input image file (e.g. cat.png)') + parser.add_argument( + '-o', '--output', + help='Output header file (default: .h)' + ) + + args = parser.parse_args() + + output_path = args.output + if not output_path: + base, _ = os.path.splitext(args.input) + output_path = base + '.h' + + image_to_rgba8888_array(args.input, output_path) + +if __name__ == '__main__': + main() diff --git a/Source/rewrite-hoc-clk/build.sh b/Source/rewrite-hoc-clk/build.sh new file mode 100644 index 00000000..fbd37070 --- /dev/null +++ b/Source/rewrite-hoc-clk/build.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -e + +ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +DIST_DIR="$ROOT_DIR/dist" +CORES="$(nproc --all)" + +if [[ -n "$1" ]]; then + DIST_DIR="$1" +fi + +echo "DIST_DIR: $DIST_DIR" +echo "CORES: $CORES" + +echo "*** sysmodule ***" +TITLE_ID="$(grep -oP '"title_id":\s*"0x\K(\w+)' "$ROOT_DIR/sysmodule/perms.json")" + +pushd "$ROOT_DIR/sysmodule" +make -j$CORES +popd > /dev/null + +mkdir -p "$DIST_DIR/atmosphere/contents/$TITLE_ID/flags" +cp -vf "$ROOT_DIR/sysmodule/out/horizon-oc.nsp" "$DIST_DIR/atmosphere/contents/$TITLE_ID/exefs.nsp" +>"$DIST_DIR/atmosphere/contents/$TITLE_ID/flags/boot2.flag" +cp -vf "$ROOT_DIR/sysmodule/toolbox.json" "$DIST_DIR/atmosphere/contents/$TITLE_ID/toolbox.json" + +echo "*** overlay ***" +pushd "$ROOT_DIR/overlay" +make -j$CORES +popd > /dev/null + +mkdir -p "$DIST_DIR/switch/.overlays" +cp -vf "$ROOT_DIR/overlay/out/horizon-oc-overlay.ovl" "$DIST_DIR/switch/.overlays/horizon-oc-overlay.ovl" + +echo "*** assets ***" +mkdir -p "$DIST_DIR/config/horizon-oc" +cp -vf "$ROOT_DIR/config.ini.template" "$DIST_DIR/config/horizon-oc/config.ini.template" +cp -vf "$ROOT_DIR/../../README.md" "$DIST_DIR/README.md" + +echo "*** lang ***" + +cp -r "$ROOT_DIR/overlay/lang/" "$DIST_DIR/config/horizon-oc/lang/" diff --git a/Source/rewrite-hoc-clk/common/include/SaltyNX.h b/Source/rewrite-hoc-clk/common/include/SaltyNX.h new file mode 100644 index 00000000..06ebc50b --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/SaltyNX.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) MasaGratoR + * + * 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 . + * + */ + +#pragma once +#include "ipc.h" + +Handle saltysd_orig; + +Result SaltySD_Connect() { + for (int i = 0; i < 200; i++) { + if (!svcConnectToNamedPort(&saltysd_orig, "SaltySD")) + return 0; + svcSleepThread(1000*1000); + } + return 1; +} + +Result SaltySD_Term() +{ + Result ret; + IpcCommand c; + + ipcInitialize(&c); + ipcSendPid(&c); + + struct input + { + u64 magic; + u64 cmd_id; + u64 zero; + u64 reserved[2]; + } *raw; + + raw = (input*)ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->zero = 0; + + ret = ipcDispatch(saltysd_orig); + + if (R_SUCCEEDED(ret)) + { + IpcParsedCommand r; + ipcParse(&r); + + struct output { + u64 magic; + u64 result; + } *resp = (output*)r.Raw; + + ret = resp->result; + } + + // Session terminated works too. + svcCloseHandle(saltysd_orig); + if (ret == 0xf601) return 0; + + return ret; +} + +Result SaltySD_CheckIfSharedMemoryAvailable(ptrdiff_t *offset, u64 size) +{ + Result ret = 0; + + // Send a command + IpcCommand c; + ipcInitialize(&c); + ipcSendPid(&c); + + struct input { + u64 magic; + u64 cmd_id; + u64 size; + u32 reserved[2]; + } *raw; + + raw = (input*)ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 6; + raw->size = size; + + ret = ipcDispatch(saltysd_orig); + + if (R_SUCCEEDED(ret)) { + IpcParsedCommand r; + ipcParse(&r); + + struct output { + u64 magic; + u64 result; + u64 offset; + } *resp = (output*)r.Raw; + + ret = resp->result; + + if (!ret) + { + *offset = resp->offset; + } + } + + return ret; +} + +Result SaltySD_GetSharedMemoryHandle(Handle *retrieve) +{ + Result ret = 0; + + // Send a command + IpcCommand c; + ipcInitialize(&c); + ipcSendPid(&c); + + struct input { + u64 magic; + u64 cmd_id; + u32 reserved[4]; + } *raw; + + raw = (input*)ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 7; + + ret = ipcDispatch(saltysd_orig); + + if (R_SUCCEEDED(ret)) { + IpcParsedCommand r; + ipcParse(&r); + + struct output { + u64 magic; + u64 result; + u64 reserved[2]; + } *resp = (output*)r.Raw; + + ret = resp->result; + + if (!ret) + { + *retrieve = r.Handles[0]; + } + } + + return ret; +} + +Result SaltySD_GetDisplayRefreshRate(uint8_t* refreshRate) +{ + Result ret = 0; + + // Send a command + IpcCommand c; + ipcInitialize(&c); + ipcSendPid(&c); + + struct input { + u64 magic; + u64 cmd_id; + u64 zero; + u64 reserved; + } *raw; + + raw = (input*)ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 10; + raw->zero = 0; + + ret = ipcDispatch(saltysd_orig); + + if (R_SUCCEEDED(ret)) { + IpcParsedCommand r; + ipcParse(&r); + + struct output { + u64 magic; + u64 result; + u64 refreshRate; + u64 reserved; + } *resp = (output*)r.Raw; + + ret = resp->result; + + if (!ret) + { + *refreshRate = (uint8_t)(resp->refreshRate); + } + } + + return ret; +} + +Result SaltySD_SetDisplayRefreshRate(uint8_t refreshRate) +{ + Result ret = 0; + + // Send a command + IpcCommand c; + ipcInitialize(&c); + ipcSendPid(&c); + + struct input { + u64 magic; + u64 cmd_id; + u64 refreshRate; + u64 reserved; + } *raw; + + raw = (input*)ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 11; + raw->refreshRate = refreshRate; + + ret = ipcDispatch(saltysd_orig); + + if (R_SUCCEEDED(ret)) { + IpcParsedCommand r; + ipcParse(&r); + + struct output { + u64 magic; + u64 result; + u64 reserved[2]; + } *resp = (output*)r.Raw; + + ret = resp->result; + } + + return ret; +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/battery.h b/Source/rewrite-hoc-clk/common/include/battery.h new file mode 100644 index 00000000..dff0b08b --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/battery.h @@ -0,0 +1,252 @@ +/* + * 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 . + * + */ + +#pragma once +#include +#include +#include +typedef enum { + BatteryFlag_NoHub = BIT(0), // Hub is disconnected + BatteryFlag_Rail = BIT(8), // At least one Joy-con is charging from rail + BatteryFlag_SPDSRC = BIT(12), // OTG + BatteryFlag_ACC = BIT(16) // Accessory +} BatteryChargeFlags; + +typedef enum { + PDState_NewPDO = 1, // Received new Power Data Object + PDState_NoPD = 2, // No Power Delivery source is detected + PDState_AcceptedRDO = 3 // Received and accepted Request Data Object +} BatteryPDControllerState; + +// Charger type detection +typedef enum { + ChargerType_None = 0, + ChargerType_PD = 1, + ChargerType_TypeC_1500mA = 2, + ChargerType_TypeC_3000mA = 3, + ChargerType_DCP = 4, // Dedicated Charging Port + ChargerType_CDP = 5, // Charging Downstream Port + ChargerType_SDP = 6, // Standard Downstream Port + ChargerType_Apple_500mA = 7, + ChargerType_Apple_1000mA = 8, + ChargerType_Apple_2000mA = 9 +} BatteryChargerType; +typedef enum { + PowerRole_Sink = 1, // Device is receiving power + PowerRole_Source = 2 // Device is providing power +} BatteryPowerRole; + +typedef struct { + int32_t InputCurrentLimit; // Input (Sink) current limit in mA + int32_t VBUSCurrentLimit; // Output (Source/VBUS/OTG) current limit in mA + int32_t ChargeCurrentLimit; // Battery charging current limit in mA + int32_t ChargeVoltageLimit; // Battery charging voltage limit in mV + int32_t unk_x10; // Unknown field (possibly enum) + int32_t unk_x14; // Unknown field (possibly flags) + BatteryPDControllerState PDControllerState; // PD Controller State + int32_t BatteryTemperature; // Battery temperature in milli-Celsius + int32_t RawBatteryCharge; // Battery charge in percentmille + int32_t VoltageAvg; // Average voltage in mV + int32_t BatteryAge; // Battery health (capacity full/design) in pcm + BatteryPowerRole PowerRole; // Current power role + BatteryChargerType ChargerType; // Type of charger connected + int32_t ChargerVoltageLimit; // Charger voltage limit in mV + int32_t ChargerCurrentLimit; // Charger current limit in mA + BatteryChargeFlags Flags; // Various status flags +} BatteryChargeInfo; + +#define IS_BATTERY_CHARGING_ENABLED(info) (((info)->unk_x14 >> 8) & 1) + +Result batteryInfoInitialize(void); + +void batteryInfoExit(void); + +Result batteryInfoGetChargeInfo(BatteryChargeInfo *out); + +Result batteryInfoGetChargePercentage(u32 *out); + +Result batteryInfoIsEnoughPowerSupplied(bool *out); + +Result batteryInfoEnableCharging(void); +Result batteryInfoDisableCharging(void); +Result batteryInfoEnableFastCharging(void); +Result batteryInfoDisableFastCharging(void); + +const char* batteryInfoGetChargerTypeString(BatteryChargerType type); +const char* batteryInfoGetPowerRoleString(BatteryPowerRole role); +const char* batteryInfoGetPDStateString(BatteryPDControllerState state); + +static inline int batteryInfoGetTemperatureMiliCelsius(BatteryChargeInfo *info) { + return info->BatteryTemperature; +} + +static inline float batteryInfoGetChargePercent(BatteryChargeInfo *info) { + return (float)info->RawBatteryCharge / 1000.0f; +} + +static inline float batteryInfoGetBatteryHealthPercent(BatteryChargeInfo *info) { + return (float)info->BatteryAge / 1000.0f; +} + +static inline bool batteryInfoIsCharging(BatteryChargeInfo *info) { + return IS_BATTERY_CHARGING_ENABLED(info); +} + +static const char* s_chargerTypeStrings[] = { + "None", + "Power Delivery", + "USB-C @ 1.5A", + "USB-C @ 3.0A", + "USB-DCP", + "USB-CDP", + "USB-SDP", + "Apple @ 0.5A", + "Apple @ 1.0A", + "Apple @ 2.0A", +}; + +static const char* s_powerRoleStrings[] = { + "Unknown", + "Sink", + "Source", +}; + +static const char* s_pdStateStrings[] = { + "Unknown", + "New PDO Received", + "No PD Source", + "RDO Accepted" +}; + +// Internal PSM service handle +static Service g_psmService = {0}; +static bool g_batteryInfoInitialized = false; + +// Internal PSM command implementations +static Result psmGetBatteryChargeInfoFields(BatteryChargeInfo *out) { + if (!g_batteryInfoInitialized) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatchOut(&g_psmService, 17, *out); +} + +static Result psmEnableBatteryCharging_internal(void) { + if (!g_batteryInfoInitialized) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatch(&g_psmService, 2); +} + +static Result psmDisableBatteryCharging_internal(void) { + if (!g_batteryInfoInitialized) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatch(&g_psmService, 3); +} + +static Result psmEnableFastBatteryCharging_internal(void) { + if (!g_batteryInfoInitialized) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatch(&g_psmService, 10); +} + +static Result psmDisableFastBatteryCharging_internal(void) { + if (!g_batteryInfoInitialized) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return serviceDispatch(&g_psmService, 11); +} + +Result batteryInfoInitialize(void) { + if (g_batteryInfoInitialized) + return 0; + + Result rc = psmInitialize(); + if (R_SUCCEEDED(rc)) { + memcpy(&g_psmService, psmGetServiceSession(), sizeof(Service)); + g_batteryInfoInitialized = true; + } + + return rc; +} + +void batteryInfoExit(void) { + if (g_batteryInfoInitialized) { + psmExit(); + memset(&g_psmService, 0, sizeof(Service)); + g_batteryInfoInitialized = false; + } +} + +Result batteryInfoGetChargeInfo(BatteryChargeInfo *out) { + if (!out) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + return psmGetBatteryChargeInfoFields(out); +} + +Result batteryInfoGetChargePercentage(u32 *out) { + if (!g_batteryInfoInitialized) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return psmGetBatteryChargePercentage(out); +} + +Result batteryInfoIsEnoughPowerSupplied(bool *out) { + if (!g_batteryInfoInitialized) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + return psmIsEnoughPowerSupplied(out); +} + +Result batteryInfoEnableCharging(void) { + return psmEnableBatteryCharging_internal(); +} + +Result batteryInfoDisableCharging(void) { + return psmDisableBatteryCharging_internal(); +} + +Result batteryInfoEnableFastCharging(void) { + return psmEnableFastBatteryCharging_internal(); +} + +Result batteryInfoDisableFastCharging(void) { + return psmDisableFastBatteryCharging_internal(); +} + +const char* batteryInfoGetChargerTypeString(BatteryChargerType type) { + if (type < 0 || type > ChargerType_Apple_2000mA) + return "Unknown"; + + return s_chargerTypeStrings[type]; +} + +const char* batteryInfoGetPowerRoleString(BatteryPowerRole role) { + if (role < PowerRole_Sink || role > PowerRole_Source) + return s_powerRoleStrings[0]; + + return s_powerRoleStrings[role]; +} + +const char* batteryInfoGetPDStateString(BatteryPDControllerState state) { + if (state < PDState_NewPDO || state > PDState_AcceptedRDO) + return s_pdStateStrings[0]; + + return s_pdStateStrings[state]; +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/cpp_util.hpp b/Source/rewrite-hoc-clk/common/include/cpp_util.hpp new file mode 100644 index 00000000..2e367ede --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/cpp_util.hpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) meha3945 (hanai3bi) + * + * 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 . + * + */ + +#pragma once + +#include + +template +class ScopeGuard { +public: + ScopeGuard(F&& f) + : f(f), engaged(true) {}; + + ~ScopeGuard() { + if (engaged) + f(); + }; + + ScopeGuard(ScopeGuard&& rhs) + : f(std::move(rhs.f)) {}; + + void dismiss() { engaged = false; } + +private: + F f; + bool engaged; +}; + +struct MakeScopeExit { + template + ScopeGuard operator+=(F&& f) { + return ScopeGuard(std::move(f)); + }; +}; + +#define STRING_CAT2(x, y) x##y +#define STRING_CAT(x, y) STRING_CAT2(x, y) +#define SCOPE_GUARD MakeScopeExit() += [&]() __attribute__((always_inline)) +#define SCOPE_EXIT auto STRING_CAT(scope_exit_, __LINE__) = SCOPE_GUARD \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/crc32.h b/Source/rewrite-hoc-clk/common/include/crc32.h new file mode 100644 index 00000000..67539c7d --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/crc32.h @@ -0,0 +1,56 @@ +/* + * 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 . + * + */ + +#pragma once +#include +#include + +uint32_t crc32(const uint8_t *data, size_t length) { + uint32_t crc = 0xFFFFFFFF; + + for (size_t i = 0; i < length; i++) { + crc ^= data[i]; + for (int j = 0; j < 8; j++) { + crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1)); + } + } + return ~crc; +} + +uint32_t checksum_file(const char *filename) { + FILE *file = fopen(filename, "rb"); + if (!file) { + perror("[crc32] Error opening file"); + return 0; + } + + uint8_t buffer[1024]; + uint32_t crc = 0xFFFFFFFF; + size_t bytes_read; + + while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { + for (size_t i = 0; i < bytes_read; i++) { + crc ^= buffer[i]; + for (int j = 0; j < 8; j++) { + crc = (crc >> 1) ^ (0xEDB88320 & -(crc & 1)); + } + } + } + + fclose(file); + return ~crc; +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/display_refresh_rate.h b/Source/rewrite-hoc-clk/common/include/display_refresh_rate.h new file mode 100644 index 00000000..add49b3a --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/display_refresh_rate.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) Souldbminer, based on reasearch by MasaGratoR and Cooler3D + * + * 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 . + * + */ + +#pragma once + +#include +#include +#include + +typedef struct { + uint16_t hFrontPorch; + uint8_t hSyncWidth; + uint8_t hBackPorch; + uint8_t vFrontPorch; + uint8_t vSyncWidth; + uint8_t vBackPorch; + uint8_t VIC; + uint32_t pixelClock_kHz; +} DockedTimings; + +typedef struct { + uint8_t hSyncWidth; + uint16_t hFrontPorch; + uint8_t hBackPorch; + uint8_t vSyncWidth; + uint16_t vFrontPorch; + uint8_t vBackPorch; + uint32_t pixelClock_kHz; +} HandheldTimings; + +typedef struct { + uint8_t min; + uint8_t max; +} MinMaxRefreshRate; + +typedef struct { + uint32_t unk0; + uint32_t hActive; + uint32_t vActive; + uint32_t hSyncWidth; + uint32_t vSyncWidth; + uint32_t hFrontPorch; + uint32_t vFrontPorch; + uint32_t hBackPorch; + uint32_t vBackPorch; + uint32_t pclkKHz; + uint32_t bitsPerPixel; + uint32_t vmode; + uint32_t sync; + uint32_t unk1; + uint32_t reserved; +} NvdcMode2; + +typedef struct { + NvdcMode2 modes[201]; + uint32_t num_modes; +} NvdcModeDB2; + +typedef struct { + unsigned int PLLD_DIVM: 8; + unsigned int reserved_1: 3; + unsigned int PLLD_DIVN: 8; + unsigned int reserved_2: 1; + unsigned int PLLD_DIVP: 3; + unsigned int CSI_CLK_SRC: 1; + unsigned int reserved_3: 1; + unsigned int PLL_D: 1; + unsigned int reserved_4: 1; + unsigned int PLLD_LOCK: 1; + unsigned int reserved_5: 1; + unsigned int PLLD_REF_DIS: 1; + unsigned int PLLD_ENABLE: 1; + unsigned int PLLD_BYPASS: 1; +} PLLD_BASE; + +typedef struct { + signed int PLLD_SDM_DIN: 16; + unsigned int PLLD_EN_SDM: 1; + unsigned int PLLD_LOCK_OVERRIDE: 1; + unsigned int PLLD_EN_LCKDET: 1; + unsigned int PLLD_FREQLOCK: 1; + unsigned int PLLD_IDDQ: 1; + unsigned int PLLD_ENABLE_CLK: 1; + unsigned int PLLD_KVCO: 1; + unsigned int PLLD_KCP: 2; + unsigned int PLLD_PTS: 2; + unsigned int PLLD_LDPULSE_ADJ: 3; + unsigned int reserved: 2; +} PLLD_MISC; + +typedef struct { + uint64_t clkVirtAddr; + uint64_t dsiVirtAddr; + bool isDocked; + bool isLite; + bool isRetroSUPER; + bool isPossiblySpoofedRetro; + bool dontForce60InDocked; + bool matchLowestDocked; + bool displaySync; + bool displaySyncOutOfFocus60; + bool displaySyncDocked; + bool displaySyncDockedOutOfFocus60; +} DisplayRefreshConfig; +bool DisplayRefresh_Initialize(const DisplayRefreshConfig* config); +void DisplayRefresh_SetDockedState(bool isDocked); +bool DisplayRefresh_SetRate(uint32_t new_refreshRate); +bool DisplayRefresh_GetRate(uint32_t* out_refreshRate, bool internal); +uint8_t DisplayRefresh_GetDockedHighestAllowed(void); +void DisplayRefresh_CorrectOledGamma(uint32_t refresh_rate); +void DisplayRefresh_SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p); +void DisplayRefresh_Shutdown(void); diff --git a/Source/rewrite-hoc-clk/common/include/i2c.h b/Source/rewrite-hoc-clk/common/include/i2c.h new file mode 100644 index 00000000..feb7c9fb --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/i2c.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 KazushiMe + * Licensed under the GPLv2 + */ + +#pragma once + +#include + +// To use i2c service, sm and i2c should be intialized via smInitialize() and i2cInitialize(). + +Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val); + +Result I2cRead_OutU8(I2cDevice dev, u8 reg, u8 *out); +Result I2cRead_OutU16(I2cDevice dev, u8 reg, u16 *out); + +// Max17050 fuel gauge +float I2c_Max17050_GetBatteryCurrent(); + +const u8 MAX17050_CURRENT_REG = 0x0A; + +// Buck Converter +typedef enum I2c_BuckConverter_Reg { + I2c_Max77620_SD1VOLT_REG = 0x17, // Used for Erista DDR VDDQ+VDD2 / Mariko VDD2 + I2c_Max77621_VOLT_REG = 0x00, + I2c_Max77812_CPUVOLT_REG = 0x26, + I2c_Max77812_GPUVOLT_REG = 0x23, + I2c_Max77812_MEMVOLT_REG = 0x25, // Master 3 (GPU 1 + 2, DRAM 3, CPU 4), used for Mariko VDDQ +} I2c_BuckConverter_Reg; + +typedef struct I2c_BuckConverter_Domain { + I2cDevice device; + I2c_BuckConverter_Reg reg; + u8 volt_mask; + u32 uv_step; + u32 uv_min; + u32 uv_max; + u8 por_val; +} I2c_BuckConverter_Domain; + +const I2c_BuckConverter_Domain I2c_Erista_CPU = { I2cDevice_Max77621Cpu, I2c_Max77621_VOLT_REG, 0x7F, 6250, 606250, 1400000, }; +const I2c_BuckConverter_Domain I2c_Erista_GPU = { I2cDevice_Max77621Gpu, I2c_Max77621_VOLT_REG, 0x7F, 6250, 606250, 1400000, }; +const I2c_BuckConverter_Domain I2c_Erista_DRAM = { I2cDevice_Max77620Pmic, I2c_Max77620_SD1VOLT_REG, 0x7F, 12500, 600000, 1250000, }; +const I2c_BuckConverter_Domain I2c_Mariko_CPU = { I2cDevice_Max77812_2, I2c_Max77812_CPUVOLT_REG, 0xFF, 5000, 250000, 1525000, 0x78 }; +const I2c_BuckConverter_Domain I2c_Mariko_GPU = { I2cDevice_Max77812_2, I2c_Max77812_GPUVOLT_REG, 0xFF, 5000, 250000, 1525000, 0x78 }; +const I2c_BuckConverter_Domain I2c_Mariko_DRAM_VDDQ = { I2cDevice_Max77812_2, I2c_Max77812_MEMVOLT_REG, 0xFF, 5000, 250000, 700000, 0x78 }; +const I2c_BuckConverter_Domain I2c_Mariko_DRAM_VDD2 = { I2cDevice_Max77620Pmic, I2c_Max77620_SD1VOLT_REG, 0x7F, 12500, 600000, 1250000, }; + +u32 I2c_BuckConverter_GetMvOut(const I2c_BuckConverter_Domain* domain); +Result I2c_BuckConverter_SetMvOut(const I2c_BuckConverter_Domain* domain, u32 mvolt); + +// Bq24193 Battery management +u32 I2c_Bq24193_Convert_Raw_mA(u8 raw); +u8 I2c_Bq24193_Convert_mA_Raw(u32 ma); + +Result I2c_Bq24193_GetFastChargeCurrentLimit(u32 *ma); +Result I2c_Bq24193_SetFastChargeCurrentLimit(u32 ma); + +const u32 MA_RANGE_MIN = 512; +const u32 MA_RANGE_MAX = 4544; + +const u8 BQ24193_CHARGE_CURRENT_CONTROL_REG = 0x2; \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/ipc.h b/Source/rewrite-hoc-clk/common/include/ipc.h new file mode 100644 index 00000000..3015af32 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/ipc.h @@ -0,0 +1,756 @@ +/** + * @file ipc.h + * @brief Inter-process communication handling + * @author plutoo + * @copyright libnx Authors (ISC License) + */ +#pragma once +#include + +/// IPC input header magic +#define SFCI_MAGIC 0x49434653 +/// IPC output header magic +#define SFCO_MAGIC 0x4f434653 + +/// IPC invalid object ID +#define IPC_INVALID_OBJECT_ID UINT32_MAX + +///@name IPC request building +///@{ + +/// IPC command (request) structure. +#define IPC_MAX_BUFFERS 8 +#define IPC_MAX_OBJECTS 8 + +typedef enum { + BufferType_Normal=0, ///< Regular buffer. + BufferType_Type1=1, ///< Allows ProcessMemory and shared TransferMemory. + BufferType_Invalid=2, + BufferType_Type3=3 ///< Same as Type1 except remote process is not allowed to use device-mapping. +} BufferType; + +typedef enum { + BufferDirection_Send=0, + BufferDirection_Recv=1, + BufferDirection_Exch=2, +} BufferDirection; + +typedef enum { + IpcCommandType_Invalid = 0, + IpcCommandType_LegacyRequest = 1, + IpcCommandType_Close = 2, + IpcCommandType_LegacyControl = 3, + IpcCommandType_Request = 4, + IpcCommandType_Control = 5, + IpcCommandType_RequestWithContext = 6, + IpcCommandType_ControlWithContext = 7, +} IpcCommandType; + +typedef enum { + DomainMessageType_Invalid = 0, + DomainMessageType_SendMessage = 1, + DomainMessageType_Close = 2, +} DomainMessageType; + +/// IPC domain message header. +typedef struct { + u8 Type; + u8 NumObjectIds; + u16 Length; + u32 ThisObjectId; + u32 Pad[2]; +} DomainMessageHeader; + +/// IPC domain response header. +typedef struct { + u32 NumObjectIds; + u32 Pad[3]; +} DomainResponseHeader; + + +typedef struct { + size_t NumSend; // A + size_t NumRecv; // B + size_t NumExch; // W + const void* Buffers[IPC_MAX_BUFFERS]; + size_t BufferSizes[IPC_MAX_BUFFERS]; + BufferType BufferTypes[IPC_MAX_BUFFERS]; + + size_t NumStaticIn; // X + size_t NumStaticOut; // C + const void* Statics[IPC_MAX_BUFFERS]; + size_t StaticSizes[IPC_MAX_BUFFERS]; + u8 StaticIndices[IPC_MAX_BUFFERS]; + + bool SendPid; + size_t NumHandlesCopy; + size_t NumHandlesMove; + Handle Handles[IPC_MAX_OBJECTS]; + + size_t NumObjectIds; + u32 ObjectIds[IPC_MAX_OBJECTS]; +} IpcCommand; + +/** + * @brief Initializes an IPC command structure. + * @param cmd IPC command structure. + */ +static inline void ipcInitialize(IpcCommand* cmd) { + *cmd = (IpcCommand){}; +} + +/// IPC buffer descriptor. +typedef struct { + u32 Size; ///< Size of the buffer. + u32 Addr; ///< Lower 32-bits of the address of the buffer + u32 Packed; ///< Packed data (including higher bits of the address) +} IpcBufferDescriptor; + +/// IPC static send-buffer descriptor. +typedef struct { + u32 Packed; ///< Packed data (including higher bits of the address) + u32 Addr; ///< Lower 32-bits of the address +} IpcStaticSendDescriptor; + +/// IPC static receive-buffer descriptor. +typedef struct { + u32 Addr; ///< Lower 32-bits of the address of the buffer + u32 Packed; ///< Packed data (including higher bits of the address) +} IpcStaticRecvDescriptor; + +/** + * @brief Adds a buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddSendBuffer(IpcCommand* cmd, const void* buffer, size_t size, BufferType type) { + size_t off = cmd->NumSend; + cmd->Buffers[off] = buffer; + cmd->BufferSizes[off] = size; + cmd->BufferTypes[off] = type; + cmd->NumSend++; +} + +/** + * @brief Adds a receive-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddRecvBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) { + size_t off = cmd->NumSend + cmd->NumRecv; + cmd->Buffers[off] = buffer; + cmd->BufferSizes[off] = size; + cmd->BufferTypes[off] = type; + cmd->NumRecv++; +} + +/** + * @brief Adds an exchange-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param type Buffer type. + */ +static inline void ipcAddExchBuffer(IpcCommand* cmd, void* buffer, size_t size, BufferType type) { + size_t off = cmd->NumSend + cmd->NumRecv + cmd->NumExch; + cmd->Buffers[off] = buffer; + cmd->BufferSizes[off] = size; + cmd->BufferTypes[off] = type; + cmd->NumExch++; +} + +/** + * @brief Adds a static-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddSendStatic(IpcCommand* cmd, const void* buffer, size_t size, u8 index) { + size_t off = cmd->NumStaticIn; + cmd->Statics[off] = buffer; + cmd->StaticSizes[off] = size; + cmd->StaticIndices[off] = index; + cmd->NumStaticIn++; +} + +/** + * @brief Adds a static-receive-buffer to an IPC command structure. + * @param cmd IPC command structure. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddRecvStatic(IpcCommand* cmd, void* buffer, size_t size, u8 index) { + size_t off = cmd->NumStaticIn + cmd->NumStaticOut; + cmd->Statics[off] = buffer; + cmd->StaticSizes[off] = size; + cmd->StaticIndices[off] = index; + cmd->NumStaticOut++; +} + +/** + * @brief Adds a smart-buffer (buffer + static-buffer pair) to an IPC command structure. + * @param cmd IPC command structure. + * @param pointer_buffer_size Pointer buffer size. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddSendSmart(IpcCommand* cmd, size_t pointer_buffer_size, const void* buffer, size_t size, u8 index) { + if (pointer_buffer_size != 0 && size <= pointer_buffer_size) { + ipcAddSendBuffer(cmd, NULL, 0, BufferType_Normal); + ipcAddSendStatic(cmd, buffer, size, index); + } else { + ipcAddSendBuffer(cmd, buffer, size, BufferType_Normal); + ipcAddSendStatic(cmd, NULL, 0, index); + } +} + +/** + * @brief Adds a smart-receive-buffer (buffer + static-receive-buffer pair) to an IPC command structure. + * @param cmd IPC command structure. + * @param pointer_buffer_size Pointer buffer size. + * @param buffer Address of the buffer. + * @param size Size of the buffer. + * @param index Index of buffer. + */ +static inline void ipcAddRecvSmart(IpcCommand* cmd, size_t pointer_buffer_size, void* buffer, size_t size, u8 index) { + if (pointer_buffer_size != 0 && size <= pointer_buffer_size) { + ipcAddRecvBuffer(cmd, NULL, 0, BufferType_Normal); + ipcAddRecvStatic(cmd, buffer, size, index); + } else { + ipcAddRecvBuffer(cmd, buffer, size, BufferType_Normal); + ipcAddRecvStatic(cmd, NULL, 0, index); + } +} + +/** + * @brief Tags an IPC command structure to send the PID. + * @param cmd IPC command structure. + */ +static inline void ipcSendPid(IpcCommand* cmd) { + cmd->SendPid = true; +} + +/** + * @brief Adds a copy-handle to be sent through an IPC command structure. + * @param cmd IPC command structure. + * @param h Handle to send. + * @remark The receiving process gets a copy of the handle. + */ +static inline void ipcSendHandleCopy(IpcCommand* cmd, Handle h) { + cmd->Handles[cmd->NumHandlesCopy++] = h; +} + +/** + * @brief Adds a move-handle to be sent through an IPC command structure. + * @param cmd IPC command structure. + * @param h Handle to send. + * @remark The sending process loses ownership of the handle, which is transferred to the receiving process. + */ +static inline void ipcSendHandleMove(IpcCommand* cmd, Handle h) { + cmd->Handles[cmd->NumHandlesCopy + cmd->NumHandlesMove++] = h; +} + +/** + * @brief Prepares the header of an IPC command structure. + * @param cmd IPC command structure. + * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request + * @return Pointer to the raw embedded data structure in the request, ready to be filled out. + */ +static inline void* ipcPrepareHeader(IpcCommand* cmd, size_t sizeof_raw) { + u32* buf = (u32*)armGetTls(); + size_t i; + *buf++ = IpcCommandType_Request | (cmd->NumStaticIn << 16) | (cmd->NumSend << 20) | (cmd->NumRecv << 24) | (cmd->NumExch << 28); + + u32* fill_in_size_later = buf; + + if (cmd->NumStaticOut > 0) { + *buf = (cmd->NumStaticOut + 2) << 10; + } + else { + *buf = 0; + } + + if (cmd->SendPid || cmd->NumHandlesCopy > 0 || cmd->NumHandlesMove > 0) { + *buf++ |= 0x80000000; + *buf++ = (!!cmd->SendPid) | (cmd->NumHandlesCopy << 1) | (cmd->NumHandlesMove << 5); + + if (cmd->SendPid) + buf += 2; + + for (i=0; i<(cmd->NumHandlesCopy + cmd->NumHandlesMove); i++) + *buf++ = cmd->Handles[i]; + } + else { + buf++; + } + + for (i=0; iNumStaticIn; i++, buf+=2) { + IpcStaticSendDescriptor* desc = (IpcStaticSendDescriptor*) buf; + + uintptr_t ptr = (uintptr_t) cmd->Statics[i]; + desc->Addr = ptr; + desc->Packed = cmd->StaticIndices[i] | (cmd->StaticSizes[i] << 16) | + (((ptr >> 32) & 15) << 12) | (((ptr >> 36) & 15) << 6); + } + + for (i=0; i<(cmd->NumSend + cmd->NumRecv + cmd->NumExch); i++, buf+=3) { + IpcBufferDescriptor* desc = (IpcBufferDescriptor*) buf; + desc->Size = cmd->BufferSizes[i]; + + uintptr_t ptr = (uintptr_t) cmd->Buffers[i]; + desc->Addr = ptr; + desc->Packed = cmd->BufferTypes[i] | + (((ptr >> 32) & 15) << 28) | ((ptr >> 36) << 2); + } + + u32 padding = ((16 - (((uintptr_t) buf) & 15)) & 15) / 4; + u32* raw = (u32*) (buf + padding); + + size_t raw_size = (sizeof_raw/4) + 4; + buf += raw_size; + + u16* buf_u16 = (u16*) buf; + + for (i=0; iNumStaticOut; i++) { + size_t off = cmd->NumStaticIn + i; + size_t sz = (uintptr_t) cmd->StaticSizes[off]; + + buf_u16[i] = (sz > 0xFFFF) ? 0 : sz; + } + + size_t u16s_size = ((2*cmd->NumStaticOut) + 3)/4; + buf += u16s_size; + raw_size += u16s_size; + + *fill_in_size_later |= raw_size; + + for (i=0; iNumStaticOut; i++, buf+=2) { + IpcStaticRecvDescriptor* desc = (IpcStaticRecvDescriptor*) buf; + size_t off = cmd->NumStaticIn + i; + + uintptr_t ptr = (uintptr_t) cmd->Statics[off]; + desc->Addr = ptr; + desc->Packed = (ptr >> 32) | (cmd->StaticSizes[off] << 16); + } + + return (void*) raw; +} + +/** + * @brief Dispatches an IPC request. + * @param session IPC session handle. + * @return Result code. + */ +static inline Result ipcDispatch(Handle session) { + return svcSendSyncRequest(session); +} + +///@} + +///@name IPC response parsing +///@{ + +/// IPC parsed command (response) structure. +typedef struct { + IpcCommandType CommandType; ///< Type of the command + + bool HasPid; ///< true if the 'Pid' field is filled out. + u64 Pid; ///< PID included in the response (only if HasPid is true) + + size_t NumHandles; ///< Number of handles copied. + Handle Handles[IPC_MAX_OBJECTS]; ///< Handles. + bool WasHandleCopied[IPC_MAX_OBJECTS]; ///< true if the handle was moved, false if it was copied. + + bool IsDomainRequest; ///< true if the the message is a Domain message. + DomainMessageType InMessageType; ///< Type of the domain message. + u32 InMessageLength; ///< Size of rawdata (for domain messages). + u32 InThisObjectId; ///< Object ID to call the command on (for domain messages). + size_t InNumObjectIds; ///< Number of object IDs (for domain messages). + u32 InObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain messages). + + bool IsDomainResponse; ///< true if the the message is a Domain response. + size_t OutNumObjectIds; ///< Number of object IDs (for domain responses). + u32 OutObjectIds[IPC_MAX_OBJECTS]; ///< Object IDs (for domain responses). + + size_t NumBuffers; ///< Number of buffers in the response. + void* Buffers[IPC_MAX_BUFFERS]; ///< Pointers to the buffers. + size_t BufferSizes[IPC_MAX_BUFFERS]; ///< Sizes of the buffers. + BufferType BufferTypes[IPC_MAX_BUFFERS]; ///< Types of the buffers. + BufferDirection BufferDirections[IPC_MAX_BUFFERS]; ///< Direction of each buffer. + + size_t NumStatics; ///< Number of statics in the response. + void* Statics[IPC_MAX_BUFFERS]; ///< Pointers to the statics. + size_t StaticSizes[IPC_MAX_BUFFERS]; ///< Sizes of the statics. + u8 StaticIndices[IPC_MAX_BUFFERS]; ///< Indices of the statics. + + size_t NumStaticsOut; ///< Number of output statics available in the response. + + void* Raw; ///< Pointer to the raw embedded data structure in the response. + void* RawWithoutPadding; ///< Pointer to the raw embedded data structure, without padding. + size_t RawSize; ///< Size of the raw embedded data. +} IpcParsedCommand; + +/** + * @brief Parse an IPC command response into an IPC parsed command structure. + * @param r IPC parsed command structure to fill in. + * @return Result code. + */ +static inline Result ipcParse(IpcParsedCommand* r) { + u32* buf = (u32*)armGetTls(); + u32 ctrl0 = *buf++; + u32 ctrl1 = *buf++; + size_t i; + + r->IsDomainRequest = false; + r->IsDomainResponse = false; + + r->CommandType = (IpcCommandType) (ctrl0 & 0xffff); + r->HasPid = false; + r->RawSize = (ctrl1 & 0x1ff) * 4; + r->NumHandles = 0; + + r->NumStaticsOut = (ctrl1 >> 10) & 15; + if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 2 -> Single descriptor + if (r->NumStaticsOut >> 1) r->NumStaticsOut--; // Value 3+ -> (Value - 2) descriptors + + if (ctrl1 & 0x80000000) { + u32 ctrl2 = *buf++; + + if (ctrl2 & 1) { + r->HasPid = true; + r->Pid = *buf++; + r->Pid |= ((u64)(*buf++)) << 32; + } + + size_t num_handles_copy = ((ctrl2 >> 1) & 15); + size_t num_handles_move = ((ctrl2 >> 5) & 15); + + size_t num_handles = num_handles_copy + num_handles_move; + u32* buf_after_handles = buf + num_handles; + + if (num_handles > IPC_MAX_OBJECTS) + num_handles = IPC_MAX_OBJECTS; + + for (i=0; iHandles[i] = *(buf+i); + r->WasHandleCopied[i] = (i < num_handles_copy); + } + + r->NumHandles = num_handles; + buf = buf_after_handles; + } + + size_t num_statics = (ctrl0 >> 16) & 15; + u32* buf_after_statics = buf + num_statics*2; + + if (num_statics > IPC_MAX_BUFFERS) + num_statics = IPC_MAX_BUFFERS; + + for (i=0; iPacked; + + r->Statics[i] = (void*) (desc->Addr | (((packed >> 12) & 15) << 32) | (((packed >> 6) & 15) << 36)); + r->StaticSizes[i] = packed >> 16; + r->StaticIndices[i] = packed & 63; + } + + r->NumStatics = num_statics; + buf = buf_after_statics; + + size_t num_bufs_send = (ctrl0 >> 20) & 15; + size_t num_bufs_recv = (ctrl0 >> 24) & 15; + size_t num_bufs_exch = (ctrl0 >> 28) & 15; + + size_t num_bufs = num_bufs_send + num_bufs_recv + num_bufs_exch; + r->Raw = (void*)(((uintptr_t)(buf + num_bufs*3) + 15) &~ 15); + r->RawWithoutPadding = (void*)((uintptr_t)(buf + num_bufs*3)); + + if (num_bufs > IPC_MAX_BUFFERS) + num_bufs = IPC_MAX_BUFFERS; + + for (i=0; iPacked; + + r->Buffers[i] = (void*) (desc->Addr | ((packed >> 28) << 32) | (((packed >> 2) & 15) << 36)); + r->BufferSizes[i] = desc->Size; + r->BufferTypes[i] = (BufferType) (packed & 3); + + if (i < num_bufs_send) + r->BufferDirections[i] = BufferDirection_Send; + else if (i < (num_bufs_send + num_bufs_recv)) + r->BufferDirections[i] = BufferDirection_Recv; + else + r->BufferDirections[i] = BufferDirection_Exch; + } + + r->NumBuffers = num_bufs; + return 0; +} + +/** + * @brief Queries the size of an IPC pointer buffer. + * @param session IPC session handle. + * @param size Output variable in which to store the size. + * @return Result code. + */ +static inline Result ipcQueryPointerBufferSize(Handle session, size_t *size) { + u32* buf = (u32*)armGetTls(); + + buf[0] = IpcCommandType_Control; + buf[1] = 8; + buf[2] = 0; + buf[3] = 0; + buf[4] = SFCI_MAGIC; + buf[5] = 0; + buf[6] = 3; + buf[7] = 0; + + Result rc = ipcDispatch(session); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct ipcQueryPointerBufferSizeResponse { + u64 magic; + u64 result; + u32 size; + } *raw = (struct ipcQueryPointerBufferSizeResponse*)r.Raw; + + rc = raw->result; + + if (R_SUCCEEDED(rc)) { + *size = raw->size & 0xffff; + } + } + + return rc; +} + +/** + * @brief Closes the IPC session with proper clean up. + * @param session IPC session handle. + * @return Result code. + */ +static inline Result ipcCloseSession(Handle session) { + u32* buf = (u32*)armGetTls(); + buf[0] = IpcCommandType_Close; + buf[1] = 0; + return ipcDispatch(session); +} + +/** + * @brief Clones an IPC session. + * @param session IPC session handle. + * @param unk Unknown. + * @param new_session_out Output cloned IPC session handle. + * @return Result code. + */ +static inline Result ipcCloneSession(Handle session, u32 unk, Handle* new_session_out) { + u32* buf = (u32*)armGetTls(); + + buf[0] = IpcCommandType_Control; + buf[1] = 9; + buf[2] = 0; + buf[3] = 0; + buf[4] = SFCI_MAGIC; + buf[5] = 0; + buf[6] = 4; + buf[7] = 0; + buf[8] = unk; + + Result rc = ipcDispatch(session); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct ipcCloneSessionResponse { + u64 magic; + u64 result; + } *raw = (struct ipcCloneSessionResponse*)r.Raw; + + rc = raw->result; + + if (R_SUCCEEDED(rc) && new_session_out) { + *new_session_out = r.Handles[0]; + } + } + + return rc; +} + +///@} + +///@name IPC domain handling +///@{ + +/** + * @brief Converts an IPC session handle into a domain. + * @param session IPC session handle. + * @param object_id_out Output variable in which to store the object ID. + * @return Result code. + */ +static inline Result ipcConvertSessionToDomain(Handle session, u32* object_id_out) { + u32* buf = (u32*)armGetTls(); + + buf[0] = IpcCommandType_Control; + buf[1] = 8; + buf[4] = SFCI_MAGIC; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + + Result rc = ipcDispatch(session); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + ipcParse(&r); + + struct ipcConvertSessionToDomainResponse { + u64 magic; + u64 result; + u32 object_id; + } *raw = (struct ipcConvertSessionToDomainResponse*)r.Raw; + + rc = raw->result; + + if (R_SUCCEEDED(rc)) { + *object_id_out = raw->object_id; + } + } + + return rc; +} + +/** + * @brief Adds an object ID to be sent through an IPC domain command structure. + * @param cmd IPC domain command structure. + * @param object_id Object ID to send. + */ +static inline void ipcSendObjectId(IpcCommand* cmd, u32 object_id) { + cmd->ObjectIds[cmd->NumObjectIds++] = object_id; +} + +/** + * @brief Prepares the header of an IPC command structure (domain version). + * @param cmd IPC command structure. + * @param sizeof_raw Size in bytes of the raw data structure to embed inside the IPC request + * @param object_id Domain object ID. + * @return Pointer to the raw embedded data structure in the request, ready to be filled out. + */ +static inline void* ipcPrepareHeaderForDomain(IpcCommand* cmd, size_t sizeof_raw, u32 object_id) { + void* raw = ipcPrepareHeader(cmd, sizeof_raw + sizeof(DomainMessageHeader) + cmd->NumObjectIds*sizeof(u32)); + DomainMessageHeader* hdr = (DomainMessageHeader*) raw; + u32 *object_ids = (u32*)(((uintptr_t) raw) + sizeof(DomainMessageHeader) + sizeof_raw); + + hdr->Type = DomainMessageType_SendMessage; + hdr->NumObjectIds = (u8)cmd->NumObjectIds; + hdr->Length = sizeof_raw; + hdr->ThisObjectId = object_id; + hdr->Pad[0] = hdr->Pad[1] = 0; + + for(size_t i = 0; i < cmd->NumObjectIds; i++) + object_ids[i] = cmd->ObjectIds[i]; + return (void*)(((uintptr_t) raw) + sizeof(DomainMessageHeader)); +} + +/** + * @brief Parse an IPC command request into an IPC parsed command structure (domain version). + * @param r IPC parsed command structure to fill in. + * @return Result code. + */ +static inline Result ipcParseDomainRequest(IpcParsedCommand* r) { + Result rc = ipcParse(r); + DomainMessageHeader *hdr; + u32 *object_ids; + if(R_FAILED(rc)) + return rc; + + hdr = (DomainMessageHeader*) r->Raw; + object_ids = (u32*)(((uintptr_t) hdr) + sizeof(DomainMessageHeader) + hdr->Length); + r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainMessageHeader)); + + r->IsDomainRequest = true; + r->InMessageType = (DomainMessageType)(hdr->Type); + switch (r->InMessageType) { + case DomainMessageType_SendMessage: + case DomainMessageType_Close: + break; + default: + return MAKERESULT(Module_Libnx, LibnxError_DomainMessageUnknownType); + } + + r->InThisObjectId = hdr->ThisObjectId; + r->InNumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds; + if ((uintptr_t)object_ids + sizeof(u32) * r->InNumObjectIds - (uintptr_t)armGetTls() >= 0x100) { + return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds); + } + for(size_t i = 0; i < r->InNumObjectIds; i++) + r->InObjectIds[i] = object_ids[i]; + + return rc; +} + +/** + * @brief Parse an IPC command response into an IPC parsed command structure (domain version). + * @param r IPC parsed command structure to fill in. + * @param sizeof_raw Size in bytes of the raw data structure. + * @return Result code. + */ +static inline Result ipcParseDomainResponse(IpcParsedCommand* r, size_t sizeof_raw) { + Result rc = ipcParse(r); + DomainResponseHeader *hdr; + u32 *object_ids; + if(R_FAILED(rc)) + return rc; + + hdr = (DomainResponseHeader*) r->Raw; + r->Raw = (void*)(((uintptr_t) r->Raw) + sizeof(DomainResponseHeader)); + object_ids = (u32*)(((uintptr_t) r->Raw) + sizeof_raw);//Official sw doesn't align this. + + r->IsDomainResponse = true; + + r->OutNumObjectIds = hdr->NumObjectIds > 8 ? 8 : hdr->NumObjectIds; + if ((uintptr_t)object_ids + sizeof(u32) * r->OutNumObjectIds - (uintptr_t)armGetTls() >= 0x100) { + return MAKERESULT(Module_Libnx, LibnxError_DomainMessageTooManyObjectIds); + } + for(size_t i = 0; i < r->OutNumObjectIds; i++) + r->OutObjectIds[i] = object_ids[i]; + + return rc; +} + +/** + * @brief Closes a domain object by ID. + * @param session IPC session handle. + * @param object_id ID of the object to close. + * @return Result code. + */ +static inline Result ipcCloseObjectById(Handle session, u32 object_id) { + IpcCommand c; + DomainMessageHeader* hdr; + + ipcInitialize(&c); + hdr = (DomainMessageHeader*)ipcPrepareHeader(&c, sizeof(DomainMessageHeader)); + + hdr->Type = DomainMessageType_Close; + hdr->NumObjectIds = 0; + hdr->Length = 0; + hdr->ThisObjectId = object_id; + hdr->Pad[0] = hdr->Pad[1] = 0; + + return ipcDispatch(session); // this command has no associated response +} + +///@} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/memmem.h b/Source/rewrite-hoc-clk/common/include/memmem.h new file mode 100644 index 00000000..0f791c1b --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/memmem.h @@ -0,0 +1,41 @@ +/* + MIT License + + Copyright (c) 2024 Roy Merkel + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#ifndef MEMMEM_IMPL_H +#define MEMMEM_IMPL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void *memmem_impl(const void *haystack, size_t haystacklen, + const void *needle, size_t needlelen); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Source/rewrite-hoc-clk/common/include/notification.h b/Source/rewrite-hoc-clk/common/include/notification.h new file mode 100644 index 00000000..a6389b6f --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/notification.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) ppkantorski (bord2death) + * + * 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 . + * + */ + +#pragma once + +#include +#include +#include + +static void writeNotification(const std::string& message) { + static const char* flagPath = "sdmc:/config/ultrahand/flags/NOTIFICATIONS.flag"; + + FILE* flagFile = fopen(flagPath, "r"); + if (!flagFile) { + return; + } + fclose(flagFile); + + std::string filename = "Horizon OC -" + std::to_string(std::time(nullptr)) + ".notify"; + std::string fullPath = "sdmc:/config/ultrahand/notifications/" + filename; + + FILE* file = fopen(fullPath.c_str(), "w"); + if (file) { + fprintf(file, "{\n"); + fprintf(file, " \"text\": \"%s\",\n", message.c_str()); + fprintf(file, " \"fontSize\": 28\n"); + fprintf(file, "}\n"); + fclose(file); + } + } diff --git a/Source/rewrite-hoc-clk/common/include/pcv_types.h b/Source/rewrite-hoc-clk/common/include/pcv_types.h new file mode 100644 index 00000000..03899f35 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/pcv_types.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) ppkantorski (bord2death) + * + * 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 . + * + */ + +#pragma once + +/* + * SDx actual min is 625 mV. Multipliers 0/1 reserved. + * SD0 max is 1400 mV + * SD1 max is 1550 mV + * SD2 max is 3787.5 mV + * SD3 max is 3787.5 mV + */ + +/* +* Switch Power domains (max77620): +* Name | Usage | uV step | uV min | uV default | uV max | Init +*-------+---------------+---------+--------+------------+---------+------------------ +* sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) +* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) +* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) +* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | +* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) +* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | +* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv) +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | 0.85V (AO, pcv) +* ldo5 | GC Card | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv) +* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V (pcv) +* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo8 | XUSB, DP, MCU | 50000 | 800000 | 1050000 | 2800000 | 1.05V/2.8V (pcv) +*/ + + +// GPIOs T210: 3: 3.3V, 5: CPU PMIC, 6: GPU PMIC, 7: DSI/VI 1.2V powered by ldo0. + +/* + * OTP: T210 - T210B01: + * SD0: 1.0V 1.05V - SoC. EN Based on FPSSRC. + * SD1: 1.15V 1.1V - DRAM for T210. EN Based on FPSSRC. + * SD2: 1.35V 1.35V + * SD3: 1.8V 1.8V + * All powered off? + * LDO0: -- -- - Display + * LDO1: 1.05V 1.05V + * LDO2: -- -- - SD + * LDO3: 3.1V 3.1V - GC ASIC + * LDO4: 1.0V 0.8V - Needed for RTC domain on T210. + * LDO5: 3.1V 3.1V + * LDO6: 2.8V 2.9V - Touch. + * LDO7: 1.05V 1.0V + * LDO8: 1.05V 1.0V + */ + +/* +* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode +* MAX77620_REG_GPIOx: 0x9 sets output and enable +*/ + +typedef enum { + PcvPowerDomain_Max77620_Sd0 = 0, + PcvPowerDomain_Max77620_Sd1 = 1, + PcvPowerDomain_Max77620_Sd2 = 2, + PcvPowerDomain_Max77620_Sd3 = 3, + PcvPowerDomain_Max77620_Ldo0 = 4, + PcvPowerDomain_Max77620_Ldo1 = 5, + PcvPowerDomain_Max77620_Ldo2 = 6, + PcvPowerDomain_Max77620_Ldo3 = 7, + PcvPowerDomain_Max77620_Ldo4 = 8, + PcvPowerDomain_Max77620_Ldo5 = 9, + PcvPowerDomain_Max77620_Ldo6 = 10, + PcvPowerDomain_Max77620_Ldo7 = 11, + PcvPowerDomain_Max77620_Ldo8 = 12, + PcvPowerDomain_Max77621_Cpu = 13, + PcvPowerDomain_Max77621_Gpu = 14, + PcvPowerDomain_Max77812_Cpu = 15, + PcvPowerDomain_Max77812_Gpu = 16, + PcvPowerDomain_Max77812_Dram = 17, +} PowerDomain; + +typedef enum { + PcvPowerDomainId_Max77620_Sd0 = 0x3A000080, + PcvPowerDomainId_Max77620_Sd1 = 0x3A000081, // vdd2 + PcvPowerDomainId_Max77620_Sd2 = 0x3A000082, + PcvPowerDomainId_Max77620_Sd3 = 0x3A000083, + PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0, + PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1, + PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2, + PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3, + PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4, + PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5, + PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6, + PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7, + PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8, + PcvPowerDomainId_Max77621_Cpu = 0x3A000003, + PcvPowerDomainId_Max77621_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Cpu = 0x3A000003, + PcvPowerDomainId_Max77812_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Dram = 0x3A000005, // vddq +} PowerDomainId; \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/pwm.h b/Source/rewrite-hoc-clk/common/include/pwm.h new file mode 100644 index 00000000..ccc24e02 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/pwm.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) MasaGratoR + * + * 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 . + * + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + Service s; +} PwmChannelSession; + +Result pwmInitialize(void); +void pwmExit(void); +Service* pwmGetServiceSession(void); +Result pwmOpenSession2(PwmChannelSession *out, u32 device_code); +Result pwmChannelSessionGetDutyCycle(PwmChannelSession *c, double* out); +void pwmChannelSessionClose(PwmChannelSession *c); + +#ifdef __cplusplus +} // extern "C" +#endif \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/registers.h b/Source/rewrite-hoc-clk/common/include/registers.h new file mode 100644 index 00000000..82977371 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/registers.h @@ -0,0 +1,527 @@ +/* + * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors + * + * Copyright (c) Linux 4 Tegra & Linux 4 Switch 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 . + * + */ + +#pragma once + +#define EMC_INTSTATUS_0 0x0 +#define EMC_INTMASK_0 0x4 +#define EMC_DBG_0 0x8 +#define EMC_CFG_0 0xC +#define EMC_ADR_CFG_0 0x10 +#define EMC_REFCTRL_0 0x20 +#define EMC_PIN_0 0x24 +#define EMC_TIMING_CONTROL_0 0x28 +#define EMC_RC_0 0x2C +#define EMC_RFC_0 0x30 +#define EMC_RAS_0 0x34 +#define EMC_RP_0 0x38 +#define EMC_R2W_0 0x3C +#define EMC_W2R_0 0x40 +#define EMC_R2P_0 0x44 +#define EMC_W2P_0 0x48 +#define EMC_RD_RCD_0 0x4C +#define EMC_WR_RCD_0 0x50 +#define EMC_RRD_0 0x54 +#define EMC_REXT_0 0x58 +#define EMC_WDV_0 0x5C +#define EMC_QUSE_0 0x60 +#define EMC_QRST_0 0x64 +#define EMC_QSAFE_0 0x68 +#define EMC_RDV_0 0x6C +#define EMC_REFRESH_0 0x70 +#define EMC_BURST_REFRESH_NUM_0 0x74 +#define EMC_PDEX2WR_0 0x78 +#define EMC_PDEX2RD_0 0x7C +#define EMC_PCHG2PDEN_0 0x80 +#define EMC_ACT2PDEN_0 0x84 +#define EMC_AR2PDEN_0 0x88 +#define EMC_RW2PDEN_0 0x8C +#define EMC_TXSR_0 0x90 +#define EMC_TCKE_0 0x94 +#define EMC_TFAW_0 0x98 +#define EMC_TRPAB_0 0x9C +#define EMC_TCLKSTABLE_0 0xA0 +#define EMC_TCLKSTOP_0 0xA4 +#define EMC_TREFBW_0 0xA8 +#define EMC_TPPD_0 0xAC +#define EMC_ODT_WRITE_0 0xB0 +#define EMC_PDEX2MRR_0 0xB4 +#define EMC_WEXT_0 0xB8 +#define EMC_RFC_SLR_0 0xC0 +#define EMC_MRS_WAIT_CNT2_0 0xC4 +#define EMC_MRS_WAIT_CNT_0 0xC8 +#define EMC_MRS_0 0xCC +#define EMC_EMRS_0 0xD0 +#define EMC_REF_0 0xD4 +#define EMC_PRE_0 0xD8 +#define EMC_NOP_0 0xDC +#define EMC_SELF_REF_0 0xE0 +#define EMC_DPD_0 0xE4 +#define EMC_MRW_0 0xE8 +#define EMC_MRR_0 0xEC +#define EMC_CMDQ_0 0xF0 +#define EMC_MC2EMCQ_0 0xF4 +#define EMC_FBIO_SPARE_0 0x100 +#define EMC_FBIO_CFG5_0 0x104 +#define EMC_FBIO_CFG6_0 0x114 +#define EMC_PDEX2CKE_0 0x118 +#define EMC_CKE2PDEN_0 0x11C +#define EMC_CFG_RSV_0 0x120 +#define EMC_ACPD_CONTROL_0 0x124 +#define EMC_MPC_0 0x128 +#define EMC_EMRS2_0 0x12C +#define EMC_EMRS3_0 0x130 +#define EMC_MRW2_0 0x134 +#define EMC_MRW3_0 0x138 +#define EMC_MRW4_0 0x13C +#define EMC_CLKEN_OVERRIDE_0 0x140 +#define EMC_R2R_0 0x144 +#define EMC_W2W_0 0x148 +#define EMC_EINPUT_0 0x14C +#define EMC_EINPUT_DURATION_0 0x150 +#define EMC_PUTERM_EXTRA_0 0x154 +#define EMC_TCKESR_0 0x158 +#define EMC_TPD_0 0x15C +#define EMC_AUTO_CAL_CONFIG_0 0x2A4 +#define EMC_AUTO_CAL_INTERVAL_0 0x2A8 +#define EMC_AUTO_CAL_STATUS_0 0x2AC +#define EMC_REQ_CTRL_0 0x2B0 +#define EMC_EMC_STATUS_0 0x2B4 +#define EMC_CFG_2_0 0x2B8 +#define EMC_CFG_DIG_DLL_0 0x2BC +#define EMC_CFG_DIG_DLL_PERIOD_0 0x2C0 +#define EMC_DIG_DLL_STATUS_0 0x2C4 +#define EMC_CFG_DIG_DLL_1_0 0x2C8 +#define EMC_RDV_MASK_0 0x2CC +#define EMC_WDV_MASK_0 0x2D0 +#define EMC_RDV_EARLY_MASK_0 0x2D4 +#define EMC_RDV_EARLY_0 0x2D8 +#define EMC_AUTO_CAL_CONFIG8_0 0x2DC +#define EMC_ZCAL_INTERVAL_0 0x2E0 +#define EMC_ZCAL_WAIT_CNT_0 0x2E4 +#define EMC_ZCAL_MRW_CMD_0 0x2E8 +#define EMC_ZQ_CAL_0 0x2EC +#define EMC_XM2COMPPADCTRL3_0 0x2F4 +#define EMC_AUTO_CAL_VREF_SEL_0_0 0x2F8 +#define EMC_AUTO_CAL_VREF_SEL_1_0 0x300 +#define EMC_XM2COMPPADCTRL_0 0x30C +#define EMC_FDPD_CTRL_DQ_0 0x310 +#define EMC_FDPD_CTRL_CMD_0 0x314 +#define EMC_PMACRO_CMD_BRICK_CTRL_FDPD_0 0x318 +#define EMC_PMACRO_DATA_BRICK_CTRL_FDPD_0 0x31C +#define EMC_SCRATCH0_0 0x324 +#define EMC_PMACRO_BRICK_CTRL_RFU1_0 0x330 +#define EMC_PMACRO_BRICK_CTRL_RFU2_0 0x334 +#define EMC_CMD_MAPPING_CMD0_0_0 0x380 +#define EMC_CMD_MAPPING_CMD0_1_0 0x384 +#define EMC_CMD_MAPPING_CMD0_2_0 0x388 +#define EMC_CMD_MAPPING_CMD1_0_0 0x38C +#define EMC_CMD_MAPPING_CMD1_1_0 0x390 +#define EMC_CMD_MAPPING_CMD1_2_0 0x394 +#define EMC_CMD_MAPPING_CMD2_0_0 0x398 +#define EMC_CMD_MAPPING_CMD2_1_0 0x39C +#define EMC_CMD_MAPPING_CMD2_2_0 0x3A0 +#define EMC_CMD_MAPPING_CMD3_0_0 0x3A4 +#define EMC_CMD_MAPPING_CMD3_1_0 0x3A8 +#define EMC_CMD_MAPPING_CMD3_2_0 0x3AC +#define EMC_CMD_MAPPING_BYTE_0 0x3B0 +#define EMC_TR_TIMING_0_0 0x3B4 +#define EMC_TR_CTRL_0_0 0x3B8 +#define EMC_TR_CTRL_1_0 0x3BC +#define EMC_SWITCH_BACK_CTRL_0 0x3C0 +#define EMC_TR_RDV_0 0x3C4 +#define EMC_STALL_THEN_EXE_BEFORE_CLKCHANGE_0 0x3C8 +#define EMC_STALL_THEN_EXE_AFTER_CLKCHANGE_0 0x3CC +#define EMC_UNSTALL_RW_AFTER_CLKCHANGE_0 0x3D0 +#define EMC_AUTO_CAL_ 0x3D4 +#define EMC_SEL_DPD_CTRL_0 0x3D8 +#define EMC_PRE_REFRESH_REQ_CNT_0 0x3DC +#define EMC_DYN_SELF_REF_CONTROL_0 0x3E0 +#define EMC_TXSRDLL_0 0x3E4 +#define EMC_CCFIFO_ADDR_0 0x3E8 +#define EMC_CCFIFO_DATA_0 0x3EC +#define EMC_CCFIFO_STATUS_0 0x3F0 +#define EMC_TR_QPOP_0 0x3F4 +#define EMC_TR_RDV_MASK_0 0x3F8 +#define EMC_TR_QSAFE_0 0x3FC +#define EMC_TR_QRST_0 0x400 +#define EMC_SWIZZLE_RANK0_BYTE0_0 0x404 +#define EMC_SWIZZLE_RANK0_BYTE1_0 0x408 +#define EMC_SWIZZLE_RANK0_BYTE2_0 0x40C +#define EMC_SWIZZLE_RANK0_BYTE3_0 0x410 +#define EMC_SWIZZLE_RANK1_BYTE0_0 0x418 +#define EMC_SWIZZLE_RANK1_BYTE1_0 0x41C +#define EMC_SWIZZLE_RANK1_BYTE2_0 0x420 +#define EMC_SWIZZLE_RANK1_BYTE3_0 0x424 +#define EMC_ISSUE_QRST_0 0x428 +#define EMC_PMC_SCRATCH1_0 0x440 +#define EMC_PMC_SCRATCH2_0 0x444 +#define EMC_PMC_SCRATCH3_0 0x448 +#define EMC_AUTO_CAL_CONFIG2_0 0x458 +#define EMC_AUTO_CAL_CONFIG3_0 0x45C +#define EMC_TR_DVFS_0 0x460 +#define EMC_AUTO_CAL_CHANNEL_0 0x464 +#define EMC_IBDLY_0 0x468 +#define EMC_OBDLY_0 0x46C +#define EMC_TXDSRVTTGEN_0 0x480 +#define EMC_WE_DURATION_0 0x48C +#define EMC_WS_DURATION_0 0x490 +#define EMC_WEV_0 0x494 +#define EMC_WSV_0 0x498 +#define EMC_CFG_3_0 0x49C +#define EMC_MRW5_0 0x4A0 +#define EMC_MRW6_0 0x4A4 +#define EMC_MRW7_0 0x4A8 +#define EMC_MRW8_0 0x4AC +#define EMC_MRW9_0 0x4B0 +#define EMC_MRW10_0 0x4B4 +#define EMC_MRW11_0 0x4B8 +#define EMC_MRW12_0 0x4BC +#define EMC_MRW13_0 0x4C0 +#define EMC_MRW14_0 0x4C4 +#define EMC_MRW15_0 0x4D0 +#define EMC_CFG_SYNC_0 0x4D4 +#define EMC_FDPD_CTRL_CMD_NO_RAMP_0 0x4D8 +#define EMC_WDV_CHK_0 0x4E0 +#define EMC_CFG_PIPE_2_0 0x554 +#define EMC_CFG_PIPE_CLK_0 0x558 +#define EMC_CFG_PIPE_1_0 0x55C +#define EMC_CFG_PIPE_0 0x560 +#define EMC_QPOP_0 0x564 +#define EMC_QUSE_WIDTH_0 0x568 +#define EMC_PUTERM_WIDTH_0 0x56C +#define EMC_BGBIAS_CTL0_0 0x570 +#define EMC_AUTO_CAL_CONFIG7_0 0x574 +#define EMC_XM2COMPPADCTRL2_0 0x578 +#define EMC_COMP_PAD_SW_CTRL_0 0x57C +#define EMC_REFCTRL2_0 0x580 +#define EMC_FBIO_CFG7_0 0x584 +#define EMC_DATA_BRLSHFT_0_0 0x588 +#define EMC_DATA_BRLSHFT_1_0 0x58C +#define EMC_RFCPB_0 0x590 +#define EMC_DQS_BRLSHFT_0_0 0x594 +#define EMC_DQS_BRLSHFT_1_0 0x598 +#define EMC_CMD_BRLSHFT_0_0 0x59C +#define EMC_CMD_BRLSHFT_1_0 0x5A0 +#define EMC_CMD_BRLSHFT_2_0 0x5A4 +#define EMC_CMD_BRLSHFT_3_0 0x5A8 +#define EMC_QUSE_BRLSHFT_0_0 0x5AC +#define EMC_AUTO_CAL_CONFIG4_0 0x5B0 +#define EMC_AUTO_CAL_CONFIG5_0 0x5B4 +#define EMC_QUSE_BRLSHFT_1_0 0x5B8 +#define EMC_QUSE_BRLSHFT_2_0 0x5BC +#define EMC_CCDMW_0 0x5C0 +#define EMC_QUSE_BRLSHFT_3_0 0x5C4 +#define EMC_FBIO_CFG8_0 0x5C8 +#define EMC_AUTO_CAL_CONFIG6_0 0x5CC +#define EMC_PROTOBIST_CONFIG_ADR_1_0 0x5D0 +#define EMC_PROTOBIST_CONFIG_ADR_2_0 0x5D4 +#define EMC_PROTOBIST_MISC_0 0x5D8 +#define EMC_PROTOBIST_WDATA_LOWER_0 0x5DC +#define EMC_PROTOBIST_WDATA_UPPER_0 0x5E0 +#define EMC_PROTOBIST_RDATA_0 0x5EC +#define EMC_DLL_CFG_0_0 0x5E4 +#define EMC_DLL_CFG_1_0 0x5E8 +#define EMC_CONFIG_SAMPLE_DELAY_0 0x5F0 +#define EMC_CFG_UPDATE_0 0x5F4 +#define EMC_PMACRO_QUSE_DDLL_RANK0_0_0 0x600 +#define EMC_PMACRO_QUSE_DDLL_RANK0_1_0 0x604 +#define EMC_PMACRO_QUSE_DDLL_RANK0_2_0 0x608 +#define EMC_PMACRO_QUSE_DDLL_RANK0_3_0 0x60C +#define EMC_PMACRO_QUSE_DDLL_RANK0_4_0 0x610 +#define EMC_PMACRO_QUSE_DDLL_RANK0_5_0 0x614 +#define EMC_PMACRO_QUSE_DDLL_RANK1_0_0 0x620 +#define EMC_PMACRO_QUSE_DDLL_RANK1_1_0 0x624 +#define EMC_PMACRO_QUSE_DDLL_RANK1_2_0 0x628 +#define EMC_PMACRO_QUSE_DDLL_RANK1_3_0 0x62C +#define EMC_PMACRO_QUSE_DDLL_RANK1_4_0 0x630 +#define EMC_PMACRO_QUSE_DDLL_RANK1_5_0 0x634 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_0_0 0x640 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_1_0 0x644 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_2_0 0x648 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_3_0 0x64C +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_4_0 0x650 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK0_5_0 0x654 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_0_0 0x660 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_1_0 0x664 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_2_0 0x668 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_3_0 0x66C +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_4_0 0x670 +#define EMC_PMACRO_OB_DDLL_LONG_DQ_RANK1_5_0 0x674 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_0_0 0x680 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_1_0 0x684 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_2_0 0x688 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_3_0 0x68C +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_4_0 0x690 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK0_5_0 0x694 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_0_0 0x6A0 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_1_0 0x6A4 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_2_0 0x6A8 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_3_0 0x6AC +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_4_0 0x6B0 +#define EMC_PMACRO_OB_DDLL_LONG_DQS_RANK1_5_0 0x6B4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_0_0 0x6C0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_1_0 0x6C4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_2_0 0x6C8 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_3_0 0x6CC +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_4_0 0x6D0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK0_5_0 0x6D4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_0_0 0x6E0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_1_0 0x6E4 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_2_0 0x6E8 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_3_0 0x6EC +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_4_0 0x6F0 +#define EMC_PMACRO_IB_DDLL_LONG_DQS_RANK1_5_0 0x6F4 +#define EMC_PMACRO_AUTOCAL_CFG_0_0 0x700 +#define EMC_PMACRO_AUTOCAL_CFG_1_0 0x704 +#define EMC_PMACRO_AUTOCAL_CFG_2_0 0x708 +#define EMC_PMACRO_TX_PWRD_0_0 0x720 +#define EMC_PMACRO_TX_PWRD_1_0 0x724 +#define EMC_PMACRO_TX_PWRD_2_0 0x728 +#define EMC_PMACRO_TX_PWRD_3_0 0x72C +#define EMC_PMACRO_TX_PWRD_4_0 0x730 +#define EMC_PMACRO_TX_PWRD_5_0 0x734 +#define EMC_PMACRO_TX_SEL_CLK_SRC_0_0 0x740 +#define EMC_PMACRO_TX_SEL_CLK_SRC_1_0 0x744 +#define EMC_PMACRO_TX_SEL_CLK_SRC_2_0 0x748 +#define EMC_PMACRO_TX_SEL_CLK_SRC_3_0 0x74C +#define EMC_PMACRO_TX_SEL_CLK_SRC_4_0 0x750 +#define EMC_PMACRO_TX_SEL_CLK_SRC_5_0 0x754 +#define EMC_PMACRO_DDLL_BYPASS_0 0x760 +#define EMC_PMACRO_DDLL_PWRD_0_0 0x770 +#define EMC_PMACRO_DDLL_PWRD_1_0 0x774 +#define EMC_PMACRO_DDLL_PWRD_2_0 0x778 +#define EMC_PMACRO_CMD_CTRL_0_0 0x780 +#define EMC_PMACRO_CMD_CTRL_1_0 0x784 +#define EMC_PMACRO_CMD_CTRL_2_0 0x788 + +#define MC_REGISTER_BASE 0x70019000 +#define MC_REGISTER_REGION_SIZE 0x1000 + +#define MC_INTSTATUS_0 0x000 +#define MC_INTMASK_0 0x004 +#define MC_ERR_STATUS_0 0x008 +#define MC_ERR_ADR_0 0x00C +#define MC_SMMU_CONFIG_0 0x010 +#define MC_SMMU_PTB_ASID_0 0x01C +#define MC_SMMU_PTB_DATA_0 0x020 +#define MC_SMMU_TLB_FLUSH_0 0x030 +#define MC_SMMU_PTC_FLUSH_0_0 0x034 +#define MC_EMEM_CFG_0 0x050 +#define MC_EMEM_ADR_CFG_0 0x054 +#define MC_EMEM_ARB_CFG_0 0x090 +#define MC_EMEM_ARB_OUTSTANDING_REQ_0 0x094 +#define MC_EMEM_ARB_TIMING_RCD_0 0x098 +#define MC_EMEM_ARB_TIMING_RP_0 0x09C +#define MC_EMEM_ARB_TIMING_RC_0 0x0A0 +#define MC_EMEM_ARB_TIMING_RAS_0 0x0A4 +#define MC_EMEM_ARB_TIMING_FAW_0 0x0A8 +#define MC_EMEM_ARB_TIMING_RRD_0 0x0AC +#define MC_EMEM_ARB_TIMING_RAP2PRE_0 0x0B0 +#define MC_EMEM_ARB_TIMING_WAP2PRE_0 0x0B4 +#define MC_EMEM_ARB_TIMING_R2R_0 0x0B8 +#define MC_EMEM_ARB_TIMING_W2W_0 0x0BC +#define MC_EMEM_ARB_TIMING_R2W_0 0x0C0 +#define MC_EMEM_ARB_TIMING_W2R_0 0x0C4 +#define MC_EMEM_ARB_MISC2_0 0x0C8 +#define MC_EMEM_ARB_DA_TURNS_0 0x0D0 +#define MC_EMEM_ARB_DA_COVERS_0 0x0D4 +#define MC_EMEM_ARB_MISC0_0 0x0D8 +#define MC_EMEM_ARB_MISC1_0 0x0DC +#define MC_TIMING_CONTROL_0 0xFC +#define MC_EMEM_ARB_RING1_THROTTLE_0 0x0E0 +#define MC_CLIENT_HOTRESET_CTRL_0 0x200 +#define MC_CLIENT_HOTRESET_STATUS_0 0x204 +#define MC_SMMU_AFI_ASID_0 0x238 +#define MC_SMMU_DC_ASID_0 0x240 +#define MC_SMMU_DCB_ASID_0 0x244 +#define MC_SMMU_HC_ASID_0 0x250 +#define MC_SMMU_HDA_ASID_0 0x254 +#define MC_SMMU_ISP2_ASID_0 0x258 +#define MC_SMMU_MSENC_NVENC_ASID_0 0x264 +#define MC_SMMU_NV_ASID_0 0x268 +#define MC_SMMU_NV2_ASID_0 0x26C +#define MC_SMMU_PPCS_ASID_0 0x270 +#define MC_SMMU_SATA_ASID_0 0x274 +#define MC_SMMU_VI_ASID_0 0x280 +#define MC_SMMU_VIC_ASID_0 0x284 +#define MC_SMMU_XUSB_HOST_ASID_0 0x288 +#define MC_SMMU_XUSB_DEV_ASID_0 0x28C +#define MC_SMMU_TSEC_ASID_0 0x294 +#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2E4 +#define MC_LATENCY_ALLOWANCE_DC_0 0x2E8 +#define MC_LATENCY_ALLOWANCE_DC_1 0x2EC +#define MC_LATENCY_ALLOWANCE_DCB_0 0x2F4 +#define MC_LATENCY_ALLOWANCE_DCB_1 0x2F8 +#define MC_LATENCY_ALLOWANCE_HC_0 0x310 +#define MC_LATENCY_ALLOWANCE_HC_1 0x314 +#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320 +#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328 +#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344 +#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348 +#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370 +#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374 +#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37C +#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380 +#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390 +#define MC_LATENCY_ALLOWANCE_VIC_0 0x394 +#define MC_LATENCY_ALLOWANCE_VI2_0 0x398 +#define MC_LATENCY_ALLOWANCE_GPU_0 0x3AC +#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3B8 +#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3BC +#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3C0 +#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3C4 +#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3D8 +#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3E8 +#define MC_DIS_PTSA_RATE_0 0x41C +#define MC_DIS_PTSA_MIN_0 0x420 +#define MC_DIS_PTSA_MAX_0 0x424 +#define MC_DISB_PTSA_RATE_0 0x428 +#define MC_DISB_PTSA_MIN_0 0x42C +#define MC_DISB_PTSA_MAX_0 0x430 +#define MC_VE_PTSA_RATE_0 0x434 +#define MC_VE_PTSA_MIN_0 0x438 +#define MC_VE_PTSA_MAX_0 0x43C +#define MC_MLL_MPCORER_PTSA_RATE_0 0x44C +#define MC_RING1_PTSA_RATE_0 0x47C +#define MC_RING1_PTSA_MIN_0 0x480 +#define MC_RING1_PTSA_MAX_0 0x484 +#define MC_PCX_PTSA_RATE_0 0x4AC +#define MC_PCX_PTSA_MIN_0 0x4B0 +#define MC_PCX_PTSA_MAX_0 0x4B4 +#define MC_MSE_PTSA_RATE_0 0x4C4 +#define MC_MSE_PTSA_MIN_0 0x4C8 +#define MC_MSE_PTSA_MAX_0 0x4CC +#define MC_AHB_PTSA_RATE_0 0x4DC +#define MC_AHB_PTSA_MIN_0 0x4E0 +#define MC_AHB_PTSA_MAX_0 0x4E4 +#define MC_APB_PTSA_RATE_0 0x4E8 +#define MC_APB_PTSA_MIN_0 0x4EC +#define MC_APB_PTSA_MAX_0 0x4F0 +#define MC_FTOP_PTSA_RATE_0 0x50C +#define MC_HOST_PTSA_RATE_0 0x518 +#define MC_HOST_PTSA_MIN_0 0x51C +#define MC_HOST_PTSA_MAX_0 0x520 +#define MC_USBX_PTSA_RATE_0 0x524 +#define MC_USBX_PTSA_MIN_0 0x528 +#define MC_USBX_PTSA_MAX_0 0x52C +#define MC_USBD_PTSA_RATE_0 0x530 +#define MC_USBD_PTSA_MIN_0 0x534 +#define MC_USBD_PTSA_MAX_0 0x538 +#define MC_GK_PTSA_RATE_0 0x53C +#define MC_GK_PTSA_MIN_0 0x540 +#define MC_GK_PTSA_MAX_0 0x544 +#define MC_AUD_PTSA_RATE_0 0x548 +#define MC_AUD_PTSA_MIN_0 0x54C +#define MC_AUD_PTSA_MAX_0 0x550 +#define MC_VICPC_PTSA_RATE_0 0x554 +#define MC_VICPC_PTSA_MIN_0 0x558 +#define MC_VICPC_PTSA_MAX_0 0x55C +#define MC_JPG_PTSA_RATE_0 0x584 +#define MC_JPG_PTSA_MIN_0 0x588 +#define MC_JPG_PTSA_MAX_0 0x58C +#define MC_GK2_PTSA_RATE_0 0x610 +#define MC_GK2_PTSA_MIN_0 0x614 +#define MC_GK2_PTSA_MAX_0 0x618 +#define MC_SDM_PTSA_RATE_0 0x61C +#define MC_SDM_PTSA_MIN_0 0x620 +#define MC_SDM_PTSA_MAX_0 0x624 +#define MC_HDAPC_PTSA_RATE_0 0x628 +#define MC_HDAPC_PTSA_MIN_0 0x62C +#define MC_HDAPC_PTSA_MAX_0 0x630 +#define MC_SEC_CARVEOUT_BOM_0 0x670 +#define MC_SEC_CARVEOUT_SIZE_MB_0 0x674 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A_0 0x690 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB_0 0x694 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B_0 0x698 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB_0 0x69C +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C_0 0x6A0 +#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB_0 0x6A4 +#define MC_EMEM_ARB_TIMING_RFCPB_0 0x6C0 +#define MC_EMEM_ARB_TIMING_CCDMW_0 0x6C4 +#define MC_EMEM_ARB_REFPB_HP_CTRL_0 0x6F0 +#define MC_EMEM_ARB_REFPB_BANK_CTRL_0 0x6F4 +#define MC_PTSA_GRANT_DECREMENT_0 0x960 +#define MC_CLIENT_HOTRESET_CTRL_1 0x970 +#define MC_CLIENT_HOTRESET_STATUS_1 0x974 +#define MC_SMMU_PTC_FLUSH_1 0x9B8 +#define MC_SMMU_DC1_ASID_0 0xA88 +#define MC_SMMU_SDMMC1A_ASID_0 0xA94 +#define MC_SMMU_SDMMC2A_ASID_0 0xA98 +#define MC_SMMU_SDMMC3A_ASID_0 0xA9C +#define MC_SMMU_SDMMC4A_ASID_0 0xAA0 +#define MC_SMMU_ISP2B_ASID_0 0xAA4 +#define MC_SMMU_GPU_ASID_0 0xAA8 +#define MC_SMMU_GPUB_ASID_0 0xAAC +#define MC_SMMU_PPCS2_ASID_0 0xAB0 +#define MC_SMMU_NVDEC_ASID_0 0xAB4 +#define MC_SMMU_APE_ASID_0 0xAB8 +#define MC_SMMU_SE_ASID_0 0xABC +#define MC_SMMU_NVJPG_ASID_0 0xAC0 +#define MC_SMMU_HC1_ASID_0 0xAC4 +#define MC_SMMU_SE1_ASID_0 0xAC8 +#define MC_SMMU_AXIAP_ASID_0 0xACC +#define MC_SMMU_ETR_ASID_0 0xAD0 +#define MC_SMMU_TSECB_ASID_0 0xAD4 +#define MC_SMMU_TSEC1_ASID_0 0xAD8 +#define MC_SMMU_TSECB1_ASID_0 0xADC +#define MC_SMMU_NVDEC1_ASID_0 0xAE0 +#define MC_EMEM_ARB_DHYST_CTRL_0 0xBCC +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xBD0 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xBD4 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xBD8 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xBDC +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xBE0 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xBE4 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xBE8 +#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xBEC +#define MC_ERR_GENERALIZED_CARVEOUT_STATUS_0 0xC00 +#define MC_SECURITY_CARVEOUT2_BOM_0 0xC5C +#define MC_SECURITY_CARVEOUT3_BOM_0 0xCAC + +#define CLDVFS_REGION_BASE 0x70110000 +#define CLDVFS_REGION_SIZE 0x1000 +#define CL_DVFS_CTRL_0 0x0 +#define CL_DVFS_CONFIG_0 0x4 +#define CL_DVFS_PARAMS_0 0x8 +#define CL_DVFS_TUNE0_0 0xC +#define CL_DVFS_TUNE1_0 0x10 +#define CL_DVFS_FREQ_REQ_0 0x14 +#define CL_DVFS_SCALE_RAMP_0 0x18 +#define CL_DVFS_DROOP_CTRL_0 0x1C +#define CL_DVFS_OUTPUT_CFG_0 0x20 +#define CL_DVFS_OUTPUT_FORCE_0 0x24 +#define CL_DVFS_MONITOR_CTRL_0 0x28 +#define CL_DVFS_MONITOR_DATA_0 0x2C +#define CL_DVFS_I2C_CFG_0 0x40 +#define CL_DVFS_I2C_VDD_REG_ADDR_0 0x44 +#define CL_DVFS_I2C_STS_0 0x48 +#define CL_DVFS_INTR_STS_0 0x5C +#define CL_DVFS_INTR_EN_0 0x60 +#define DVFS_DFLL_THROTTLE_CTRL_0 0x64 +#define DVFS_DFLL_THROTTLE_LIGHT_0 0x68 +#define DVFS_DFLL_THROTTLE_MEDIUM_0 0x6C +#define DVFS_DFLL_THROTTLE_HEAVY_0 0x70 +#define DVFS_CC4_HVC_0 0x74 +#define CL_DVFS_MONITOR_DATA_0 0x2C +#define CL_DVFS_I2C_CFG_0 0x40 +#define CL_DVFS_I2C_VDD_REG_ADDR_0 0x44 +#define CL_DVFS_I2C_STS_0 0x48 +#define CL_DVFS_INTR_STS_0 0x5C +#define CL_DVFS_I2C_CLK_DIVISOR_REGISTER_0 0x16C diff --git a/Source/rewrite-hoc-clk/common/include/rgltr.h b/Source/rewrite-hoc-clk/common/include/rgltr.h new file mode 100644 index 00000000..33268b8a --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/rgltr.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) ppkantorski (bord2death) + * + * 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 . + * + */ + +#pragma once +#include +#include "pcv_types.h" + +typedef struct { + Service s; +} RgltrSession; + +Result rgltrInitialize(void); + +void rgltrExit(void); + +Service* rgltrGetServiceSession(void); + +Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id); +void rgltrCloseSession(RgltrSession* session); +Result rgltrGetVoltage(RgltrSession* session, u32 *out_volt); +Result rgltrGetPowerModuleNumLimit(u32 *out); +Result rgltrGetVoltageEnabled(RgltrSession* session, u32 *out); +Result rgltrRequestVoltage(RgltrSession* session, u32 microvolt); +Result rgltrCancelVoltageRequest(RgltrSession* session); \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/rgltr_services.h b/Source/rewrite-hoc-clk/common/include/rgltr_services.h new file mode 100644 index 00000000..2010cc33 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/rgltr_services.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) ppkantorski (bord2death) + * + * 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 . + * + */ + +#pragma once + +#include // for Service, Result, hosversionBefore(), smGetService(), serviceClose(), etc. +#include "rgltr.h" // for RgltrSession, PowerDomainId, etc. + +extern Service g_rgltrSrv; + +Result rgltrInitialize(void); +void rgltrExit(void); + +Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id); + +Result rgltrGetVoltage(RgltrSession* session, u32* out_volt); + +void rgltrCloseSession(RgltrSession* session); diff --git a/Source/rewrite-hoc-clk/common/include/service_guard.h b/Source/rewrite-hoc-clk/common/include/service_guard.h new file mode 100644 index 00000000..96a348a5 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/service_guard.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) MasaGratoR + * + * 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 . + * + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +typedef struct ServiceGuard { + Mutex mutex; + u32 refCount; +} ServiceGuard; + +NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g) +{ + mutexLock(&g->mutex); + return (g->refCount++) == 0; +} + +NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void)) +{ + if (R_FAILED(rc)) { + cleanupFunc(); + --g->refCount; + } + mutexUnlock(&g->mutex); + return rc; +} + +NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void)) +{ + mutexLock(&g->mutex); + if (g->refCount && (--g->refCount) == 0) + cleanupFunc(); + mutexUnlock(&g->mutex); +} + +#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \ +\ +static ServiceGuard g_##name##Guard; \ +NX_INLINE Result _##name##Initialize _paramdecl; \ +static void _##name##Cleanup(void); \ +\ +Result name##Initialize _paramdecl \ +{ \ + Result rc = 0; \ + if (serviceGuardBeginInit(&g_##name##Guard)) \ + rc = _##name##Initialize _parampass; \ + return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \ +} \ +\ +void name##Exit(void) \ +{ \ + serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \ +} + +#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ()) + +#ifdef __cplusplus +} +#endif diff --git a/Source/rewrite-hoc-clk/common/include/sysclk.h b/Source/rewrite-hoc-clk/common/include/sysclk.h new file mode 100644 index 00000000..c44f506b --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk.h @@ -0,0 +1,55 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once +#include +#include // include libnx +#ifdef __cplusplus +#include "cpp_util.hpp" +extern "C" { +#endif + +// typedef std::uint32_t Result; +// typedef std::uint32_t u32; +// typedef std::int32_t s32; +// typedef std::uint64_t u64; +// typedef std::int64_t s64; +// typedef std::uint8_t u8; +// typedef std::int16_t s16; +// typedef std::uint16_t u16; + +#include "sysclk/ipc.h" +#include "sysclk/board.h" +#include "sysclk/clock_manager.h" +#include "sysclk/apm.h" +#include "sysclk/config.h" +#include "sysclk/errors.h" +#include "sysclk/psm_ext.h" + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/apm.h b/Source/rewrite-hoc-clk/common/include/sysclk/apm.h new file mode 100644 index 00000000..9815a9b0 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/apm.h @@ -0,0 +1,39 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include "board.h" + +typedef struct { + uint32_t id; + uint32_t cpu_hz; + uint32_t gpu_hz; + uint32_t mem_hz; +} SysClkApmConfiguration; + +extern SysClkApmConfiguration sysclk_g_apm_configurations[]; diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/board.h b/Source/rewrite-hoc-clk/common/include/sysclk/board.h new file mode 100644 index 00000000..3f8d974d --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/board.h @@ -0,0 +1,270 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include +#include +#include +#include +typedef enum +{ + SysClkSocType_Erista = 0, + SysClkSocType_Mariko, + SysClkSocType_EnumMax +} SysClkSocType; + +typedef enum +{ + HorizonOCConsoleType_Icosa = 0, + HorizonOCConsoleType_Copper, + HorizonOCConsoleType_Hoag, + HorizonOCConsoleType_Iowa, + HorizonOCConsoleType_Calcio, + HorizonOCConsoleType_Aula, + HorizonOCConsoleType_EnumMax, +} HorizonOCConsoleType; + +typedef enum { + HocClkVoltage_SOC = 0, + HocClkVoltage_EMCVDD2, + HocClkVoltage_CPU, + HocClkVoltage_GPU, + HocClkVoltage_EMCVDDQ_MarikoOnly, + HocClkVoltage_Display, + HocClkVoltage_Battery, + HocClkVoltage_EnumMax, +} HocClkVoltage; + +typedef enum +{ + SysClkProfile_Handheld = 0, + SysClkProfile_HandheldCharging, + SysClkProfile_HandheldChargingUSB, + SysClkProfile_HandheldChargingOfficial, + SysClkProfile_Docked, + SysClkProfile_EnumMax +} SysClkProfile; + +typedef enum +{ + SysClkModule_CPU = 0, + SysClkModule_GPU, + SysClkModule_MEM, + HorizonOCModule_Governor, + HorizonOCModule_Display, + SysClkModule_EnumMax, +} SysClkModule; + +typedef enum +{ + SysClkThermalSensor_SOC = 0, + SysClkThermalSensor_PCB, + SysClkThermalSensor_Skin, + HorizonOCThermalSensor_Battery, + HorizonOCThermalSensor_PMIC, + SysClkThermalSensor_EnumMax +} SysClkThermalSensor; + +typedef enum +{ + SysClkPowerSensor_Now = 0, + SysClkPowerSensor_Avg, + SysClkPowerSensor_EnumMax +} SysClkPowerSensor; + +typedef enum +{ + SysClkPartLoad_EMC = 0, + SysClkPartLoad_EMCCpu, + HocClkPartLoad_GPU, + HocClkPartLoad_CPUMax, + HocClkPartLoad_BAT, + HocClkPartLoad_FAN, + SysClkPartLoad_EnumMax +} SysClkPartLoad; + +typedef enum { + HorizonOCSpeedo_CPU = 0, + HorizonOCSpeedo_GPU, + HorizonOCSpeedo_SOC, + HorizonOCSpeedo_EnumMax, +} HorizonOCSpeedo; + +typedef enum { + GPUUVLevel_NoUV = 0, + GPUUVLevel_SLT, + GPUUVLevel_HiOPT, + GPUUVLevel_EnumMax, +} GPUUndervoltLevel; + +enum { + DVFSMode_Disabled = 0, + DVFSMode_Hijack, + // DVFSMode_OfficialService, + // DVFSMode_Hack, + DVFSMode_EnumMax, +}; + +typedef enum { + GpuSchedulingMode_DoNotOverride = 0, + GpuSchedulingMode_Enabled, + GpuSchedulingMode_Disabled, + GpuSchedulingMode_EnumMax, +} GpuSchedulingMode; + +typedef enum { + GpuSchedulingOverrideMethod_Ini = 0, + GpuSchedulingOverrideMethod_NvService, + GpuSchedulingOverrideMethod_EnumMax, +} GpuSchedulingOverrideMethod; +typedef enum { + ComponentGovernor_DoNotOverride = 0, + ComponentGovernor_Enabled = 1, + ComponentGovernor_Disabled = 2, + ComponentGovernor_EnumMax, +} ComponentGovernorState; +typedef enum { + RamDisplayMode_VDD2VDDQ = 0, + RamDisplayMode_VDD2Usage, + RamDisplayMode_VDDQUsage, + RamDisplayMode_EnumMax, +} RamDisplayMode; + +#define SYSCLK_ENUM_VALID(n, v) ((v) < n##_EnumMax) + +// Packed u32 +// Bits 0-7 - CPU +// Bits 8-15 - GPU +// Bits 16-23 - VRR +// Bits 24-32 - unused + +inline u32 GovernorStatePack(u8 cpu, u8 gpu, u8 vrr) { + return (u32)cpu | ((u32)gpu << 8) | ((u32)vrr << 16); +} +inline u8 GovernorStateCpu(u32 p) { + return (u8)(p & 0xFF); +} +inline u8 GovernorStateGpu(u32 p) { + return (u8)((p >> 8) & 0xFF); +} +inline u8 GovernorStateVrr(u32 p) { + return (u8)((p >> 16) & 0xFF); +} + +static inline const char* sysclkFormatModule(SysClkModule module, bool pretty) +{ + switch(module) + { + case SysClkModule_CPU: + return pretty ? "CPU" : "cpu"; + case SysClkModule_GPU: + return pretty ? "GPU" : "gpu"; + case SysClkModule_MEM: + return pretty ? "Memory" : "mem"; + case HorizonOCModule_Display: + return pretty ? "Display" : "display"; + case HorizonOCModule_Governor: + return pretty ? "Governor" : "governor"; + default: + return "null"; + } +} + +static inline const char* sysclkFormatThermalSensor(SysClkThermalSensor thermSensor, bool pretty) +{ + switch(thermSensor) + { + case SysClkThermalSensor_SOC: + return pretty ? "SOC" : "soc"; + case SysClkThermalSensor_PCB: + return pretty ? "PCB" : "pcb"; + case SysClkThermalSensor_Skin: + return pretty ? "Skin" : "skin"; + case HorizonOCThermalSensor_Battery: + return pretty ? "BAT" : "battery"; + case HorizonOCThermalSensor_PMIC: + return pretty ? "PMIC" : "pmic"; + + default: + return NULL; + } +} + +static inline const char* sysclkFormatPowerSensor(SysClkPowerSensor powSensor, bool pretty) +{ + switch(powSensor) + { + case SysClkPowerSensor_Now: + return pretty ? "Now" : "now"; + case SysClkPowerSensor_Avg: + return pretty ? "Avg" : "avg"; + default: + return NULL; + } +} + +static inline const char* sysclkFormatProfile(SysClkProfile profile, bool pretty) +{ + switch(profile) + { + case SysClkProfile_Docked: + return pretty ? "Docked" : "docked"; + case SysClkProfile_Handheld: + return pretty ? "Handheld" : "handheld"; + case SysClkProfile_HandheldCharging: + return pretty ? "Charging" : "handheld_charging"; + case SysClkProfile_HandheldChargingUSB: + return pretty ? "USB Charger" : "handheld_charging_usb"; + case SysClkProfile_HandheldChargingOfficial: + return pretty ? "PD Charger" : "handheld_charging_official"; + default: + return NULL; + } +} + + +static inline const char* hocClkFormatVoltage(HocClkVoltage voltage, bool pretty) +{ + switch(voltage) + { + case HocClkVoltage_CPU: + return pretty ? "CPU" : "cpu"; + case HocClkVoltage_GPU: + return pretty ? "GPU" : "gpu"; + case HocClkVoltage_EMCVDD2: + return pretty ? "VDD2" : "emcvdd2"; + case HocClkVoltage_EMCVDDQ_MarikoOnly: + return pretty ? "VDDQ" : "vddq"; + case HocClkVoltage_SOC: + return pretty ? "SOC" : "soc"; + case HocClkVoltage_Display: + return pretty ? "Display" : "display"; + default: + return NULL; + } +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/client/ipc.h b/Source/rewrite-hoc-clk/common/include/sysclk/client/ipc.h new file mode 100644 index 00000000..242e3d1a --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/client/ipc.h @@ -0,0 +1,57 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include "types.h" +#include "../config.h" +#include "../board.h" +#include "../ipc.h" + +bool sysclkIpcRunning(); +Result sysclkIpcInitialize(void); +void sysclkIpcExit(void); + +Result sysclkIpcGetAPIVersion(u32* out_ver); +Result sysclkIpcGetVersionString(char* out, size_t len); +Result sysclkIpcGetCurrentContext(SysClkContext* out_context); +Result sysclkIpcGetProfileCount(u64 tid, u8* out_count); +Result sysclkIpcSetEnabled(bool enabled); +Result sysclkIpcExitCmd(); +Result sysclkIpcSetOverride(SysClkModule module, u32 hz); +Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles); +Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles); +Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues); +Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues); +Result sysclkIpcGetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* outCount); +Result hocClkIpcSetKipData(); +Result hocClkIpcGetKipData(); + +static inline Result sysclkIpcRemoveOverride(SysClkModule module) +{ + return sysclkIpcSetOverride(module, 0); +} diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/client/types.h b/Source/rewrite-hoc-clk/common/include/sysclk/client/types.h new file mode 100644 index 00000000..b9feb9ba --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/client/types.h @@ -0,0 +1,46 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#ifdef __SWITCH__ + +#include +#include + +#else + +#define R_FAILED(res) ((res) != 0) +#define R_SUCCEEDED(res) ((res) == 0) + +typedef std::uint32_t Result; +typedef std::uint32_t u32; +typedef std::int32_t s32; +typedef std::uint64_t u64; +typedef std::uint8_t u8; + +#endif diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/clock_manager.h b/Source/rewrite-hoc-clk/common/include/sysclk/clock_manager.h new file mode 100644 index 00000000..917b3701 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/clock_manager.h @@ -0,0 +1,66 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include +#include "board.h" + +typedef struct +{ + uint64_t applicationId; + SysClkProfile profile; + uint32_t freqs[SysClkModule_EnumMax]; + uint32_t realFreqs[SysClkModule_EnumMax]; + uint32_t overrideFreqs[SysClkModule_EnumMax]; + uint32_t temps[SysClkThermalSensor_EnumMax]; + int32_t power[SysClkPowerSensor_EnumMax]; + uint32_t partLoad[SysClkPartLoad_EnumMax]; + uint32_t voltages[HocClkVoltage_EnumMax]; + u16 speedos[HorizonOCSpeedo_EnumMax]; + u16 iddq[HorizonOCSpeedo_EnumMax]; + GpuSchedulingMode gpuSchedulingMode; + bool isSysDockInstalled; + bool isSaltyNXInstalled; + u8 maxDisplayFreq; + u8 dramID; + bool isDram8GB; + u8 fps; + u16 resolutionHeight; +} SysClkContext; + +typedef struct +{ + union { + uint32_t mhz[+SysClkProfile_EnumMax * +SysClkModule_EnumMax]; + uint32_t mhzMap[+SysClkProfile_EnumMax][+SysClkModule_EnumMax]; + }; +} SysClkTitleProfileList; + +#define SYSCLK_FREQ_LIST_MAX 32 + +#define GLOBAL_PROFILE_ID 0xA111111111111111 \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/config.h b/Source/rewrite-hoc-clk/common/include/sysclk/config.h new file mode 100644 index 00000000..ccde8e5e --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/config.h @@ -0,0 +1,593 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include +#include + +typedef enum { + SysClkConfigValue_PollingIntervalMs = 0, + SysClkConfigValue_TempLogIntervalMs, + SysClkConfigValue_FreqLogIntervalMs, + SysClkConfigValue_PowerLogIntervalMs, + SysClkConfigValue_CsvWriteIntervalMs, + + HocClkConfigValue_UncappedClocks, + HocClkConfigValue_OverwriteBoostMode, + + HocClkConfigValue_EristaMaxCpuClock, + HocClkConfigValue_MarikoMaxCpuClock, + + HocClkConfigValue_ThermalThrottle, + HocClkConfigValue_ThermalThrottleThreshold, + + HocClkConfigValue_HandheldTDP, + HocClkConfigValue_HandheldTDPLimit, + + HocClkConfigValue_LiteTDPLimit, + + HorizonOCConfigValue_BatteryChargeCurrent, + + HorizonOCConfigValue_OverwriteRefreshRate, + HorizonOCConfigValue_EnableUnsafeDisplayFreqs, + + HorizonOCConfigValue_DVFSMode, + HorizonOCConfigValue_DVFSOffset, + HorizonOCConfigValue_LiveCpuUv, + HorizonOCConfigValue_EnableExperimentalSettings, + + HorizonOCConfigValue_GPUScheduling, + HorizonOCConfigValue_GPUSchedulingMethod, + + HorizonOCConfigValue_RAMVoltUsageDisplayMode, + HorizonOCConfigValue_CpuGovernorMinimumFreq, + + KipConfigValue_custRev, + // KipConfigValue_mtcConf, + KipConfigValue_hpMode, + + KipConfigValue_commonEmcMemVolt, + KipConfigValue_eristaEmcMaxClock, + KipConfigValue_eristaEmcMaxClock1, + KipConfigValue_eristaEmcMaxClock2, + KipConfigValue_marikoEmcMaxClock, + KipConfigValue_marikoEmcVddqVolt, + KipConfigValue_emcDvbShift, + + KipConfigValue_t1_tRCD, + KipConfigValue_t2_tRP, + KipConfigValue_t3_tRAS, + KipConfigValue_t4_tRRD, + KipConfigValue_t5_tRFC, + KipConfigValue_t6_tRTW, + KipConfigValue_t7_tWTR, + KipConfigValue_t8_tREFI, + KipConfigValue_mem_burst_read_latency, + KipConfigValue_mem_burst_write_latency, + + KipConfigValue_eristaCpuUV, + KipConfigValue_eristaCpuVmin, + KipConfigValue_eristaCpuMaxVolt, + KipConfigValue_eristaCpuUnlock, + + KipConfigValue_marikoCpuUVLow, + KipConfigValue_marikoCpuUVHigh, + KipConfigValue_tableConf, + KipConfigValue_marikoCpuLowVmin, + KipConfigValue_marikoCpuHighVmin, + KipConfigValue_marikoCpuMaxVolt, + KipConfigValue_marikoCpuMaxClock, + KipConfigValue_eristaCpuBoostClock, + KipConfigValue_marikoCpuBoostClock, + + KipConfigValue_eristaGpuUV, + KipConfigValue_eristaGpuVmin, + + KipConfigValue_marikoGpuUV, + KipConfigValue_marikoGpuVmin, + KipConfigValue_marikoGpuVmax, + + KipConfigValue_commonGpuVoltOffset, + KipConfigValue_gpuSpeedo, + + KipConfigValue_g_volt_76800, + KipConfigValue_g_volt_153600, + KipConfigValue_g_volt_230400, + KipConfigValue_g_volt_307200, + KipConfigValue_g_volt_384000, + KipConfigValue_g_volt_460800, + KipConfigValue_g_volt_537600, + KipConfigValue_g_volt_614400, + KipConfigValue_g_volt_691200, + KipConfigValue_g_volt_768000, + KipConfigValue_g_volt_844800, + KipConfigValue_g_volt_921600, + KipConfigValue_g_volt_998400, + KipConfigValue_g_volt_1075200, + KipConfigValue_g_volt_1152000, + KipConfigValue_g_volt_1228800, + KipConfigValue_g_volt_1267200, + KipConfigValue_g_volt_1305600, + KipConfigValue_g_volt_1344000, + KipConfigValue_g_volt_1382400, + KipConfigValue_g_volt_1420800, + KipConfigValue_g_volt_1459200, + KipConfigValue_g_volt_1497600, + KipConfigValue_g_volt_1536000, + + KipConfigValue_g_volt_e_76800, + KipConfigValue_g_volt_e_115200, + KipConfigValue_g_volt_e_153600, + KipConfigValue_g_volt_e_192000, + KipConfigValue_g_volt_e_230400, + KipConfigValue_g_volt_e_268800, + KipConfigValue_g_volt_e_307200, + KipConfigValue_g_volt_e_345600, + KipConfigValue_g_volt_e_384000, + KipConfigValue_g_volt_e_422400, + KipConfigValue_g_volt_e_460800, + KipConfigValue_g_volt_e_499200, + KipConfigValue_g_volt_e_537600, + KipConfigValue_g_volt_e_576000, + KipConfigValue_g_volt_e_614400, + KipConfigValue_g_volt_e_652800, + KipConfigValue_g_volt_e_691200, + KipConfigValue_g_volt_e_729600, + KipConfigValue_g_volt_e_768000, + KipConfigValue_g_volt_e_806400, + KipConfigValue_g_volt_e_844800, + KipConfigValue_g_volt_e_883200, + KipConfigValue_g_volt_e_921600, + KipConfigValue_g_volt_e_960000, + KipConfigValue_g_volt_e_998400, + KipConfigValue_g_volt_e_1036800, + KipConfigValue_g_volt_e_1075200, + + KipConfigValue_t6_tRTW_fine_tune, + KipConfigValue_t7_tWTR_fine_tune, + + KipCrc32, + HocClkConfigValue_IsFirstLoad, + SysClkConfigValue_EnumMax, +} SysClkConfigValue; + +typedef struct { + uint64_t values[SysClkConfigValue_EnumMax]; +} SysClkConfigValueList; + +static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pretty) +{ + switch(val) + { + case SysClkConfigValue_PollingIntervalMs: + return pretty ? "Polling Interval (ms)" : "poll_interval_ms"; + case SysClkConfigValue_TempLogIntervalMs: + return pretty ? "Temperature logging interval (ms)" : "temp_log_interval_ms"; + case SysClkConfigValue_FreqLogIntervalMs: + return pretty ? "Frequency logging interval (ms)" : "freq_log_interval_ms"; + case SysClkConfigValue_PowerLogIntervalMs: + return pretty ? "Power logging interval (ms)" : "power_log_interval_ms"; + case SysClkConfigValue_CsvWriteIntervalMs: + return pretty ? "CSV write interval (ms)" : "csv_write_interval_ms"; + + case HocClkConfigValue_UncappedClocks: + return pretty ? "Uncapped Clocks" : "uncapped_clocks"; + case HocClkConfigValue_OverwriteBoostMode: + return pretty ? "Overwrite Boost Mode" : "ow_boost"; + + case HocClkConfigValue_EristaMaxCpuClock: + return pretty ? "CPU Max Clock" : "cpu_max_e"; + + case HocClkConfigValue_MarikoMaxCpuClock: + return pretty ? "CPU Max Display Clock" : "cpu_max_m"; + + case HocClkConfigValue_ThermalThrottle: + return pretty ? "Thermal Throttle" : "thermal_throttle"; + + case HocClkConfigValue_ThermalThrottleThreshold: + return pretty ? "Thermal Throttle Threshold" : "thermal_throttle_threshold"; + + case HocClkConfigValue_HandheldTDP: + return pretty ? "Handheld TDP" : "handheld_tdp"; + + case HocClkConfigValue_HandheldTDPLimit: + return pretty ? "Handheld TDP Limit" : "tdp_limit"; + + case HocClkConfigValue_LiteTDPLimit: + return pretty ? "Handheld TDP Limit" : "tdp_limit_l"; + + case HorizonOCConfigValue_BatteryChargeCurrent: + return pretty ? "Battery Charge Current" : "bat_charge_current"; + + case HorizonOCConfigValue_OverwriteRefreshRate: + return pretty ? "Display Refresh Rate Changing" : "drr_changing"; + + case HorizonOCConfigValue_EnableUnsafeDisplayFreqs: + return pretty ? "Enable Unsafe Display Frequencies" : "drr_unsafe"; + + case HorizonOCConfigValue_DVFSMode: + return pretty ? "DVFS Mode" : "dvfs_mode"; + + case HorizonOCConfigValue_DVFSOffset: + return pretty ? "DVFS Offset" : "dvfs_offset"; + + case HorizonOCConfigValue_GPUScheduling: + return pretty ? "GPU Scheduling" : "gpu_scheduling"; + + case HorizonOCConfigValue_GPUSchedulingMethod: + return pretty ? "GPU Scheduling Method" : "gpu_sched_method"; + + case HorizonOCConfigValue_LiveCpuUv: + return pretty ? "Live CPU Undervolt" : "live_cpu_uv"; + + case HorizonOCConfigValue_EnableExperimentalSettings: + return pretty ? "Enable Experimental Settings" : "enable_experimental_settings"; + + case HorizonOCConfigValue_RAMVoltUsageDisplayMode: + return pretty ? "RAM Voltage / Usage Display Mode" : "ram_volt_usage_display_mode"; + case HorizonOCConfigValue_CpuGovernorMinimumFreq: + return pretty ? "CPU Governor Minimum Frequency" : "cpu_gov_min_freq"; + // KIP config values + case KipConfigValue_custRev: + return pretty ? "Custom Revision" : "kip_cust_rev"; + // case KipConfigValue_mtcConf: + // return pretty ? "MTC Config" : "kip_mtc_conf"; + case KipConfigValue_hpMode: + return pretty ? "HP Mode" : "kip_hp_mode"; + + // EMC + case KipConfigValue_commonEmcMemVolt: + return pretty ? "Common EMC/MEM Voltage" : "common_emc_mem_volt"; + case KipConfigValue_eristaEmcMaxClock: + return pretty ? "Erista EMC Max Clock 1" : "erista_emc_max_clock"; + case KipConfigValue_eristaEmcMaxClock1: + 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_marikoEmcMaxClock: + return pretty ? "Mariko EMC Max Clock" : "mariko_emc_max_clock"; + case KipConfigValue_marikoEmcVddqVolt: + return pretty ? "Mariko EMC VDDQ Voltage" : "mariko_emc_vddq_volt"; + case KipConfigValue_emcDvbShift: + return pretty ? "EMC DVB Shift" : "emc_dvb_shift"; + + // Memory timings + case KipConfigValue_t1_tRCD: + return pretty ? "t1 - tRCD" : "t1_trcd"; + case KipConfigValue_t2_tRP: + return pretty ? "t2 - tRP" : "t2_trp"; + case KipConfigValue_t3_tRAS: + return pretty ? "t3 - tRAS" : "t3_tras"; + case KipConfigValue_t4_tRRD: + return pretty ? "t4 - tRRD" : "t4_trrd"; + case KipConfigValue_t5_tRFC: + return pretty ? "t5 - tRFC" : "t5_trfc"; + case KipConfigValue_t6_tRTW: + return pretty ? "t6 - tRTW" : "t6_trtw"; + case KipConfigValue_t7_tWTR: + return pretty ? "t7 - tWTR" : "t7_twtr"; + case KipConfigValue_t8_tREFI: + return pretty ? "t8 - tREFI" : "t8_trefi"; + case KipConfigValue_mem_burst_read_latency: + return pretty ? "Memory Burst Read Latency" : "mem_burst_read_latency"; + case KipConfigValue_mem_burst_write_latency: + return pretty ? "Memory Burst Write Latency" : "mem_burst_write_latency"; + + // CPU – Erista + case KipConfigValue_eristaCpuUV: + return pretty ? "Erista CPU Undervolt" : "erista_cpu_uv"; + case KipConfigValue_eristaCpuVmin: + return pretty ? "Erista CPU vMin" : "erista_cpu_vmin"; + case KipConfigValue_eristaCpuMaxVolt: + return pretty ? "Erista CPU Max Voltage" : "erista_cpu_max_volt"; + case KipConfigValue_eristaCpuUnlock: + return pretty ? "Erista CPU Unlock" : "erista_cpu_unlock"; + + // CPU – Mariko + case KipConfigValue_marikoCpuUVLow: + return pretty ? "Mariko CPU Undervolt (Low)" : "mariko_cpu_uv_low"; + case KipConfigValue_marikoCpuUVHigh: + return pretty ? "Mariko CPU Undervolt (High)" : "mariko_cpu_uv_high"; + case KipConfigValue_tableConf: + return pretty ? "Table Config" : "kip_table_conf"; + case KipConfigValue_marikoCpuLowVmin: + return pretty ? "Mariko CPU Low Vmin" : "mariko_cpu_low_vmin"; + case KipConfigValue_marikoCpuHighVmin: + return pretty ? "Mariko CPU High Vmin" : "mariko_cpu_high_vmin"; + case KipConfigValue_marikoCpuMaxVolt: + return pretty ? "Mariko CPU Max Voltage" : "mariko_cpu_max_volt"; + + case KipConfigValue_eristaCpuBoostClock: + return pretty ? "Erista CPU Boost Clock" : "erista_cpu_boost_clock"; + case KipConfigValue_marikoCpuBoostClock: + return pretty ? "Mariko CPU Boost Clock" : "mariko_cpu_boost_clock"; + + case KipConfigValue_marikoCpuMaxClock: + return pretty ? "Mariko CPU Max Clock" : "mariko_cpu_max_clock"; + + // GPU – Erista + case KipConfigValue_eristaGpuUV: + return pretty ? "Erista GPU Undervolt" : "erista_gpu_uv"; + case KipConfigValue_eristaGpuVmin: + return pretty ? "Erista GPU Vmin" : "erista_gpu_vmin"; + + // GPU – Mariko + case KipConfigValue_marikoGpuUV: + return pretty ? "Mariko GPU Undervolt" : "mariko_gpu_uv"; + case KipConfigValue_marikoGpuVmin: + return pretty ? "Mariko GPU Vmin" : "mariko_gpu_vmin"; + case KipConfigValue_marikoGpuVmax: + return pretty ? "Mariko GPU Vmax" : "mariko_gpu_vmax"; + + case KipConfigValue_commonGpuVoltOffset: + return pretty ? "Common GPU Voltage Offset" : "common_gpu_volt_offset"; + case KipConfigValue_gpuSpeedo: + return pretty ? "GPU Speedo" : "gpu_speedo"; + + // Mariko GPU voltages (24) + case KipConfigValue_g_volt_76800: return pretty ? "Mariko GPU Volt 76 MHz" : "g_volt_76800"; + case KipConfigValue_g_volt_153600: return pretty ? "Mariko GPU Volt 153 MHz" : "g_volt_153600"; + case KipConfigValue_g_volt_230400: return pretty ? "Mariko GPU Volt 230 MHz" : "g_volt_230400"; + case KipConfigValue_g_volt_307200: return pretty ? "Mariko GPU Volt 307 MHz" : "g_volt_307200"; + case KipConfigValue_g_volt_384000: return pretty ? "Mariko GPU Volt 384 MHz" : "g_volt_384000"; + case KipConfigValue_g_volt_460800: return pretty ? "Mariko GPU Volt 460 MHz" : "g_volt_460800"; + case KipConfigValue_g_volt_537600: return pretty ? "Mariko GPU Volt 537 MHz" : "g_volt_537600"; + case KipConfigValue_g_volt_614400: return pretty ? "Mariko GPU Volt 614 MHz" : "g_volt_614400"; + case KipConfigValue_g_volt_691200: return pretty ? "Mariko GPU Volt 691 MHz" : "g_volt_691200"; + case KipConfigValue_g_volt_768000: return pretty ? "Mariko GPU Volt 768 MHz" : "g_volt_768000"; + case KipConfigValue_g_volt_844800: return pretty ? "Mariko GPU Volt 844 MHz" : "g_volt_844800"; + case KipConfigValue_g_volt_921600: return pretty ? "Mariko GPU Volt 921 MHz" : "g_volt_921600"; + case KipConfigValue_g_volt_998400: return pretty ? "Mariko GPU Volt 998 MHz" : "g_volt_998400"; + case KipConfigValue_g_volt_1075200: return pretty ? "Mariko GPU Volt 1075 MHz" : "g_volt_1075200"; + case KipConfigValue_g_volt_1152000: return pretty ? "Mariko GPU Volt 1152 MHz" : "g_volt_1152000"; + case KipConfigValue_g_volt_1228800: return pretty ? "Mariko GPU Volt 1228 MHz" : "g_volt_1228800"; + case KipConfigValue_g_volt_1267200: return pretty ? "Mariko GPU Volt 1267 MHz" : "g_volt_1267200"; + case KipConfigValue_g_volt_1305600: return pretty ? "Mariko GPU Volt 1305 MHz" : "g_volt_1305600"; + case KipConfigValue_g_volt_1344000: return pretty ? "Mariko GPU Volt 1344 MHz" : "g_volt_1344000"; + case KipConfigValue_g_volt_1382400: return pretty ? "Mariko GPU Volt 1382 MHz" : "g_volt_1382400"; + case KipConfigValue_g_volt_1420800: return pretty ? "Mariko GPU Volt 1420 MHz" : "g_volt_1420800"; + case KipConfigValue_g_volt_1459200: return pretty ? "Mariko GPU Volt 1459 MHz" : "g_volt_1459200"; + case KipConfigValue_g_volt_1497600: return pretty ? "Mariko GPU Volt 1497 MHz" : "g_volt_1497600"; + case KipConfigValue_g_volt_1536000: return pretty ? "Mariko GPU Volt 1536 MHz" : "g_volt_1536000"; + + // Erista GPU voltages (27) + case KipConfigValue_g_volt_e_76800: return pretty ? "Erista GPU Volt 76 MHz" : "g_volt_e_76800"; + case KipConfigValue_g_volt_e_115200: return pretty ? "Erista GPU Volt 115 MHz" : "g_volt_e_115200"; + case KipConfigValue_g_volt_e_153600: return pretty ? "Erista GPU Volt 153 MHz" : "g_volt_e_153600"; + case KipConfigValue_g_volt_e_192000: return pretty ? "Erista GPU Volt 192 MHz" : "g_volt_e_192000"; + case KipConfigValue_g_volt_e_230400: return pretty ? "Erista GPU Volt 230 MHz" : "g_volt_e_230400"; + case KipConfigValue_g_volt_e_268800: return pretty ? "Erista GPU Volt 268 MHz" : "g_volt_e_268800"; + case KipConfigValue_g_volt_e_307200: return pretty ? "Erista GPU Volt 307 MHz" : "g_volt_e_307200"; + case KipConfigValue_g_volt_e_345600: return pretty ? "Erista GPU Volt 345 MHz" : "g_volt_e_345600"; + case KipConfigValue_g_volt_e_384000: return pretty ? "Erista GPU Volt 384 MHz" : "g_volt_e_384000"; + case KipConfigValue_g_volt_e_422400: return pretty ? "Erista GPU Volt 422 MHz" : "g_volt_e_422400"; + case KipConfigValue_g_volt_e_460800: return pretty ? "Erista GPU Volt 460 MHz" : "g_volt_e_460800"; + case KipConfigValue_g_volt_e_499200: return pretty ? "Erista GPU Volt 499 MHz" : "g_volt_e_499200"; + case KipConfigValue_g_volt_e_537600: return pretty ? "Erista GPU Volt 537 MHz" : "g_volt_e_537600"; + case KipConfigValue_g_volt_e_576000: return pretty ? "Erista GPU Volt 576 MHz" : "g_volt_e_576000"; + case KipConfigValue_g_volt_e_614400: return pretty ? "Erista GPU Volt 614 MHz" : "g_volt_e_614400"; + case KipConfigValue_g_volt_e_652800: return pretty ? "Erista GPU Volt 652 MHz" : "g_volt_e_652800"; + case KipConfigValue_g_volt_e_691200: return pretty ? "Erista GPU Volt 691 MHz" : "g_volt_e_691200"; + case KipConfigValue_g_volt_e_729600: return pretty ? "Erista GPU Volt 729 MHz" : "g_volt_e_729600"; + case KipConfigValue_g_volt_e_768000: return pretty ? "Erista GPU Volt 768 MHz" : "g_volt_e_768000"; + case KipConfigValue_g_volt_e_806400: return pretty ? "Erista GPU Volt 806 MHz" : "g_volt_e_806400"; + case KipConfigValue_g_volt_e_844800: return pretty ? "Erista GPU Volt 844 MHz" : "g_volt_e_844800"; + case KipConfigValue_g_volt_e_883200: return pretty ? "Erista GPU Volt 883 MHz" : "g_volt_e_883200"; + case KipConfigValue_g_volt_e_921600: return pretty ? "Erista GPU Volt 921 MHz" : "g_volt_e_921600"; + case KipConfigValue_g_volt_e_960000: return pretty ? "Erista GPU Volt 960 MHz" : "g_volt_e_960000"; + 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_t7_tWTR_fine_tune: return pretty ? "t7 - tWTR Fine Tune" : "t7_tWTR_fine_tune"; + case KipCrc32: + return pretty ? "CRC32" : "crc32"; + case HocClkConfigValue_IsFirstLoad: + return pretty ? "Is First Load" : "is_first_load"; + default: + return pretty ? "[cfg] no enum format string" : "err_no_format_string"; + } +} + +static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val) +{ + switch(val) + { + case SysClkConfigValue_PollingIntervalMs: + return 300ULL; + case SysClkConfigValue_TempLogIntervalMs: + case SysClkConfigValue_FreqLogIntervalMs: + case SysClkConfigValue_PowerLogIntervalMs: + case SysClkConfigValue_CsvWriteIntervalMs: + case HocClkConfigValue_UncappedClocks: + case HocClkConfigValue_OverwriteBoostMode: + case HorizonOCConfigValue_BatteryChargeCurrent: + case HorizonOCConfigValue_OverwriteRefreshRate: + case HorizonOCConfigValue_EnableUnsafeDisplayFreqs: + case HorizonOCConfigValue_GPUScheduling: + case HorizonOCConfigValue_LiveCpuUv: + case HorizonOCConfigValue_GPUSchedulingMethod: + return 0ULL; + case HocClkConfigValue_EristaMaxCpuClock: + return 1785ULL; + + case HocClkConfigValue_MarikoMaxCpuClock: + return 1963ULL; + + case HocClkConfigValue_ThermalThrottle: + case HocClkConfigValue_HandheldTDP: + case HocClkConfigValue_IsFirstLoad: + case HorizonOCConfigValue_DVFSMode: + return 1ULL; + case HocClkConfigValue_ThermalThrottleThreshold: + return 70ULL; + case HocClkConfigValue_HandheldTDPLimit: + return 9600ULL; // 8600mW will trigger on erista stock, so raise it a bit + case HocClkConfigValue_LiteTDPLimit: + return 6400ULL; // 0.5C + case HorizonOCConfigValue_CpuGovernorMinimumFreq: + return 612ULL; // 612MHz + default: + return 0ULL; + } +} + +static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t input) +{ + switch(val) + { + case HocClkConfigValue_EristaMaxCpuClock: + case HocClkConfigValue_MarikoMaxCpuClock: + case HocClkConfigValue_ThermalThrottleThreshold: + case HocClkConfigValue_HandheldTDPLimit: + case HocClkConfigValue_LiteTDPLimit: + case SysClkConfigValue_PollingIntervalMs: + return input > 0; + + case SysClkConfigValue_TempLogIntervalMs: + case SysClkConfigValue_FreqLogIntervalMs: + case SysClkConfigValue_PowerLogIntervalMs: + case SysClkConfigValue_CsvWriteIntervalMs: + case HocClkConfigValue_UncappedClocks: + case HocClkConfigValue_OverwriteBoostMode: + case HocClkConfigValue_ThermalThrottle: + case HocClkConfigValue_HandheldTDP: + case HorizonOCConfigValue_OverwriteRefreshRate: + case HorizonOCConfigValue_EnableUnsafeDisplayFreqs: + case HocClkConfigValue_IsFirstLoad: + case HorizonOCConfigValue_EnableExperimentalSettings: + case HorizonOCConfigValue_LiveCpuUv: + case HorizonOCConfigValue_GPUSchedulingMethod: + return (input & 0x1) == input; + + case KipConfigValue_custRev: + // case KipConfigValue_mtcConf: + case KipConfigValue_hpMode: + case KipConfigValue_commonEmcMemVolt: + case KipConfigValue_eristaEmcMaxClock: + case KipConfigValue_eristaEmcMaxClock1: + case KipConfigValue_eristaEmcMaxClock2: + case KipConfigValue_marikoEmcMaxClock: + case KipConfigValue_marikoEmcVddqVolt: + case KipConfigValue_emcDvbShift: + case KipConfigValue_t1_tRCD: + case KipConfigValue_t2_tRP: + case KipConfigValue_t3_tRAS: + case KipConfigValue_t4_tRRD: + case KipConfigValue_t5_tRFC: + case KipConfigValue_t6_tRTW: + case KipConfigValue_t7_tWTR: + case KipConfigValue_t8_tREFI: + case KipConfigValue_mem_burst_read_latency: + case KipConfigValue_mem_burst_write_latency: + case KipConfigValue_eristaCpuUV: + case KipConfigValue_eristaCpuMaxVolt: + case KipConfigValue_marikoCpuUVLow: + case KipConfigValue_marikoCpuUVHigh: + case KipConfigValue_tableConf: + case KipConfigValue_marikoCpuLowVmin: + case KipConfigValue_marikoCpuHighVmin: + case KipConfigValue_marikoCpuMaxVolt: + case KipConfigValue_eristaCpuBoostClock: + case KipConfigValue_marikoCpuBoostClock: + case KipConfigValue_marikoCpuMaxClock: + case KipConfigValue_eristaGpuUV: + case KipConfigValue_eristaGpuVmin: + case KipConfigValue_marikoGpuUV: + case KipConfigValue_marikoGpuVmin: + case KipConfigValue_marikoGpuVmax: + case KipConfigValue_commonGpuVoltOffset: + case KipConfigValue_gpuSpeedo: + case KipConfigValue_g_volt_76800: + case KipConfigValue_g_volt_153600: + case KipConfigValue_g_volt_230400: + case KipConfigValue_g_volt_307200: + case KipConfigValue_g_volt_384000: + case KipConfigValue_g_volt_460800: + case KipConfigValue_g_volt_537600: + case KipConfigValue_g_volt_614400: + case KipConfigValue_g_volt_691200: + case KipConfigValue_g_volt_768000: + case KipConfigValue_g_volt_844800: + case KipConfigValue_g_volt_921600: + case KipConfigValue_g_volt_998400: + case KipConfigValue_g_volt_1075200: + case KipConfigValue_g_volt_1152000: + case KipConfigValue_g_volt_1228800: + case KipConfigValue_g_volt_1267200: + case KipConfigValue_g_volt_1305600: + case KipConfigValue_g_volt_1344000: + case KipConfigValue_g_volt_1382400: + case KipConfigValue_g_volt_1420800: + case KipConfigValue_g_volt_1459200: + case KipConfigValue_g_volt_1497600: + case KipConfigValue_g_volt_1536000: + case KipConfigValue_g_volt_e_76800: + case KipConfigValue_g_volt_e_115200: + case KipConfigValue_g_volt_e_153600: + case KipConfigValue_g_volt_e_192000: + case KipConfigValue_g_volt_e_230400: + case KipConfigValue_g_volt_e_268800: + case KipConfigValue_g_volt_e_307200: + case KipConfigValue_g_volt_e_345600: + case KipConfigValue_g_volt_e_384000: + case KipConfigValue_g_volt_e_422400: + case KipConfigValue_g_volt_e_460800: + case KipConfigValue_g_volt_e_499200: + case KipConfigValue_g_volt_e_537600: + case KipConfigValue_g_volt_e_576000: + case KipConfigValue_g_volt_e_614400: + case KipConfigValue_g_volt_e_652800: + case KipConfigValue_g_volt_e_691200: + case KipConfigValue_g_volt_e_729600: + case KipConfigValue_g_volt_e_768000: + case KipConfigValue_g_volt_e_806400: + case KipConfigValue_g_volt_e_844800: + case KipConfigValue_g_volt_e_883200: + case KipConfigValue_g_volt_e_921600: + case KipConfigValue_g_volt_e_960000: + case KipConfigValue_g_volt_e_998400: + case KipConfigValue_g_volt_e_1036800: + case KipConfigValue_g_volt_e_1075200: + case KipConfigValue_eristaCpuVmin: + case KipConfigValue_eristaCpuUnlock: + case KipConfigValue_t6_tRTW_fine_tune: + case KipConfigValue_t7_tWTR_fine_tune: + case KipCrc32: + case HorizonOCConfigValue_DVFSMode: + case HorizonOCConfigValue_DVFSOffset: + case HorizonOCConfigValue_GPUScheduling: + case HorizonOCConfigValue_RAMVoltUsageDisplayMode: + case HorizonOCConfigValue_CpuGovernorMinimumFreq: + return true; + case HorizonOCConfigValue_BatteryChargeCurrent: + return ((input >= 1024) && (input <= 3072)) || !input; + default: + return false; + } +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/errors.h b/Source/rewrite-hoc-clk/common/include/sysclk/errors.h new file mode 100644 index 00000000..a50f1920 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/errors.h @@ -0,0 +1,39 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#define SYSCLK_ERROR_MODULE 388 +#define SYSCLK_ERROR(desc) ((SYSCLK_ERROR_MODULE & 0x1FF) | (SysClkError_##desc & 0x1FFF)<<9) + +typedef enum +{ + SysClkError_Generic = 0, + SysClkError_ConfigNotLoaded = 1, + SysClkError_ConfigSaveFailed = 2, + // HocClkError_SocThermFail = 3, +} SysClkError; diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/ipc.h b/Source/rewrite-hoc-clk/common/include/sysclk/ipc.h new file mode 100644 index 00000000..6f0f6146 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/ipc.h @@ -0,0 +1,72 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include +#include "board.h" +#include "clock_manager.h" + +#define SYSCLK_IPC_API_VERSION 1 +#define SYSCLK_IPC_SERVICE_NAME "hoc:clk" + +enum SysClkIpcCmd +{ + SysClkIpcCmd_GetApiVersion = 0, + SysClkIpcCmd_GetVersionString = 1, + SysClkIpcCmd_GetCurrentContext = 2, + SysClkIpcCmd_Exit = 3, + SysClkIpcCmd_GetProfileCount = 4, + SysClkIpcCmd_GetProfiles = 5, + SysClkIpcCmd_SetProfiles = 6, + SysClkIpcCmd_SetEnabled = 7, + SysClkIpcCmd_SetOverride = 8, + SysClkIpcCmd_GetConfigValues = 9, + SysClkIpcCmd_SetConfigValues = 10, + SysClkIpcCmd_GetFreqList = 11, + HocClkIpcCmd_SetKipData = 12, + HocClkIpcCmd_GetKipData = 13, +}; + + +typedef struct +{ + uint64_t tid; + SysClkTitleProfileList profiles; +} SysClkIpc_SetProfiles_Args; + +typedef struct +{ + SysClkModule module; + uint32_t hz; +} SysClkIpc_SetOverride_Args; + +typedef struct +{ + SysClkModule module; + uint32_t maxCount; +} SysClkIpc_GetFreqList_Args; \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/include/sysclk/psm_ext.h b/Source/rewrite-hoc-clk/common/include/sysclk/psm_ext.h new file mode 100644 index 00000000..8ba88c8a --- /dev/null +++ b/Source/rewrite-hoc-clk/common/include/sysclk/psm_ext.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) KazushiMe + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#pragma once + +#include + +typedef enum { + PsmPDC_NewPDO = 1, //Received new Power Data Object + PsmPDC_NoPD = 2, //No Power Delivery source is detected + PsmPDC_AcceptedRDO = 3 //Received and accepted Request Data Object +} PsmChargeInfoPDC; //BM92T series + +typedef enum { + PsmPowerRole_Sink = 1, + PsmPowerRole_Source = 2 +} PsmPowerRole; + +const char* PsmPowerRoleToStr(PsmPowerRole role); + +typedef enum { + PsmInfoChargerType_None = 0, + PsmInfoChargerType_PD = 1, + PsmInfoChargerType_TypeC_1500mA = 2, + PsmInfoChargerType_TypeC_3000mA = 3, + PsmInfoChargerType_DCP = 4, + PsmInfoChargerType_CDP = 5, + PsmInfoChargerType_SDP = 6, + PsmInfoChargerType_Apple_500mA = 7, + PsmInfoChargerType_Apple_1000mA = 8, + PsmInfoChargerType_Apple_2000mA = 9 +} PsmInfoChargerType; + +const char* PsmInfoChargerTypeToStr(PsmInfoChargerType type); + +typedef enum { + PsmFlags_NoHub = BIT(0), //If hub is disconnected + PsmFlags_Rail = BIT(8), //At least one Joy-con is charging from rail + PsmFlags_SPDSRC = BIT(12), //OTG + PsmFlags_ACC = BIT(16) //Accessory +} PsmChargeInfoFlags; + +typedef struct { + int32_t InputCurrentLimit; //Input (Sink) current limit in mA + int32_t VBUSCurrentLimit; //Output (Source/VBUS/OTG) current limit in mA + int32_t ChargeCurrentLimit; //Battery charging current limit in mA (512mA when Docked, 768mA when BatteryTemperature < 17.0 C) + int32_t ChargeVoltageLimit; //Battery charging voltage limit in mV (3952mV when BatteryTemperature >= 51.0 C) + int32_t unk_x10; //Possibly an emum, getting the same value as PowerRole in all tested cases + int32_t unk_x14; //Possibly flags + PsmChargeInfoPDC PDCState; //Power Delivery Controller State + int32_t BatteryTemperature; //Battery temperature in milli C + int32_t RawBatteryCharge; //Raw battery charged capacity per cent-mille (i.e. 100% = 100000 pcm) + int32_t VoltageAvg; //Voltage avg in mV (more in Notes) + int32_t BatteryAge; //Battery age (capacity full / capacity design) per cent-mille (i.e. 100% = 100000 pcm) + PsmPowerRole PowerRole; + PsmInfoChargerType ChargerType; + int32_t ChargerVoltageLimit; //Charger and external device voltage limit in mV + int32_t ChargerCurrentLimit; //Charger and external device current limit in mA + PsmChargeInfoFlags Flags; //Unknown flags +} PsmChargeInfo; + +typedef enum { + Psm_EnableBatteryCharging = 2, + Psm_DisableBatteryCharging = 3, + Psm_EnableFastBatteryCharging = 10, + Psm_DisableFastBatteryCharging = 11, + Psm_GetBatteryChargeInfoFields = 17, +} IPsmServerCmd; + +bool PsmIsChargerConnected(const PsmChargeInfo* info); +bool PsmIsCharging(const PsmChargeInfo* info); + +typedef enum { + PsmBatteryState_Discharging, + PsmBatteryState_ChargingPaused, + PsmBatteryState_FastCharging +} PsmBatteryState; + +PsmBatteryState PsmGetBatteryState(const PsmChargeInfo* info); +const char* PsmGetBatteryStateIcon(const PsmChargeInfo* info); \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/src/apm_profile_table.c b/Source/rewrite-hoc-clk/common/src/apm_profile_table.c new file mode 100644 index 00000000..41e4e520 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/src/apm_profile_table.c @@ -0,0 +1,49 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include + +SysClkApmConfiguration sysclk_g_apm_configurations[] = { + {0x00010000, 1020000000, 384000000, 1600000000}, + {0x00010001, 1020000000, 768000000, 1600000000}, + {0x00010002, 1224000000, 691200000, 1600000000}, + {0x00020000, 1020000000, 230400000, 1600000000}, + {0x00020001, 1020000000, 307200000, 1600000000}, + {0x00020002, 1224000000, 230400000, 1600000000}, + {0x00020003, 1020000000, 307200000, 1331200000}, + {0x00020004, 1020000000, 384000000, 1331200000}, + {0x00020005, 1020000000, 307200000, 1065600000}, + {0x00020006, 1020000000, 384000000, 1065600000}, + {0x92220007, 1020000000, 460800000, 1600000000}, + {0x92220008, 1020000000, 460800000, 1331200000}, + {0x92220009, 1785000000, 76800000, 1600000000}, + {0x9222000A, 1785000000, 76800000, 1331200000}, + {0x9222000B, 1020000000, 76800000, 1600000000}, + {0x9222000C, 1020000000, 76800000, 1331200000}, + {0, 0, 0, 0}, +}; + diff --git a/Source/rewrite-hoc-clk/common/src/client/ipc.c b/Source/rewrite-hoc-clk/common/src/client/ipc.c new file mode 100644 index 00000000..f81004f0 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/src/client/ipc.c @@ -0,0 +1,169 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include +#include +#include +#include + +static Service g_sysclkSrv; +static atomic_size_t g_refCnt; + +bool sysclkIpcRunning() +{ + Handle handle; + bool running = R_FAILED(smRegisterService(&handle, smEncodeName(SYSCLK_IPC_SERVICE_NAME), false, 1)); + + if (!running) + { + smUnregisterService(smEncodeName(SYSCLK_IPC_SERVICE_NAME)); + } + + return running; +} + +Result sysclkIpcInitialize(void) +{ + Result rc = 0; + + g_refCnt++; + + if (serviceIsActive(&g_sysclkSrv)) + return 0; + + rc = smGetService(&g_sysclkSrv, SYSCLK_IPC_SERVICE_NAME); + + if (R_FAILED(rc)) sysclkIpcExit(); + + return rc; +} + +void sysclkIpcExit(void) +{ + if (--g_refCnt == 0) + { + serviceClose(&g_sysclkSrv); + } +} + +Result sysclkIpcGetAPIVersion(u32* out_ver) +{ + return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetApiVersion, *out_ver); +} + +Result sysclkIpcGetVersionString(char* out, size_t len) +{ + return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString, + .buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out }, + .buffers = {{out, len}}, + ); +} + +Result sysclkIpcGetCurrentContext(SysClkContext* out_context) +{ + return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext, + .buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out }, + .buffers = {{out_context, sizeof(SysClkContext)}}, + ); +} + +Result sysclkIpcGetProfileCount(u64 tid, u8* out_count) +{ + return serviceDispatchInOut(&g_sysclkSrv, SysClkIpcCmd_GetProfileCount, tid, *out_count); +} + +Result sysclkIpcSetEnabled(bool enabled) +{ + u8 enabledRaw = (u8)enabled; + return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetEnabled, enabledRaw); +} + +Result sysclkIpcSetOverride(SysClkModule module, u32 hz) +{ + SysClkIpc_SetOverride_Args args = { + .module = module, + .hz = hz + }; + return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetOverride, args); +} + +Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles) +{ + return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid, + .buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out }, + .buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}}, + ); +} + +Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles) +{ + SysClkIpc_SetProfiles_Args args; + args.tid = tid; + memcpy(&args.profiles, profiles, sizeof(SysClkTitleProfileList)); + return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetProfiles, args); +} + +Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues) +{ + return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues, + .buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out }, + .buffers = {{out_configValues, sizeof(SysClkConfigValueList)}}, + ); +} + +Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues) +{ + return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues, + .buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In }, + .buffers = {{configValues, sizeof(SysClkConfigValueList)}}, + ); +} + +Result sysclkIpcGetFreqList(SysClkModule module, u32* list, u32 maxCount, u32* outCount) +{ + SysClkIpc_GetFreqList_Args args = { + .module = module, + .maxCount = maxCount + }; + return serviceDispatchInOut(&g_sysclkSrv, SysClkIpcCmd_GetFreqList, args, *outCount, + .buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out }, + .buffers = {{list, maxCount * sizeof(u32)}}, + ); +} + +Result hocClkIpcSetKipData() +{ + u32 temp = 0; + return serviceDispatchIn(&g_sysclkSrv, HocClkIpcCmd_SetKipData, temp); +} + +Result hocClkIpcGetKipData() +{ + u32 temp = 0; + return serviceDispatchIn(&g_sysclkSrv, HocClkIpcCmd_GetKipData, temp); +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/src/display_refresh_rate.cpp b/Source/rewrite-hoc-clk/common/src/display_refresh_rate.cpp new file mode 100644 index 00000000..7a169883 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/src/display_refresh_rate.cpp @@ -0,0 +1,720 @@ +/* + * Copyright (c) Souldbminer, based on reasearch by MasaGratoR and Cooler3D + * + * 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 . + * + */ + +#include "display_refresh_rate.h" +#include +#include +#include +#include +#define DSI_CLOCK_HZ 234000000llu +#define NVDISP_GET_MODE2 0x803C021B +#define NVDISP_SET_MODE2 0x403C021C +#define NVDISP_VALIDATE_MODE2 0xC03C021D +#define NVDISP_GET_MODE_DB2 0xEF20021E +#define NVDISP_GET_PANEL_DATA 0xC01C0226 + +#define MAX_REFRESH_RATE 72 + +static DisplayRefreshConfig g_config = {0}; +static bool g_initialized = false; + +static uint8_t g_dockedHighestRefreshRate = 60; +static uint8_t g_dockedLinkRate = 10; +static bool g_wasRetroSuperTurnedOff = false; +static uint32_t g_lastVActive = 1080; +static bool g_canChangeRefreshRateDocked = false; +static uint8_t g_lastVActiveSet = 0; + +static const uint8_t g_dockedRefreshRates[] = {40, 45, 50, 55, 60, 70, 72, 75, 80, 90, 95, 100, 110, 120, 130, 140, 144, 150, 160, 165, 170, 180, 190, 200, 210, 220, 230, 240}; +// Calculate with this tool: + +// https://tomverbeure.github.io/video_timings_calculator?horiz_pixels=1920&vert_pixels=1080&refresh_rate=240&margins=false&interlaced=false&bpc=8&color_fmt=rgb444&video_opt=false&custom_hblank=80&custom_vblank=6 + +/* +typedef struct { + uint16_t hFrontPorch; + uint8_t hSyncWidth; + uint8_t hBackPorch; + uint8_t vFrontPorch; + uint8_t vSyncWidth; + uint8_t vBackPorch; + uint8_t VIC; + uint32_t pixelClock_kHz; +} DockedTimings; +*/ +static const DockedTimings g_dockedTimings1080p[] = { + {8, 32, 40, 7, 8, 6, 0, 88080}, // 40Hz + {8, 32, 40, 9, 8, 6, 0, 99270}, // 45Hz + {528, 44, 148, 4, 5, 36, 31, 148500}, // 50Hz + {8, 32, 40, 15, 8, 6, 0, 121990}, // 55Hz + {88, 44, 148, 4, 5, 36, 16, 148500}, // 60Hz + {8, 32, 40, 22, 8, 6, 0, 156240}, // 70Hz + {8, 32, 40, 23, 8, 6, 0, 160848}, // 72Hz + {8, 32, 40, 25, 8, 6, 0, 167850}, // 75Hz + {8, 32, 40, 28, 8, 6, 0, 179520}, // 80Hz + {8, 32, 40, 33, 8, 6, 0, 202860}, // 90Hz + {8, 32, 40, 36, 8, 6, 0, 214700}, // 95Hz + {528, 44, 148, 4, 5, 36, 64, 297000}, // 100Hz + {8, 32, 40, 44, 8, 6, 0, 250360}, // 110Hz + {88, 44, 148, 4, 5, 36, 63, 297000}, // 120Hz + {8, 32, 40, 55, 8, 6, 0, 298750}, //130Hz CVT-RBv2 + {8, 32, 40, 61, 8, 6, 0, 323400}, //140Hz CVT-RBv2 + {8, 32, 40, 63, 8, 6, 0, 333216}, //144Hz CVT-RBv2 + {8, 32, 40, 67, 8, 6, 0, 348300}, //150Hz CVT-RBv2 + {8, 32, 40, 72, 8, 6, 0, 373120}, //160Hz CVT-RBv2 + {8, 32, 40, 75, 8, 6, 0, 385770}, //165Hz CVT-RBv2 + {8, 32, 40, 78, 8, 6, 0, 398480}, //170Hz CVT-RBv2 + {8, 32, 40, 84, 8, 6, 0, 424080}, //180Hz CVT-RBv2 + {8, 32, 40, 90, 8, 6, 0, 449920}, //190Hz CVT-RBv2 + {8, 32, 40, 96, 8, 6, 0, 476000}, //200Hz CVT-RBv2 + {8, 32, 40, 102, 8, 6, 0, 502320}, //210Hz CVT-RBv2 + {8, 32, 40, 108, 8, 6, 0, 528880}, //220Hz CVT-RBv2 + {8, 32, 40, 114, 8, 6, 0, 555680}, //230Hz CVT-RBv2 + {8, 32, 40, 121, 8, 6, 0, 583200}, //240Hz CVT-RBv2 + // technically you can go to 476hz, but in practice, why would you? +}; + +static const HandheldTimings g_handheldTimingsRETRO[] = { + {72, 136, 72, 1, 660, 9, 78000}, + {72, 136, 72, 1, 443, 9, 77985}, + {72, 136, 72, 1, 270, 9, 78000}, + {72, 136, 72, 1, 128, 9, 77990}, + {72, 136, 72, 1, 10, 9, 78000} +}; + +static const MinMaxRefreshRate g_handheldModeRefreshRate = {40, 80}; + +static uint8_t _getDockedRefreshRateIterator(uint32_t refreshRate) { + for (size_t i = 0; i < sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]); i++) { + if (g_dockedRefreshRates[i] == refreshRate) return i; + } + return 0xFF; +} + +static void _changeOledElvssSettings(const uint32_t* offsets, const uint32_t* value, uint32_t size, uint32_t start) { + if (!g_config.dsiVirtAddr || !value || !size) return; + + volatile uint32_t* dsi = (uint32_t*)g_config.dsiVirtAddr; + + #define DSI_VIDEO_MODE_CONTROL 0x4E + #define DSI_WR_DATA 0xA + #define DSI_TRIGGER 0x13 + #define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15 + #define MIPI_DSI_DCS_LONG_WRITE 0x39 + #define MIPI_DCS_PRIV_SM_SET_REG_OFFSET 0xB0 + #define MIPI_DCS_PRIV_SM_SET_ELVSS 0xB1 + + dsi[DSI_VIDEO_MODE_CONTROL] = true; + svcSleepThread(20000000); + + dsi[DSI_WR_DATA] = MIPI_DSI_DCS_LONG_WRITE | (5 << 8); + dsi[DSI_WR_DATA] = 0x5A5A5AE2; + dsi[DSI_WR_DATA] = 0x5A; + dsi[DSI_TRIGGER] = 0; + + for (size_t i = start; i < size; i++) { + dsi[DSI_WR_DATA] = ((MIPI_DCS_PRIV_SM_SET_REG_OFFSET | ((offsets[i] % 0x100) << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM; + dsi[DSI_TRIGGER] = 0; + dsi[DSI_WR_DATA] = ((MIPI_DCS_PRIV_SM_SET_ELVSS | (value[i] << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM; + dsi[DSI_TRIGGER] = 0; + } + + dsi[DSI_WR_DATA] = MIPI_DSI_DCS_LONG_WRITE | (5 << 8); + dsi[DSI_WR_DATA] = 0xA55A5AE2; + dsi[DSI_WR_DATA] = 0xA5; + dsi[DSI_TRIGGER] = 0; + + dsi[DSI_VIDEO_MODE_CONTROL] = false; + svcSleepThread(20000000); +} +void DisplayRefresh_SetDockedState(bool isDocked) { + g_config.isDocked = isDocked; +} + +bool DisplayRefresh_Initialize(const DisplayRefreshConfig* config) { + if (!config) return false; + + g_config = *config; + g_initialized = true; + return true; +} + +void DisplayRefresh_CorrectOledGamma(uint32_t refresh_rate) { + static uint32_t last_refresh_rate = 60; + static int counter = 0; + + if (g_config.isDocked || refresh_rate < 45 || refresh_rate > 60) { + last_refresh_rate = 60; + return; + } + + if (counter != 9) { + counter++; + return; + } + counter = 0; + + uint32_t offsets[] = {0x1A, 0x24, 0x25}; + uint32_t values[] = {2, 0, 0x83}; + + if (refresh_rate == 60) { + if (last_refresh_rate == 60) return; + } else if (refresh_rate == 45) { + if (last_refresh_rate == 45) return; + uint32_t vals[] = {4, 1, 0}; + memcpy(values, vals, sizeof(vals)); + } else if (refresh_rate == 50) { + if (last_refresh_rate == 50) return; + uint32_t vals[] = {3, 1, 0}; + memcpy(values, vals, sizeof(vals)); + } else if (refresh_rate == 55) { + if (last_refresh_rate == 55) return; + uint32_t vals[] = {3, 1, 0}; + memcpy(values, vals, sizeof(vals)); + } else { + return; + } + + for (int i = 0; i < 5; i++) { + _changeOledElvssSettings(offsets, values, 3, 0); + } + last_refresh_rate = refresh_rate; +} + +void DisplayRefresh_SetAllowedDockedRatesIPC(uint32_t refreshRates, bool is720p) { + // Function kept for API compatibility but does nothing + (void)refreshRates; + (void)is720p; +} + +uint8_t DisplayRefresh_GetDockedHighestAllowed(void) { + return (g_dockedHighestRefreshRate > 60) ? g_dockedHighestRefreshRate : 60; +} + +static void _getDockedHighestRefreshRate(uint32_t fd_in) { + uint8_t highestRefreshRate = 60; + uint32_t fd = fd_in; + + if(!fd) nvOpen(&fd, "/dev/nvdisp-disp1"); + NvdcModeDB2 db2 = {0}; + int rc = nvIoctl(fd, NVDISP_GET_MODE_DB2, &db2); + + if (rc == 0) { + for (size_t i = 0; i < db2.num_modes; i++) { + if (db2.modes[i].hActive < 1920 || db2.modes[i].vActive < 1080) + continue; + + uint32_t v_total = db2.modes[i].vActive + db2.modes[i].vSyncWidth + db2.modes[i].vFrontPorch + db2.modes[i].vBackPorch; + uint32_t h_total = db2.modes[i].hActive + db2.modes[i].hSyncWidth + db2.modes[i].hFrontPorch + db2.modes[i].hBackPorch; + double refreshRate = round((double)(db2.modes[i].pclkKHz * 1000) / (double)(v_total * h_total)); + + if (highestRefreshRate < (uint8_t)refreshRate) + highestRefreshRate = (uint8_t)refreshRate; + } + } else { + g_dockedHighestRefreshRate = 60; + } + + const size_t numRates = sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]); + if (highestRefreshRate > g_dockedRefreshRates[numRates - 1]) + highestRefreshRate = g_dockedRefreshRates[numRates - 1]; + + NvdcMode2 display_b = {0}; + rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b); + + struct dpaux_read_0x100 { + uint32_t cmd; + uint32_t addr; + uint32_t size; + struct { + unsigned char link_rate; + unsigned int lane_count: 5; + unsigned int unk1: 2; + unsigned int isFramingEnhanced: 1; + unsigned char downspread; + unsigned char training_pattern; + unsigned char lane_pattern[4]; + unsigned char unk2[8]; + } set; + } dpaux = {6, 0x100, 0x10}; + + rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux); + if (rc == 0) { + g_dockedLinkRate = dpaux.set.link_rate; + // if (display_b.hActive == 1920 && display_b.vActive == 1080 && highestRefreshRate > 75 && dpaux.set.link_rate < 20 && ) + // highestRefreshRate = 75; + } + + if (!fd_in) nvClose(fd); + g_dockedHighestRefreshRate = highestRefreshRate; +} + +static bool _setPLLDHandheldRefreshRate(uint32_t new_refreshRate) { + if (!g_config.clkVirtAddr) return false; + + uint32_t fd = 0; + if (nvOpen(&fd, "/dev/nvdisp-disp0")) { + return false; + } + + struct dpaux_read { + uint32_t cmd; + uint32_t addr; + uint32_t size; + struct { + unsigned int rev_minor : 4; + unsigned int rev_major : 4; + unsigned char link_rate; + unsigned int lane_count: 5; + unsigned int unk1: 2; + unsigned int isFramingEnhanced: 1; + unsigned char unk2[13]; + } DPCD; + } dpaux = {6, 0, 0x10}; + + int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux); + nvClose(fd); + if (rc != 0x75c) return false; + + PLLD_BASE base = {0}; + PLLD_MISC misc = {0}; + memcpy(&base, (void*)(g_config.clkVirtAddr + 0xD0), 4); + memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4); + + uint32_t value = ((base.PLLD_DIVN / base.PLLD_DIVM) * 10) / 4; + if (value == 0 || value == 80) return false; + + if (new_refreshRate > g_handheldModeRefreshRate.max) { + new_refreshRate = g_handheldModeRefreshRate.max; + } else if (new_refreshRate < g_handheldModeRefreshRate.min) { + bool skip = false; + for (size_t i = 2; i <= 4; i++) { + if (new_refreshRate * i == 60) { + skip = true; + new_refreshRate = 60; + break; + } + } + if (!skip) { + for (size_t i = 2; i <= 4; i++) { + if (((new_refreshRate * i) >= g_handheldModeRefreshRate.min) && ((new_refreshRate * i) <= g_handheldModeRefreshRate.max)) { + skip = true; + new_refreshRate *= i; + break; + } + } + } + if (!skip) new_refreshRate = 60; + } + + uint32_t pixelClock = (9375 * ((4096 * ((2 * base.PLLD_DIVN) + 1)) + misc.PLLD_SDM_DIN)) / (8 * base.PLLD_DIVM); + uint16_t refreshRateNow = pixelClock / (DSI_CLOCK_HZ / 60); + + if (refreshRateNow == new_refreshRate) { + return true; + } + + uint8_t base_refreshRate = new_refreshRate - (new_refreshRate % 5); + base.PLLD_DIVN = (4 * base_refreshRate) / 10; + base.PLLD_DIVM = 1; + + uint64_t expected_pixel_clock = (DSI_CLOCK_HZ * new_refreshRate) / 60; + misc.PLLD_SDM_DIN = ((8 * base.PLLD_DIVM * expected_pixel_clock) / 9375) - (4096 * ((2 * base.PLLD_DIVN) + 1)); + + memcpy((void*)(g_config.clkVirtAddr + 0xD0), &base, 4); + memcpy((void*)(g_config.clkVirtAddr + 0xDC), &misc, 4); + return true; +} + +static bool _setNvDispDockedRefreshRate(uint32_t new_refreshRate) { + if (g_config.isLite || !g_canChangeRefreshRateDocked) + return false; + + uint32_t fd = 0; + if (nvOpen(&fd, "/dev/nvdisp-disp1")) { + return false; + } + + NvdcMode2 display_b = {0}; + int rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b); + if (rc != 0) { + nvClose(fd); + return false; + } + + if (!display_b.pclkKHz) { + nvClose(fd); + return false; + } + + if (!((display_b.vActive == 480 && display_b.hActive == 720) || + (display_b.vActive == 720 && display_b.hActive == 1280) || + (display_b.vActive == 1080 && display_b.hActive == 1920))) { + nvClose(fd); + return false; + } + + if (display_b.vActive != g_lastVActiveSet) { + g_lastVActiveSet = display_b.vActive; + } + + uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch; + uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch; + uint32_t refreshRateNow = ((display_b.pclkKHz) * 1000 + 999) / (h_total * v_total); + + int8_t itr = -1; + const size_t numRates = sizeof(g_dockedRefreshRates) / sizeof(g_dockedRefreshRates[0]); + + // Find closest matching refresh rate + if ((new_refreshRate <= 60) && ((60 % new_refreshRate) == 0)) { + itr = _getDockedRefreshRateIterator(60); + } + + if (itr == -1) { + for (size_t i = 0; i < numRates; i++) { + uint8_t val = g_dockedRefreshRates[i]; + if ((val % new_refreshRate) == 0) { + itr = i; + break; + } + } + } + + if (itr == -1) { + if (!g_config.matchLowestDocked) { + itr = _getDockedRefreshRateIterator(60); + } else { + for (size_t i = 0; i < numRates; i++) { + if (new_refreshRate < g_dockedRefreshRates[i]) { + itr = i; + break; + } + } + } + } + + if (itr == -1) itr = _getDockedRefreshRateIterator(60); + + // Clamp to highest allowed refresh rate + if (g_dockedRefreshRates[itr] > g_dockedHighestRefreshRate) { + for (int8_t i = itr; i >= 0; i--) { + if (g_dockedRefreshRates[i] <= g_dockedHighestRefreshRate) { + itr = i; + break; + } + } + } + + if (refreshRateNow == g_dockedRefreshRates[itr]) { + nvClose(fd); + return true; + } + + if (itr >= 0 && itr < (int8_t)numRates) { + if (display_b.vActive == 720) { + uint32_t clock = ((h_total * v_total) * g_dockedRefreshRates[itr]) / 1000; + display_b.pclkKHz = clock; + } else { + display_b.hFrontPorch = g_dockedTimings1080p[itr].hFrontPorch; + display_b.hSyncWidth = g_dockedTimings1080p[itr].hSyncWidth; + display_b.hBackPorch = g_dockedTimings1080p[itr].hBackPorch; + display_b.vFrontPorch = g_dockedTimings1080p[itr].vFrontPorch; + display_b.vSyncWidth = g_dockedTimings1080p[itr].vSyncWidth; + display_b.vBackPorch = g_dockedTimings1080p[itr].vBackPorch; + display_b.pclkKHz = g_dockedTimings1080p[itr].pixelClock_kHz; + display_b.vmode = (g_dockedRefreshRates[itr] >= 100 ? 0x400000 : 0x200000); + display_b.unk1 = (g_dockedRefreshRates[itr] >= 100 ? 0x80 : 0); + display_b.sync = 3; + display_b.bitsPerPixel = 24; + } + + rc = nvIoctl(fd, NVDISP_VALIDATE_MODE2, &display_b); + if (rc == 0) { + rc = nvIoctl(fd, NVDISP_SET_MODE2, &display_b); + } + } + + nvClose(fd); + return true; +} + +static bool _setNvDispHandheldRefreshRate(uint32_t new_refreshRate) { + if (!g_config.isRetroSUPER) return false; + + if (!g_config.displaySync) { + g_wasRetroSuperTurnedOff = false; + } else if (g_wasRetroSuperTurnedOff) { + svcSleepThread(2000000000); + g_wasRetroSuperTurnedOff = false; + } + + svcSleepThread(1000000000); + + uint32_t fd = 0; + if (nvOpen(&fd, "/dev/nvdisp-disp0")) { + return false; + } + + NvdcMode2 display_b = {0}; + int rc = nvIoctl(fd, NVDISP_GET_MODE2, &display_b); + if (rc != 0) { + nvClose(fd); + return false; + } + + if (!display_b.pclkKHz) { + nvClose(fd); + return false; + } + + if ((display_b.vActive == 1280 && display_b.hActive == 720) == false) { + nvClose(fd); + return false; + } + + uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch; + uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch; + uint32_t refreshRateNow = ((display_b.pclkKHz) * 1000 + 999) / (h_total * v_total); + + if (new_refreshRate > g_handheldModeRefreshRate.max) { + new_refreshRate = g_handheldModeRefreshRate.max; + } else if (new_refreshRate < g_handheldModeRefreshRate.min) { + bool skip = false; + for (size_t i = 2; i <= 4; i++) { + if (new_refreshRate * i == 60) { + skip = true; + new_refreshRate = 60; + break; + } + } + if (!skip) { + for (size_t i = 2; i <= (sizeof(g_handheldTimingsRETRO) / sizeof(g_handheldTimingsRETRO[0])); i++) { + if (((new_refreshRate * i) >= g_handheldModeRefreshRate.min) && ((new_refreshRate * i) <= g_handheldModeRefreshRate.max)) { + skip = true; + new_refreshRate *= i; + break; + } + } + } + if (!skip) new_refreshRate = 60; + } + + if (new_refreshRate == refreshRateNow) { + nvClose(fd); + return true; + } + + uint32_t itr = (new_refreshRate - 40) / 5; + display_b.hFrontPorch = g_handheldTimingsRETRO[itr].hFrontPorch; + display_b.hSyncWidth = g_handheldTimingsRETRO[itr].hSyncWidth; + display_b.hBackPorch = g_handheldTimingsRETRO[itr].hBackPorch; + display_b.vFrontPorch = g_handheldTimingsRETRO[itr].vFrontPorch; + display_b.vSyncWidth = g_handheldTimingsRETRO[itr].vSyncWidth; + display_b.vBackPorch = g_handheldTimingsRETRO[itr].vBackPorch; + display_b.pclkKHz = g_handheldTimingsRETRO[itr].pixelClock_kHz; + + rc = nvIoctl(fd, NVDISP_VALIDATE_MODE2, &display_b); + if (rc == 0) { + for (size_t i = 0; i < 5; i++) { + nvIoctl(fd, NVDISP_SET_MODE2, &display_b); + } + } + + nvClose(fd); + return true; +} + +bool DisplayRefresh_SetRate(uint32_t new_refreshRate) { + if (!new_refreshRate || !g_initialized) return false; + + uint32_t fd = 0; + + if (g_config.isLite && g_config.isPossiblySpoofedRetro) { + g_config.isRetroSUPER = false; // Would check flag file here in original, but i dont care lol + } + + if (g_config.isRetroSUPER && !g_config.isDocked) { + return _setNvDispHandheldRefreshRate(new_refreshRate); + } + + else if ((!g_config.isRetroSUPER && g_config.isLite) || R_FAILED(nvOpen(&fd, "/dev/nvdisp-disp1"))) { + if (_setPLLDHandheldRefreshRate(new_refreshRate) == false) + return false; + } + else { + struct dpaux_read { + uint32_t cmd; + uint32_t addr; + uint32_t size; + struct { + unsigned int rev_minor : 4; + unsigned int rev_major : 4; + unsigned char link_rate; + unsigned int lane_count: 5; + unsigned int unk1: 2; + unsigned int isFramingEnhanced: 1; + unsigned char unk2[13]; + } DPCD; + } dpaux = {6, 0, 0x10}; + + int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux); + nvClose(fd); + + if (rc != 0) { + if (!g_config.isRetroSUPER) { + return _setPLLDHandheldRefreshRate(new_refreshRate); + } else { + return _setNvDispHandheldRefreshRate(new_refreshRate); + } + } else { + if(g_config.isDocked) + return _setNvDispDockedRefreshRate(new_refreshRate); + else + return true; + } + } + return false; +} + +bool DisplayRefresh_GetRate(uint32_t* out_refreshRate, bool internal) { + if (!out_refreshRate || !g_initialized || !g_config.clkVirtAddr) return false; + static uint32_t value = 60; + + if (g_config.isRetroSUPER && !g_config.isDocked) { + uint32_t fd = 0; + PLLD_BASE temp = {0}; + PLLD_MISC misc = {0}; + memcpy(&temp, (void*)(g_config.clkVirtAddr + 0xD0), 4); + memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4); + + value = ((temp.PLLD_DIVN / temp.PLLD_DIVM) * 10) / 4; + + if (value != 0 && value != 80) { + if (!nvOpen(&fd, "/dev/nvdisp-disp0")) { + NvdcMode2 display_b = {0}; + if (nvIoctl(fd, NVDISP_GET_MODE2, &display_b) == 0) { + uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch; + uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch; + uint32_t pixelClock = display_b.pclkKHz * 1000 + 999; + value = pixelClock / (h_total * v_total); + } + nvClose(fd); + } else { + return false; + } + } else { + g_wasRetroSuperTurnedOff = true; + } + } + else if ((!g_config.isPossiblySpoofedRetro) || (g_config.isPossiblySpoofedRetro && !g_config.isRetroSUPER)) { + PLLD_BASE temp = {0}; + PLLD_MISC misc = {0}; + memcpy(&temp, (void*)(g_config.clkVirtAddr + 0xD0), 4); + memcpy(&misc, (void*)(g_config.clkVirtAddr + 0xDC), 4); + + value = ((temp.PLLD_DIVN / temp.PLLD_DIVM) * 10) / 4; + + if (value == 0 || value == 80) { + // Docked mode + if (g_config.isLite) return false; + + g_config.isDocked = true; + + if (!g_canChangeRefreshRateDocked) { + uint32_t fd = 0; + if (!nvOpen(&fd, "/dev/nvdisp-disp1")) { + struct dpaux_read_0x100 { + uint32_t cmd; + uint32_t addr; + uint32_t size; + struct { + unsigned char link_rate; + unsigned int lane_count: 5; + unsigned int unk1: 2; + unsigned int isFramingEnhanced: 1; + unsigned char downspread; + unsigned char training_pattern; + unsigned char lane_pattern[4]; + unsigned char unk2[8]; + } set; + } dpaux = {6, 0x100, 0x10}; + + int rc = nvIoctl(fd, NVDISP_GET_PANEL_DATA, &dpaux); + nvClose(fd); + + if (rc == 0) { + _getDockedHighestRefreshRate(0); + g_canChangeRefreshRateDocked = true; + } else { + svcSleepThread(1000000000); + return false; + } + } else { + return false; + } + } + if(internal) { + *out_refreshRate = value; + return true; + } + uint32_t fd = 0; + if (!nvOpen(&fd, "/dev/nvdisp-disp1")) { + NvdcMode2 display_b = {0}; + if (nvIoctl(fd, NVDISP_GET_MODE2, &display_b) == 0) { + if (!display_b.pclkKHz) { + nvClose(fd); + return false; + } + + if (g_lastVActive != display_b.vActive) { + g_lastVActive = display_b.vActive; + _getDockedHighestRefreshRate(fd); + } + + uint32_t h_total = display_b.hActive + display_b.hFrontPorch + display_b.hSyncWidth + display_b.hBackPorch; + uint32_t v_total = display_b.vActive + display_b.vFrontPorch + display_b.vSyncWidth + display_b.vBackPorch; + uint32_t pixelClock = display_b.pclkKHz * 1000 + 999; + value = pixelClock / (h_total * v_total); + } else { + value = 60; + } + nvClose(fd); + } else { + value = 60; + } + } + else if (!g_config.isRetroSUPER) { + // Handheld mode + g_config.isDocked = false; + g_canChangeRefreshRateDocked = false; + + uint32_t pixelClock = (9375 * ((4096 * ((2 * temp.PLLD_DIVN) + 1)) + misc.PLLD_SDM_DIN)) / (8 * temp.PLLD_DIVM); + value = pixelClock / (DSI_CLOCK_HZ / 60); + } + else { + return false; + } + } + + *out_refreshRate = value; + return true; +} + +void DisplayRefresh_Shutdown(void) { + g_initialized = false; + memset(&g_config, 0, sizeof(g_config)); +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/src/i2c.cpp b/Source/rewrite-hoc-clk/common/src/i2c.cpp new file mode 100644 index 00000000..3760efeb --- /dev/null +++ b/Source/rewrite-hoc-clk/common/src/i2c.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (c) KazushiMe + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#include "i2c.h" + +Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) { + // ams::fatal::srv::StopSoundTask::StopSound() + // I2C Bus Communication Reference: https://www.ti.com/lit/an/slva704/slva704.pdf + struct { + u8 reg; + u8 val; + } __attribute__((packed)) cmd; + + I2cSession _session; + Result res = i2cOpenSession(&_session, dev); + if (res) + return res; + + cmd.reg = reg; + cmd.val = val; + res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + i2csessionClose(&_session); + return res; +} + +Result I2cRead_OutU8(I2cDevice dev, u8 reg, u8 *out) { + struct { u8 reg; } __attribute__((packed)) cmd; + struct { u8 val; } __attribute__((packed)) rec; + + I2cSession _session; + Result res = i2cOpenSession(&_session, dev); + if (res) + return res; + + cmd.reg = reg; + res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + if (res) { + i2csessionClose(&_session); + return res; + } + + res = i2csessionReceiveAuto(&_session, &rec, sizeof(rec), I2cTransactionOption_All); + i2csessionClose(&_session); + if (res) { + return res; + } + + *out = rec.val; + return 0; +} + +Result I2cRead_OutU16(I2cDevice dev, u8 reg, u16 *out) { + struct { u8 reg; } __attribute__((packed)) cmd; + struct { u16 val; } __attribute__((packed)) rec; + + I2cSession _session; + Result res = i2cOpenSession(&_session, dev); + if (res) + return res; + + cmd.reg = reg; + res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + if (res) { + i2csessionClose(&_session); + return res; + } + + res = i2csessionReceiveAuto(&_session, &rec, sizeof(rec), I2cTransactionOption_All); + i2csessionClose(&_session); + if (res) { + return res; + } + + *out = rec.val; + return 0; +} + +float I2c_Max17050_GetBatteryCurrent() { + u16 val; + Result res = I2cRead_OutU16(I2cDevice_Max17050, MAX17050_CURRENT_REG, &val); + if (res) + return 0.f; + + const float SenseResistor = 5.; // in uOhm + const float CGain = 1.99993; + return (s16)val * (1.5625 / (SenseResistor * CGain)); +} + +u32 I2c_BuckConverter_MultiplierToMvOut(const I2c_BuckConverter_Domain* domain, u8 multiplier) { + return (domain->uv_min + domain->uv_step * multiplier) / 1000; +} + +u8 I2c_BuckConverter_MvOutToMultiplier(const I2c_BuckConverter_Domain* domain, u32 mvolt) { + u32 uvolt = mvolt * 1000; + if (uvolt < domain->uv_min) + uvolt = domain->uv_min; + if (uvolt > domain->uv_max) + uvolt = domain->uv_max; + + return (uvolt - domain->uv_min) / domain->uv_step; +} + +u32 I2c_BuckConverter_GetMvOut(const I2c_BuckConverter_Domain* domain) { + u8 val; + // Retry 5 times if received POR value + for (int i = 0; i < 5; i++) { + if (R_FAILED(I2cRead_OutU8(domain->device, domain->reg, &val))) + return 0u; + + // Wait 1us + svcSleepThread(1E3); + + if (!domain->por_val || val != domain->por_val) + break; + } + return I2c_BuckConverter_MultiplierToMvOut(domain, val & domain->volt_mask); +} + +Result I2c_BuckConverter_SetMvOut(const I2c_BuckConverter_Domain* domain, u32 mvolt) { + u8 val; + Result res = I2cRead_OutU8(domain->device, domain->reg, &val); + if (R_FAILED(res)) + return res; + + u8 multiplier = I2c_BuckConverter_MvOutToMultiplier(domain, mvolt); + val &= ~domain->volt_mask; + val |= multiplier & domain->volt_mask; + + res = I2cSet_U8(domain->device, domain->reg, val); + if (R_FAILED(res)) + return res; + + // 5ms Ramp delay + svcSleepThread(5E6); + u8 new_val; + res = I2cRead_OutU8(domain->device, domain->reg, &new_val); + if (R_FAILED(res)) + return res; + if (new_val != val) + return -1; + + return 0; +} + +u8 I2c_Bq24193_Convert_mA_Raw(u32 ma) { + // Adjustment is required + u8 raw = 0; + + if (ma > MA_RANGE_MAX) // capping + ma = MA_RANGE_MAX; + + bool pct20 = ma <= (MA_RANGE_MIN - 64); + if (pct20) { + ma = ma * 5; + raw |= 0x1; + } + + ma -= ma % 100; // round to 100 + ma -= (MA_RANGE_MIN - 64); // ceiling + raw |= (ma >> 6) << 2; + + return raw; +}; + +u32 I2c_Bq24193_Convert_Raw_mA(u8 raw) { + // No adjustment is allowed + u32 ma = (((raw >> 2)) << 6) + MA_RANGE_MIN; + + bool pct20 = raw & 1; + if (pct20) + ma = ma * 20 / 100; + + return ma; +}; + +Result I2c_Bq24193_GetFastChargeCurrentLimit(u32 *ma) { + u8 raw; + Result res = I2cRead_OutU8(I2cDevice_Bq24193, BQ24193_CHARGE_CURRENT_CONTROL_REG, &raw); + if (res) + return res; + + *ma = I2c_Bq24193_Convert_Raw_mA(raw); + return 0; +} + +Result I2c_Bq24193_SetFastChargeCurrentLimit(u32 ma) { + u8 raw = I2c_Bq24193_Convert_mA_Raw(ma); + return I2cSet_U8(I2cDevice_Bq24193, BQ24193_CHARGE_CURRENT_CONTROL_REG, raw); +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/src/memmem.c b/Source/rewrite-hoc-clk/common/src/memmem.c new file mode 100644 index 00000000..33ff7e06 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/src/memmem.c @@ -0,0 +1,83 @@ +/* + MIT License + + Copyright (c) 2024 Roy Merkel + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "memmem.h" + +void *memmem_impl(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) +{ + const unsigned char *cmpp; + const unsigned char *p; + const unsigned char *endp; + const unsigned char *q; + const unsigned char *endq; + unsigned char found; + + if(haystack == NULL) + { + return NULL; + } + if(needle == NULL) + { + return (void*)haystack; + } + if(haystacklen == 0) + { + return NULL; + } + if(needlelen == 0) + { + return (void*)haystack; + } + + if(needlelen > haystacklen) + { + return NULL; + } + + endp = haystack + haystacklen - needlelen; + endq = needle + needlelen; + for(p = haystack; p <= endp; p++) + { + found = 1; + cmpp = p; + for(q = needle; q < endq; q++) + { + if(*cmpp != *q) + { + found = 0; + break; + } + else + { + cmpp++; + } + } + if(found) + { + return (void*)p; + } + } + + return NULL; +} diff --git a/Source/rewrite-hoc-clk/common/src/psm_ext.c b/Source/rewrite-hoc-clk/common/src/psm_ext.c new file mode 100644 index 00000000..9bb1ae0c --- /dev/null +++ b/Source/rewrite-hoc-clk/common/src/psm_ext.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) KazushiMe + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +const char* PsmPowerRoleToStr(PsmPowerRole role) { + switch (role) { + case PsmPowerRole_Sink: return "Sink"; + case PsmPowerRole_Source: return "Source"; + default: return "Unknown"; + } +} + +const char* PsmInfoChargerTypeToStr(PsmInfoChargerType type) { + switch (type) { + case PsmInfoChargerType_None: return "None"; + case PsmInfoChargerType_PD: return "USB-C PD"; + case PsmInfoChargerType_TypeC_1500mA: + case PsmInfoChargerType_TypeC_3000mA: return "USB-C"; + case PsmInfoChargerType_DCP: return "USB DCP"; + case PsmInfoChargerType_CDP: return "USB CDP"; + case PsmInfoChargerType_SDP: return "USB SDP"; + case PsmInfoChargerType_Apple_500mA: + case PsmInfoChargerType_Apple_1000mA: + case PsmInfoChargerType_Apple_2000mA: return "Apple"; + default: return "Unknown"; + } +} + +bool PsmIsChargerConnected(const PsmChargeInfo* info) { + return info->ChargerType != PsmInfoChargerType_None; +} + +bool PsmIsCharging(const PsmChargeInfo* info) { + return PsmIsChargerConnected(info) && ((info->unk_x14 >> 8) & 1); +} + +PsmBatteryState PsmGetBatteryState(const PsmChargeInfo* info) { + if (!PsmIsChargerConnected(info)) + return PsmBatteryState_Discharging; + if (!PsmIsCharging(info)) + return PsmBatteryState_ChargingPaused; + return PsmBatteryState_FastCharging; +} + +const char* PsmGetBatteryStateIcon(const PsmChargeInfo* info) { + switch (PsmGetBatteryState(info)) { + case PsmBatteryState_Discharging: return "\u25c0"; // â—€ + case PsmBatteryState_ChargingPaused:return "| |"; + case PsmBatteryState_FastCharging: return "\u25b6"; // â–¶ + default: return "?"; + } +} diff --git a/Source/rewrite-hoc-clk/common/src/pwm.c b/Source/rewrite-hoc-clk/common/src/pwm.c new file mode 100644 index 00000000..96475997 --- /dev/null +++ b/Source/rewrite-hoc-clk/common/src/pwm.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) MasaGratoR + * + * 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 . + * + */ + +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include +#include "service_guard.h" +#include "pwm.h" + +static Service g_pwmSrv; + +NX_GENERATE_SERVICE_GUARD(pwm); + +Result _pwmInitialize(void) { + return smGetService(&g_pwmSrv, "pwm"); +} + +void _pwmCleanup(void) { + serviceClose(&g_pwmSrv); +} + +Service* pwmGetServiceSession(void) { + return &g_pwmSrv; +} + +Result pwmOpenSession2(PwmChannelSession *out, u32 device_code) { + return serviceDispatchIn(&g_pwmSrv, 2, device_code, + .out_num_objects = 1, + .out_objects = &out->s, + ); +} + +Result pwmChannelSessionGetDutyCycle(PwmChannelSession *c, double* out) { + return serviceDispatchOut(&c->s, 7, *out); +} + +void pwmChannelSessionClose(PwmChannelSession *controller) { + serviceClose(&controller->s); +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/common/src/rgltr_services.cpp b/Source/rewrite-hoc-clk/common/src/rgltr_services.cpp new file mode 100644 index 00000000..6365e2ed --- /dev/null +++ b/Source/rewrite-hoc-clk/common/src/rgltr_services.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) ppkantorski (bord2death) + * + * 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 . + * + */ + +#include +#include "rgltr.h" +#include "rgltr_services.h" // for extern Service g_rgltrSrv, etc. + +// Global service handle +Service g_rgltrSrv; + +Result rgltrInitialize(void) { + if (hosversionBefore(8, 0, 0)) { + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + } + return smGetService(&g_rgltrSrv, "rgltr"); +} + +void rgltrExit(void) { + serviceClose(&g_rgltrSrv); +} + +Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id) { + const u32 in = (u32)module_id; + return serviceDispatchIn( + &g_rgltrSrv, + 0, + in, + .out_num_objects = 1, + .out_objects = &session_out->s + ); +} + +Result rgltrGetVoltage(RgltrSession* session, u32* out_volt) { + u32 temp = 0; + Result rc = serviceDispatchOut(&session->s, 4, temp); + if (R_SUCCEEDED(rc)) { + *out_volt = temp; + } + return rc; +} + +Result rgltrRequestVoltage(RgltrSession* session, u32 microvolt) { + return serviceDispatchIn(&session->s, 5, microvolt); +} + +Result rgltrCancelVoltageRequest(RgltrSession* session) { + return serviceDispatch(&session->s, 6); +} + +void rgltrCloseSession(RgltrSession* session) { + serviceClose(&session->s); +} diff --git a/Source/rewrite-hoc-clk/config.ini.template b/Source/rewrite-hoc-clk/config.ini.template new file mode 100644 index 00000000..4305c725 --- /dev/null +++ b/Source/rewrite-hoc-clk/config.ini.template @@ -0,0 +1,19 @@ +[values] +; Defines how often sys-clk log temperatures, in milliseconds (set 0 to disable) +temp_log_interval_ms=0 +; Defines how often sys-clk writes to the CSV, in milliseconds (set 0 to disable) +csv_write_interval_ms=0 + +; Example #1: BOTW +; Overclock CPU when docked +; Overclock MEM to docked clocks when handheld +;[01007EF00011E000] +;docked_cpu=1224 +;handheld_mem=1600 + +; Example #2: Picross +; Underclock to save battery +;[0100BA0003EEA000] +;handheld_cpu=816 +;handheld_gpu=153 +;handheld_mem=800 \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/.gitignore b/Source/rewrite-hoc-clk/sysmodule/.gitignore new file mode 100644 index 00000000..36a52c92 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/.gitignore @@ -0,0 +1,2 @@ +/out +/build diff --git a/Source/rewrite-hoc-clk/sysmodule/Makefile b/Source/rewrite-hoc-clk/sysmodule/Makefile new file mode 100644 index 00000000..ae8bfa66 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/Makefile @@ -0,0 +1,164 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". +#--------------------------------------------------------------------------------- +TARGET := horizon-oc +BUILD := build +OUTDIR := out +RESOURCES := res +SOURCES := src src/nx/ipc ../common/src +DATA := data +INCLUDES := ../common/include +EXEFS_SRC := exefs_src +LIBNAMES := minIni nxExt + +#--------------------------------------------------------------------------------- +# version control constants +#--------------------------------------------------------------------------------- +TARGET_VERSION := $(shell git describe --dirty --always --tags) + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +DEFINES := -DDISABLE_IPC -DTARGET="\"$(TARGET)\"" -DTARGET_VERSION="\"$(TARGET_VERSION)\"" + +ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -Os -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -std=gnu++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := $(foreach lib,$(LIBNAMES),-l$(lib)) -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) $(foreach lib,$(LIBNAMES),$(TOPDIR)/lib/$(lib)) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(OUTDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +export APP_JSON := $(TOPDIR)/perms.json + +.PHONY: $(BUILD) clean clean-libs clean-build all libs + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +libs: + @$(foreach lib,$(LIBNAMES),$(MAKE) --no-print-directory -C $(TOPDIR)/lib/$(lib) && ) true + +$(LIBNAMES): + @echo $@ + +$(BUILD): libs + @[ -d $@ ] || mkdir -p $@ + @[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR) + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean-libs: + @echo clean libs $(LIBNAMES) ... + @$(foreach lib,$(LIBNAMES),$(MAKE) -C $(TOPDIR)/lib/$(lib) clean && ) true + +clean-build: + @echo clean build ... + @rm -fr $(BUILD) $(TARGET).kip $(TARGET).nsp $(TARGET).npdm $(TARGET).nso $(TARGET).elf $(OUTDIR) + +clean: clean-libs clean-build + + +#--------------------------------------------------------------------------------- +else +.PHONY: all $(LIBFILES) + +LIBFILES := $(foreach lib,$(LIBNAMES),$(TOPDIR)/lib/$(lib)/lib/lib$(lib).a) +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- + +all: $(OUTPUT).nsp + +$(OUTPUT).nsp: $(OUTPUT).nso $(OUTPUT).npdm + +$(OUTPUT).elf: $(OFILES) $(LIBFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/.gitignore b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/.gitignore new file mode 100644 index 00000000..0806f7bf --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/.gitignore @@ -0,0 +1,13 @@ +# Editor files +*.swp +*~ + +# Objects +*.o +*.a +*.so + +# Lib +lib +release +debug \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/.gitrepo b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/.gitrepo new file mode 100644 index 00000000..44cc0b36 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/.gitrepo @@ -0,0 +1,12 @@ +; DO NOT EDIT (unless you know what you are doing) +; +; This subdirectory is a git "subrepo", and this file is maintained by the +; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme +; +[subrepo] + remote = https://github.com/compuphase/minIni + branch = master + commit = 8ce144c3c287fa4e59f8ed4c405cd8b7e29f189b + parent = f32c0b1bca87a6d7e55fb341f8db9ef33b13819f + method = merge + cmdver = 0.4.0 diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/LICENSE b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/LICENSE new file mode 100644 index 00000000..cbf8eb4c --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/LICENSE @@ -0,0 +1,189 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + + EXCEPTION TO THE APACHE 2.0 LICENSE + + As a special exception to the Apache License 2.0 (and referring to the + definitions in Section 1 of this license), you may link, statically or + dynamically, the "Work" to other modules to produce an executable file + containing portions of the "Work", and distribute that executable file + in "Object" form under the terms of your choice, without any of the + additional requirements listed in Section 4 of the Apache License 2.0. + This exception applies only to redistributions in "Object" form (not + "Source" form) and only if no modifications have been made to the "Work". + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/Makefile b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/Makefile new file mode 100644 index 00000000..b9565d07 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/Makefile @@ -0,0 +1,133 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +include $(DEVKITPRO)/libnx/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +SOURCES := dev +DATA := data +INCLUDES := dev +SRC_H_FILES := + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec + +CFLAGS := -g -Wall -Werror \ + -ffunction-sections \ + -fdata-sections \ + $(ARCH) \ + $(BUILD_CFLAGS) + +CFLAGS += $(INCLUDE) + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := -g $(ARCH) + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := minIni.c +CPPFILES := +SFILES := +BINFILES := + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +.PHONY: clean all lib/lib$(TARGET).a + +#--------------------------------------------------------------------------------- +all: lib/lib$(TARGET).a + +lib: + @[ -d $@ ] || mkdir -p $@ + +release: + @[ -d $@ ] || mkdir -p $@ + +debug: + @[ -d $@ ] || mkdir -p $@ + +lib/lib$(TARGET).a : lib release $(SOURCES) $(INCLUDES) + @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ + BUILD_CFLAGS="-DNDEBUG=1 -O2" \ + DEPSDIR=$(CURDIR)/release \ + --no-print-directory -C release \ + -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr release debug lib + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES_SRC) : $(HFILES) + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/NOTICE b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/NOTICE new file mode 100644 index 00000000..dbd0bba0 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/NOTICE @@ -0,0 +1,12 @@ +minIni is a programmer's library to read and write "INI" files in embedded +systems. The library takes little resources and can be configured for various +kinds of file I/O libraries. + +The method for portable INI file management in minIni is, in part based, on the +article "Multiplatform .INI Files" by Joseph J. Graf in the March 1994 issue of +Dr. Dobb's Journal. + +The C++ class in minIni.h was contributed by Steven Van Ingelgem. + +The option to compile minIni as a read-only library was contributed by Luca +Bassanello. diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/README.md b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/README.md new file mode 100644 index 00000000..9f30a019 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/README.md @@ -0,0 +1,170 @@ +# minIni +minIni is a portable and configurable library for reading and writing ".INI" files. At 830 lines of commented source +code +(version 1.2), minIni truly is a "mini" INI file parser, especially considering its features. + +The library does not require the file I/O functions from the standard C/C++ library, but instead lets you configure +the file I/O interface to use via macros. minIni uses limited stack space and does not use dynamic memory (malloc and +friends) at all. + +Some minor variations on standard INI files are supported too, notably minIni supports INI files that lack sections. + + +# Acknowledgement + +minIni is derived from an earlier INI file parser (which I wrote) for desktop systems. + +In turn, that earlier parser was a re-write of the code from the article "Multiplatform .INI Files" by Joseph J. Graf +in the March 1994 issue of Dr. Dobb's Journal. In other words, minIni has its roots in the work of Joseph Graf (even +though the code has been almost completely re-written). + + +# Features + +minIni is a programmer's library to read and write "INI" files in embedded systems. minIni takes little resources, +can be configured for various kinds of file I/O libraries and provides functionality for reading, writing and +deleting keys from an INI file. + +Although the main feature of minIni is that it is small and minimal, it has a few other features: + + * minIni supports reading keys that are outside a section, and it thereby supports configuration files that do not use sections (but that are otherwise compatible with INI files). + * You may use a colon to separate key and value; the colon is equivalent to the equal sign. That is, the strings "Name: Value" and "Name=Value" have the same meaning. + * The hash character ("#") is an alternative for the semicolon to start a comment. Trailing comments (i.e. behind a key/value pair on a line) are allowed. + * Leading and trailing white space around key names and values is ignored. + * When writing a value that contains a comment character (";" or "#"), that value will automatically be put between double quotes; when reading the value, these quotes are removed. When a double-quote itself appears in the setting, these characters are escaped. + * Section and key enumeration are supported. + * You can optionally set the line termination (for text files) that minIni will use. (This is a compile-time setting, not a run-time setting.) + * Since writing speed is much lower than reading speed in Flash memory (SD/MMC cards, USB memory sticks), minIni minimizes "file writes" at the expense of double "file reads". + * The memory footprint is deterministic. There is no dynamic memory allocation. + +## INI file reading paradigms + +There are two approaches to reading settings from an INI file. One way is to call a function, such as +GetProfileString() for every section and key that you need. This is especially convenient if there is a large +INI file, but you only need a few settings from that file at any time —especially if the INI file can also +change while your program runs. This is the approach that the Microsoft Windows API uses. + +The above procedure is quite inefficient, however, when you need to retrieve quite a few settings in a row from +the INI file —especially if the INI file is not cached in memory (which it isn't, in minIni). A different approach +to getting settings from an INI file is to call a "parsing" function and let that function call the application +back with the section and key names plus the associated data. XML parsing libraries often use this approach; see +for example the Expat library. + +minIni supports both approaches. For reading a single setting, use functions like ini_gets(). For the callback +approach, implement a callback and call ini_browse(). See the minIni manual for details. + + +# INI file syntax + +INI files are best known from Microsoft Windows, but they are also used with applications that run on other +platforms (although their file extension is sometimes ".cfg" instead of ".ini"). + +INI files have a simple syntax with name/value pairs in a plain text file. The name must be unique (per section) +and the value must fit on a single line. INI files are commonly separated into sections —in minIni, this is +optional. A section is a name between square brackets, like "[Network]" in the example below. + +``` +[Network] +hostname=My Computer +address=dhcp +dns = 192.168.1.1 +``` + +In the API and in this documentation, the "name" for a setting is denoted as the key for the setting. The key +and the value are separated by an equal sign ("="). minIni supports the colon (":") as an alternative to the +equal sign for the key/value delimiter. + +Leading a trailing spaces around values or key names are removed. If you need to include leading and/or trailing +spaces in a value, put the value between double quotes. The ini_gets() function (from the minIni library, see the +minIni manual) strips off the double quotes from the returned value. Function ini_puts() adds double quotes if +the value to write contains trailing white space (or special characters). + +minIni ignores spaces around the "=" or ":" delimiters, but it does not ignore spaces between the brackets in a +section name. In other words, it is best not to put spaces behind the opening bracket "[" or before the closing +bracket "]" of a section name. + +Comments in the INI must start with a semicolon (";") or a hash character ("#"), and run to the end of the line. +A comment can be a line of its own, or it may follow a key/value pair (the "#" character and trailing comments +are extensions of minIni). + +For more details on the format, please see http://en.wikipedia.org/wiki/INI_file. + + +# Adapting minIni to a file system + +The minIni library must be configured for a platform with the help of a so- called "glue file". This glue file +contains macros (and possibly functions) that map file reading and writing functions used by the minIni library +to those provided by the operating system. The glue file must be called "minGlue.h". + +To get you started, the minIni distribution comes with the following example glue files: + + * a glue file that maps to the standard C/C++ library (specifically the file I/O functions from the "stdio" package), + * a glue file for Microchip's "Memory Disk Drive File System Library" (see http://www.microchip.com/), + * a glue file for the FAT library provided with the CCS PIC compiler (see http://www.ccsinfo.com/) + * a glue file for the EFS Library (EFSL, http://www.efsl.be/), + * and a glue file for the FatFs and Petit-FatFs libraries (http://elm-chan.org/fsw/ff/00index_e.html). + +The minIni library does not rely on the availability of a standard C library, because embedded operating systems +may have limited support for file I/O. Even on full operating systems, separating the file I/O from the INI format +parsing carries advantages, because it allows you to cache the INI file and thereby enhance performance. + +The glue file must specify the type that identifies a file, whether it is a handle or a pointer. For the standard +C/C++ file I/O library, this would be: + +```C +#define INI_FILETYPE FILE* +``` + +If you are not using the standard C/C++ file I/O library, chances are that you need a different handle or +"structure" to identify the storage than the ubiquitous "FILE*" type. For example, the glue file for the FatFs +library uses the following declaration: + +```C +#define INI_FILETYPE FIL +``` + +The minIni functions declare variables of this INI_FILETYPE type and pass these variables to sub-functions +(including the glue interface functions) by reference. + +For "write support", another type that must be defined is for variables that hold the "current position" in a +file. For the standard C/C++ I/O library, this is "fpos_t". + +Another item that needs to be configured is the buffer size. The functions in the minIni library allocate this +buffer on the stack, so the buffer size is directly related to the stack usage. In addition, the buffer size +determines the maximum line length that is supported in the INI file and the maximum path name length for the +temporary file. For example, minGlue.h could contain the definition: + +```C +#define INI_BUFFERSIZE 512 +``` + +The above macro limits the line length of the INI files supported by minIni to 512 characters. + +The temporary file is only used when writing to INI files. The minIni routines copy/change the INI file to a +temporary file and then rename that temporary file to the original file. This approach uses the least amount of +memory. The path name of the temporary file is the same as the input file, but with the last character set to a +tilde ("~"). + +Below is an example of a glue file (this is the one that maps to the C/C++ "stdio" library). + +```C +#include + +#define INI_FILETYPE FILE* +#define ini_openread(filename,file) ((*(file) = fopen((filename),"r")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"w")) != NULL) +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (rename((source), (dest)) == 0) +#define ini_remove(filename) (remove(filename) == 0) + +#define INI_FILEPOS fpos_t +#define ini_tell(file,pos) (fgetpos(*(file), (pos)) == 0) +#define ini_seek(file,pos) (fsetpos(*(file), (pos)) == 0) +``` + +As you can see, a glue file is mostly a set of macros that wraps one function definition around another. + +The glue file may contain more settings, for support of rational numbers, to explicitly set the line termination +character(s), or to disable write support (for example). See the manual that comes with the archive for the details. diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-FatFs.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-FatFs.h new file mode 100644 index 00000000..51593a26 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-FatFs.h @@ -0,0 +1,37 @@ +/* Glue functions for the minIni library, based on the FatFs and Petit-FatFs + * libraries, see http://elm-chan.org/fsw/ff/00index_e.html + * + * By CompuPhase, 2008-2012 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (The FatFs and Petit-FatFs libraries are copyright by ChaN and licensed at + * its own terms.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +/* You must set _USE_STRFUNC to 1 or 2 in the include file ff.h (or tff.h) + * to enable the "string functions" fgets() and fputs(). + */ +#include "ff.h" /* include tff.h for Tiny-FatFs */ + +#define INI_FILETYPE FIL +#define ini_openread(filename,file) (f_open((file), (filename), FA_READ+FA_OPEN_EXISTING) == FR_OK) +#define ini_openwrite(filename,file) (f_open((file), (filename), FA_WRITE+FA_CREATE_ALWAYS) == FR_OK) +#define ini_close(file) (f_close(file) == FR_OK) +#define ini_read(buffer,size,file) f_gets((buffer), (size),(file)) +#define ini_write(buffer,file) f_puts((buffer), (file)) +#define ini_remove(filename) (f_unlink(filename) == FR_OK) + +#define INI_FILEPOS DWORD +#define ini_tell(file,pos) (*(pos) = f_tell((file))) +#define ini_seek(file,pos) (f_lseek((file), *(pos)) == FR_OK) + +static int ini_rename(TCHAR *source, const TCHAR *dest) +{ + /* Function f_rename() does not allow drive letters in the destination file */ + char *drive = strchr(dest, ':'); + drive = (drive == NULL) ? dest : drive + 1; + return (f_rename(source, drive) == FR_OK); +} diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-ccs.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-ccs.h new file mode 100644 index 00000000..22a8c92b --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-ccs.h @@ -0,0 +1,64 @@ +/* minIni glue functions for FAT library by CCS, Inc. (as provided with their + * PIC MCU compiler) + * + * By CompuPhase, 2011-2012 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (The FAT library is copyright (c) 2007 Custom Computer Services, and + * licensed at its own terms.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +#ifndef FAT_PIC_C + #error FAT library must be included before this module +#endif +#define const /* keyword not supported by CCS */ + +#define INI_FILETYPE FILE +#define ini_openread(filename,file) (fatopen((filename), "r", (file)) == GOODEC) +#define ini_openwrite(filename,file) (fatopen((filename), "w", (file)) == GOODEC) +#define ini_close(file) (fatclose((file)) == 0) +#define ini_read(buffer,size,file) (fatgets((buffer), (size), (file)) != NULL) +#define ini_write(buffer,file) (fatputs((buffer), (file)) == GOODEC) +#define ini_remove(filename) (rm_file((filename)) == 0) + +#define INI_FILEPOS fatpos_t +#define ini_tell(file,pos) (fatgetpos((file), (pos)) == 0) +#define ini_seek(file,pos) (fatsetpos((file), (pos)) == 0) + +#ifndef INI_READONLY +/* CCS FAT library lacks a rename function, so instead we copy the file to the + * new name and delete the old file + */ +static int ini_rename(char *source, char *dest) +{ + FILE fr, fw; + int n; + + if (fatopen(source, "r", &fr) != GOODEC) + return 0; + if (rm_file(dest) != 0) + return 0; + if (fatopen(dest, "w", &fw) != GOODEC) + return 0; + + /* With some "insider knowledge", we can save some memory: the "source" + * parameter holds a filename that was built from the "dest" parameter. It + * was built in a local buffer with the size INI_BUFFERSIZE. We can reuse + * this buffer for copying the file. + */ + while (n=fatread(source, 1, INI_BUFFERSIZE, &fr)) + fatwrite(source, 1, n, &fw); + + fatclose(&fr); + fatclose(&fw); + + /* Now we need to delete the source file. However, we have garbled the buffer + * that held the filename of the source. So we need to build it again. + */ + ini_tempname(source, dest, INI_BUFFERSIZE); + return rm_file(source) == 0; +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-efsl.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-efsl.h new file mode 100644 index 00000000..5fe0fcf8 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-efsl.h @@ -0,0 +1,63 @@ +/* Glue functions for the minIni library, based on the EFS Library, see + * http://www.efsl.be/ + * + * By CompuPhase, 2008-2012 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (EFSL is copyright 2005-2006 Lennart Ysboodt and Michael De Nil, and + * licensed under the GPL with an exception clause for static linking.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ +#define INI_LINETERM "\r\n" /* set line termination explicitly */ + +#include "efs.h" +extern EmbeddedFileSystem g_efs; + +#define INI_FILETYPE EmbeddedFile +#define ini_openread(filename,file) (file_fopen((file), &g_efs.myFs, (char*)(filename), 'r') == 0) +#define ini_openwrite(filename,file) (file_fopen((file), &g_efs.myFs, (char*)(filename), 'w') == 0) +#define ini_close(file) file_fclose(file) +#define ini_read(buffer,size,file) (file_read((file), (size), (buffer)) > 0) +#define ini_write(buffer,file) (file_write((file), strlen(buffer), (char*)(buffer)) > 0) +#define ini_remove(filename) rmfile(&g_efs.myFs, (char*)(filename)) + +#define INI_FILEPOS euint32 +#define ini_tell(file,pos) (*(pos) = (file)->FilePtr)) +#define ini_seek(file,pos) file_setpos((file), (*pos)) + +#if ! defined INI_READONLY +/* EFSL lacks a rename function, so instead we copy the file to the new name + * and delete the old file + */ +static int ini_rename(char *source, const char *dest) +{ + EmbeddedFile fr, fw; + int n; + + if (file_fopen(&fr, &g_efs.myFs, source, 'r') != 0) + return 0; + if (rmfile(&g_efs.myFs, (char*)dest) != 0) + return 0; + if (file_fopen(&fw, &g_efs.myFs, (char*)dest, 'w') != 0) + return 0; + + /* With some "insider knowledge", we can save some memory: the "source" + * parameter holds a filename that was built from the "dest" parameter. It + * was built in buffer and this buffer has the size INI_BUFFERSIZE. We can + * reuse this buffer for copying the file. + */ + while (n=file_read(&fr, INI_BUFFERSIZE, source)) + file_write(&fw, n, source); + + file_fclose(&fr); + file_fclose(&fw); + + /* Now we need to delete the source file. However, we have garbled the buffer + * that held the filename of the source. So we need to build it again. + */ + ini_tempname(source, dest, INI_BUFFERSIZE); + return rmfile(&g_efs.myFs, source) == 0; +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-ffs.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-ffs.h new file mode 100644 index 00000000..bf874e41 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-ffs.h @@ -0,0 +1,26 @@ +/* Glue functions for the minIni library, based on the "FAT Filing System" + * library by embedded-code.com + * + * By CompuPhase, 2008-2012 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (The "FAT Filing System" library itself is copyright embedded-code.com, and + * licensed at its own terms.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ +#include + +#define INI_FILETYPE FFS_FILE* +#define ini_openread(filename,file) ((*(file) = ffs_fopen((filename),"r")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = ffs_fopen((filename),"w")) != NULL) +#define ini_close(file) (ffs_fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (ffs_fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (ffs_fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (ffs_rename((source), (dest)) == 0) +#define ini_remove(filename) (ffs_remove(filename) == 0) + +#define INI_FILEPOS long +#define ini_tell(file,pos) (ffs_fgetpos(*(file), (pos)) == 0) +#define ini_seek(file,pos) (ffs_fsetpos(*(file), (pos)) == 0) diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-mdd.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-mdd.h new file mode 100644 index 00000000..ec5e0be1 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-mdd.h @@ -0,0 +1,58 @@ +/* minIni glue functions for Microchip's "Memory Disk Drive" file system + * library, as presented in Microchip application note AN1045. + * + * By CompuPhase, 2011-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + * + * (The "Microchip Memory Disk Drive File System" is copyright (c) Microchip + * Technology Incorporated, and licensed at its own terms.) + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +#include "MDD File System\fsio.h" +#include + +#define INI_FILETYPE FSFILE* +#define ini_openread(filename,file) ((*(file) = FSfopen((filename),FS_READ)) != NULL) +#define ini_openwrite(filename,file) ((*(file) = FSfopen((filename),FS_WRITE)) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),FS_READPLUS)) != NULL) +#define ini_close(file) (FSfclose(*(file)) == 0) +#define ini_write(buffer,file) (FSfwrite((buffer), 1, strlen(buffer), (*file)) > 0) +#define ini_remove(filename) (FSremove((filename)) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = FSftell(*(file))) +#define ini_seek(file,pos) (FSfseek(*(file), *(pos), SEEK_SET) == 0) + +/* Since the Memory Disk Drive file system library reads only blocks of files, + * the function to read a text line does so by "over-reading" a block of the + * of the maximum size and truncating it behind the end-of-line. + */ +static int ini_read(char *buffer, int size, INI_FILETYPE *file) +{ + size_t numread = size; + char *eol; + + if ((numread = FSfread(buffer, 1, size, *file)) == 0) + return 0; /* at EOF */ + if ((eol = strchr(buffer, '\n')) == NULL) + eol = strchr(buffer, '\r'); + if (eol != NULL) { + /* terminate the buffer */ + *++eol = '\0'; + /* "unread" the data that was read too much */ + FSfseek(*file, - (int)(numread - (size_t)(eol - buffer)), SEEK_CUR); + } /* if */ + return 1; +} + +#ifndef INI_READONLY +static int ini_rename(const char *source, const char *dest) +{ + FSFILE* ftmp = FSfopen((source), FS_READ); + FSrename((dest), ftmp); + return FSfclose(ftmp) == 0; +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-stdio.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-stdio.h new file mode 100644 index 00000000..67d24334 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue-stdio.h @@ -0,0 +1,31 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library + * + * Or better said: this file contains macros that maps the function interface + * used by minIni to the standard C/C++ file I/O functions. + * + * By CompuPhase, 2008-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + */ + +/* map required file I/O types and functions to the standard C library */ +#include + +#define INI_FILETYPE FILE* +#define ini_openread(filename,file) ((*(file) = fopen((filename),"rb")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"wb")) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),"r+b")) != NULL) +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (rename((source), (dest)) == 0) +#define ini_remove(filename) (remove(filename) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = ftell(*(file))) +#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0) + +/* for floating-point support, define additional types and functions */ +#define INI_REAL float +#define ini_ftoa(string,value) sprintf((string),"%f",(value)) +#define ini_atof(string) (INI_REAL)strtod((string),NULL) diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue.h new file mode 100644 index 00000000..c3149627 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minGlue.h @@ -0,0 +1,35 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library + * + * Or better said: this file contains macros that maps the function interface + * used by minIni to the standard C/C++ file I/O functions. + * + * By CompuPhase, 2008-2014 + * This "glue file" is in the public domain. It is distributed without + * warranties or conditions of any kind, either express or implied. + */ + +/* map required file I/O types and functions to the standard C library */ +#include + +#define INI_FILETYPE FILE* +#define ini_openread(filename,file) ((*(file) = fopen((filename),"rb")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"wb")) != NULL) +#define ini_openrewrite(filename,file) ((*(file) = fopen((filename),"r+b")) != NULL) +#define ini_close(file) (fclose(*(file)) == 0) +#define ini_read(buffer,size,file) (fgets((buffer),(size),*(file)) != NULL) +#define ini_write(buffer,file) (fputs((buffer),*(file)) >= 0) +#define ini_rename(source,dest) (rename((source), (dest)) == 0) +#define ini_remove(filename) (remove(filename) == 0) + +#define INI_FILEPOS long int +#define ini_tell(file,pos) (*(pos) = ftell(*(file))) +#define ini_seek(file,pos) (fseek(*(file), *(pos), SEEK_SET) == 0) + +/* for floating-point support, define additional types and functions */ +//#define INI_REAL float +//#define ini_ftoa(string,value) sprintf((string),"%f",(value)) +//#define ini_atof(string) (INI_REAL)strtod((string),NULL) + +#define INI_ANSIONLY +#define INI_LINETERM "\n" +#define PORTABLE_STRNICMP \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minIni.c b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minIni.c new file mode 100644 index 00000000..a0f75a27 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minIni.c @@ -0,0 +1,1009 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * These routines are in part based on the article "Multiplatform .INI Files" + * by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal. + * + * Copyright (c) CompuPhase, 2008-2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: minIni.c 53 2015-01-18 13:35:11Z thiadmer.riemersma@gmail.com $ + */ + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#define MININI_IMPLEMENTATION +#include "minIni.h" +#if defined NDEBUG + #define assert(e) +#else + #include +#endif + +#if !defined __T || defined INI_ANSIONLY + #include + #include + #include + #define TCHAR char + #define __T(s) s + #define _tcscat strcat + #define _tcschr strchr + #define _tcscmp strcmp + #define _tcscpy strcpy + #define _tcsicmp stricmp + #define _tcslen strlen + #define _tcsncmp strncmp + #define _tcsnicmp strnicmp + #define _tcsrchr strrchr + #define _tcstol strtol + #define _tcstod strtod + #define _totupper toupper + #define _stprintf sprintf + #define _tfgets fgets + #define _tfputs fputs + #define _tfopen fopen + #define _tremove remove + #define _trename rename +#endif + +#if defined __linux || defined __linux__ + #define __LINUX__ +#elif defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#elif defined(_MSC_VER) + #pragma warning(disable: 4996) /* for Microsoft Visual C/C++ */ +#endif +#if !defined strnicmp && !defined PORTABLE_STRNICMP + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #define strnicmp strncasecmp + #endif +#endif +#if !defined _totupper + #define _totupper toupper +#endif + +#if !defined INI_LINETERM + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __APPLE__ + #define INI_LINETERM __T("\n") + #else + #define INI_LINETERM __T("\r\n") + #endif +#endif +#if !defined INI_FILETYPE + #error Missing definition for INI_FILETYPE. +#endif + +#if !defined sizearray + #define sizearray(a) (sizeof(a) / sizeof((a)[0])) +#endif + +enum quote_option { + QUOTE_NONE, + QUOTE_ENQUOTE, + QUOTE_DEQUOTE, +}; + +#if defined PORTABLE_STRNICMP +int strnicmp(const TCHAR *s1, const TCHAR *s2, size_t n) +{ + while (n-- != 0 && (*s1 || *s2)) { + register int c1, c2; + c1 = *s1++; + if ('a' <= c1 && c1 <= 'z') + c1 += ('A' - 'a'); + c2 = *s2++; + if ('a' <= c2 && c2 <= 'z') + c2 += ('A' - 'a'); + if (c1 != c2) + return c1 - c2; + } /* while */ + return 0; +} +#endif /* PORTABLE_STRNICMP */ + +static TCHAR *skipleading(const TCHAR *str) +{ + assert(str != NULL); + while ('\0' < *str && *str <= ' ') + str++; + return (TCHAR *)str; +} + +static TCHAR *skiptrailing(const TCHAR *str, const TCHAR *base) +{ + assert(str != NULL); + assert(base != NULL); + while (str > base && '\0' < *(str-1) && *(str-1) <= ' ') + str--; + return (TCHAR *)str; +} + +static TCHAR *striptrailing(TCHAR *str) +{ + TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str); + assert(ptr != NULL); + *ptr = '\0'; + return str; +} + +static TCHAR *ini_strncpy(TCHAR *dest, const TCHAR *source, size_t maxlen, enum quote_option option) +{ + size_t d, s; + + assert(maxlen>0); + assert(source != NULL && dest != NULL); + assert((dest < source || (dest == source && option != QUOTE_ENQUOTE)) || dest > source + strlen(source)); + if (option == QUOTE_ENQUOTE && maxlen < 3) + option = QUOTE_NONE; /* cannot store two quotes and a terminating zero in less than 3 characters */ + + switch (option) { + case QUOTE_NONE: + for (d = 0; d < maxlen - 1 && source[d] != '\0'; d++) + dest[d] = source[d]; + assert(d < maxlen); + dest[d] = '\0'; + break; + case QUOTE_ENQUOTE: + d = 0; + dest[d++] = '"'; + for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) { + if (source[s] == '"') { + if (d >= maxlen - 3) + break; /* no space to store the escape character plus the one that follows it */ + dest[d++] = '\\'; + } /* if */ + dest[d] = source[s]; + } /* for */ + dest[d++] = '"'; + dest[d] = '\0'; + break; + case QUOTE_DEQUOTE: + for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) { + if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"') + s++; + dest[d] = source[s]; + } /* for */ + dest[d] = '\0'; + break; + default: + assert(0); + } /* switch */ + + return dest; +} + +static TCHAR *cleanstring(TCHAR *string, enum quote_option *quotes) +{ + int isstring; + TCHAR *ep; + + assert(string != NULL); + assert(quotes != NULL); + + /* Remove a trailing comment */ + isstring = 0; + for (ep = string; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) { + if (*ep == '"') { + if (*(ep + 1) == '"') + ep++; /* skip "" (both quotes) */ + else + isstring = !isstring; /* single quote, toggle isstring */ + } else if (*ep == '\\' && *(ep + 1) == '"') { + ep++; /* skip \" (both quotes */ + } /* if */ + } /* for */ + assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#')); + *ep = '\0'; /* terminate at a comment */ + striptrailing(string); + /* Remove double quotes surrounding a value */ + *quotes = QUOTE_NONE; + if (*string == '"' && (ep = _tcschr(string, '\0')) != NULL && *(ep - 1) == '"') { + string++; + *--ep = '\0'; + *quotes = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */ + } /* if */ + return string; +} + +static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key, + int idxSection, int idxKey, TCHAR *Buffer, int BufferSize, + INI_FILEPOS *mark) +{ + TCHAR *sp, *ep; + int len, idx; + enum quote_option quotes; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + + assert(fp != NULL); + /* Move through file 1 line at a time until a section is matched or EOF. If + * parameter Section is NULL, only look at keys above the first section. If + * idxSection is positive, copy the relevant section name. + */ + len = (Section != NULL) ? (int)_tcslen(Section) : 0; + if (len > 0 || idxSection >= 0) { + assert(idxSection >= 0 || Section != NULL); + idx = -1; + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp)) + return 0; + sp = skipleading(LocalBuffer); + ep = _tcsrchr(sp, ']'); + } while (*sp != '[' || ep == NULL || + (((int)(ep-sp-1) != len || Section == NULL || _tcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection)); + if (idxSection >= 0) { + if (idx == idxSection) { + assert(ep != NULL); + assert(*ep == ']'); + *ep = '\0'; + ini_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more section found */ + } /* if */ + } /* if */ + + /* Now that the section has been found, find the entry. + * Stop searching upon leaving the section's area. + */ + assert(Key != NULL || idxKey >= 0); + len = (Key != NULL) ? (int)_tcslen(Key) : 0; + idx = -1; + do { + if (mark != NULL) + ini_tell(fp, mark); /* optionally keep the mark to the start of the line */ + if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[') + return 0; + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + } while (*sp == ';' || *sp == '#' || ep == NULL + || ((len == 0 || (int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey)); + if (idxKey >= 0) { + if (idx == idxKey) { + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + *ep = '\0'; + striptrailing(sp); + ini_strncpy(Buffer, sp, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more key found (in this section) */ + } /* if */ + + /* Copy up to BufferSize chars to buffer */ + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + sp = skipleading(ep + 1); + sp = cleanstring(sp, "es); /* Remove a trailing comment */ + ini_strncpy(Buffer, sp, BufferSize, quotes); + return 1; +} + +/** ini_gets() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default string in the event of a failed read + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue, + TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || Key == NULL) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + ini_strncpy(Buffer, (DefValue != NULL) ? DefValue : __T(""), BufferSize, QUOTE_NONE); + return (int)_tcslen(Buffer); +} + +/** ini_getl() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + return (len == 0) ? DefValue + : ((len >= 2 && _totupper((int)LocalBuffer[1]) == 'X') ? _tcstol(LocalBuffer, NULL, 16) + : _tcstol(LocalBuffer, NULL, 10)); +} + +#if defined INI_REAL +/** ini_getf() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +INI_REAL ini_getf(const TCHAR *Section, const TCHAR *Key, INI_REAL DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + int len = ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + return (len == 0) ? DefValue : ini_atof(LocalBuffer); +} +#endif + +/** ini_getbool() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default value in the event of a failed read; it should + * zero (0) or one (1). + * \param Filename the name and full path of the .ini file to read from + * + * A true boolean is found if one of the following is matched: + * - A string starting with 'y' or 'Y' + * - A string starting with 't' or 'T' + * - A string starting with '1' + * + * A false boolean is found if one of the following is matched: + * - A string starting with 'n' or 'N' + * - A string starting with 'f' or 'F' + * - A string starting with '0' + * + * \return the true/false flag as interpreted at Key + */ +int ini_getbool(const TCHAR *Section, const TCHAR *Key, int DefValue, const TCHAR *Filename) +{ + TCHAR LocalBuffer[2] = __T(""); + int ret; + + ini_gets(Section, Key, __T(""), LocalBuffer, sizearray(LocalBuffer), Filename); + LocalBuffer[0] = (TCHAR)_totupper((int)LocalBuffer[0]); + if (LocalBuffer[0] == 'Y' || LocalBuffer[0] == '1' || LocalBuffer[0] == 'T') + ret = 1; + else if (LocalBuffer[0] == 'N' || LocalBuffer[0] == '0' || LocalBuffer[0] == 'F') + ret = 0; + else + ret = DefValue; + + return(ret); +} + +/** ini_getsection() + * \param idx the zero-based sequence number of the section to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return (int)_tcslen(Buffer); +} + +/** ini_getkey() + * \param Section the name of the section to browse through, or NULL to + * browse through the keys outside any section + * \param idx the zero-based sequence number of the key to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize, NULL); + (void)ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return (int)_tcslen(Buffer); +} + + +#if !defined INI_NOBROWSE +/** ini_browse() + * \param Callback a pointer to a function that will be called for every + * setting in the INI file. + * \param UserData arbitrary data, which the function passes on the + * \c Callback function + * \param Filename the name and full path of the .ini file to read from + * + * \return 1 on success, 0 on failure (INI file not found) + * + * \note The \c Callback function must return 1 to continue + * browsing through the INI file, or 0 to stop. Even when the + * callback stops the browsing, this function will return 1 + * (for success). + */ +int ini_browse(INI_CALLBACK Callback, void *UserData, const TCHAR *Filename) +{ + TCHAR LocalBuffer[INI_BUFFERSIZE]; + int lenSec, lenKey; + enum quote_option quotes; + INI_FILETYPE fp; + + if (Callback == NULL) + return 0; + if (!ini_openread(Filename, &fp)) + return 0; + + LocalBuffer[0] = '\0'; /* copy an empty section in the buffer */ + lenSec = (int)_tcslen(LocalBuffer) + 1; + for ( ;; ) { + TCHAR *sp, *ep; + if (!ini_read(LocalBuffer + lenSec, INI_BUFFERSIZE - lenSec, &fp)) + break; + sp = skipleading(LocalBuffer + lenSec); + /* ignore empty strings and comments */ + if (*sp == '\0' || *sp == ';' || *sp == '#') + continue; + /* see whether we reached a new section */ + ep = _tcsrchr(sp, ']'); + if (*sp == '[' && ep != NULL) { + *ep = '\0'; + ini_strncpy(LocalBuffer, sp + 1, INI_BUFFERSIZE, QUOTE_NONE); + lenSec = (int)_tcslen(LocalBuffer) + 1; + continue; + } /* if */ + /* not a new section, test for a key/value pair */ + ep = _tcschr(sp, '='); /* test for the equal sign or colon */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + if (ep == NULL) + continue; /* invalid line, ignore */ + *ep++ = '\0'; /* split the key from the value */ + striptrailing(sp); + ini_strncpy(LocalBuffer + lenSec, sp, INI_BUFFERSIZE - lenSec, QUOTE_NONE); + lenKey = (int)_tcslen(LocalBuffer + lenSec) + 1; + /* clean up the value */ + sp = skipleading(ep); + sp = cleanstring(sp, "es); /* Remove a trailing comment */ + ini_strncpy(LocalBuffer + lenSec + lenKey, sp, INI_BUFFERSIZE - lenSec - lenKey, quotes); + /* call the callback */ + if (!Callback(LocalBuffer, LocalBuffer + lenSec, LocalBuffer + lenSec + lenKey, UserData)) + break; + } /* for */ + + (void)ini_close(&fp); + return 1; +} +#endif /* INI_NOBROWSE */ + +#if ! defined INI_READONLY +static void ini_tempname(TCHAR *dest, const TCHAR *source, int maxlength) +{ + TCHAR *p; + + ini_strncpy(dest, source, maxlength, QUOTE_NONE); + p = _tcsrchr(dest, '\0'); + assert(p != NULL); + *(p - 1) = '~'; +} + +static enum quote_option check_enquote(const TCHAR *Value) +{ + const TCHAR *p; + + /* run through the value, if it has trailing spaces, or '"', ';' or '#' + * characters, enquote it + */ + assert(Value != NULL); + for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++) + /* nothing */; + return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE; +} + +static void writesection(TCHAR *LocalBuffer, const TCHAR *Section, INI_FILETYPE *fp) +{ + if (Section != NULL && _tcslen(Section) > 0) { + TCHAR *p; + LocalBuffer[0] = '['; + ini_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE); /* -1 for '[', -1 for ']', -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = ']'; + _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + if (fp != NULL) + (void)ini_write(LocalBuffer, fp); + } /* if */ +} + +static void writekey(TCHAR *LocalBuffer, const TCHAR *Key, const TCHAR *Value, INI_FILETYPE *fp) +{ + TCHAR *p; + enum quote_option option = check_enquote(Value); + ini_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE); /* -1 for '=', -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = '='; + ini_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + if (fp != NULL) + (void)ini_write(LocalBuffer, fp); +} + +static int cache_accum(const TCHAR *string, int *size, int max) +{ + int len = (int)_tcslen(string); + if (*size + len >= max) + return 0; + *size += len; + return 1; +} + +static int cache_flush(TCHAR *buffer, int *size, + INI_FILETYPE *rfp, INI_FILETYPE *wfp, INI_FILEPOS *mark) +{ + int terminator_len = (int)_tcslen(INI_LINETERM); + int pos = 0; + + (void)ini_seek(rfp, mark); + assert(buffer != NULL); + buffer[0] = '\0'; + assert(size != NULL); + assert(*size <= INI_BUFFERSIZE); + while (pos < *size) { + (void)ini_read(buffer + pos, INI_BUFFERSIZE - pos, rfp); + while (pos < *size && buffer[pos] != '\0') + pos++; /* cannot use _tcslen() because buffer may not be zero-terminated */ + } /* while */ + if (buffer[0] != '\0') { + assert(pos > 0 && pos <= INI_BUFFERSIZE); + if (pos == INI_BUFFERSIZE) + pos--; + buffer[pos] = '\0'; /* force zero-termination (may be left unterminated in the above while loop) */ + (void)ini_write(buffer, wfp); + } + ini_tell(rfp, mark); /* update mark */ + *size = 0; + /* return whether the buffer ended with a line termination */ + return (pos > terminator_len) && (_tcscmp(buffer + pos - terminator_len, INI_LINETERM) == 0); +} + +static int close_rename(INI_FILETYPE *rfp, INI_FILETYPE *wfp, const TCHAR *filename, TCHAR *buffer) +{ + (void)ini_close(rfp); + (void)ini_close(wfp); + (void)ini_remove(filename); + (void)ini_tempname(buffer, filename, INI_BUFFERSIZE); + (void)ini_rename(buffer, filename); + return 1; +} + +/** ini_puts() + * \param Section the name of the section to write the string in + * \param Key the name of the entry to write, or NULL to erase all keys in the section + * \param Value a pointer to the buffer the string, or NULL to erase the key + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename) +{ + INI_FILETYPE rfp; + INI_FILETYPE wfp; + INI_FILEPOS mark; + INI_FILEPOS head, tail; + TCHAR *sp, *ep; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + int len, match, flag, cachelen; + + assert(Filename != NULL); + if (!ini_openread(Filename, &rfp)) { + /* If the .ini file doesn't exist, make a new file */ + if (Key != NULL && Value != NULL) { + if (!ini_openwrite(Filename, &wfp)) + return 0; + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + (void)ini_close(&wfp); + } /* if */ + return 1; + } /* if */ + + /* If parameters Key and Value are valid (so this is not an "erase" request) + * and the setting already exists, there are two short-cuts to avoid rewriting + * the INI file. + */ + if (Key != NULL && Value != NULL) { + ini_tell(&rfp, &mark); + match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer), &head); + if (match) { + /* if the current setting is identical to the one to write, there is + * nothing to do. + */ + if (_tcscmp(LocalBuffer,Value) == 0) { + (void)ini_close(&rfp); + return 1; + } /* if */ + /* if the new setting has the same length as the current setting, and the + * glue file permits file read/write access, we can modify in place. + */ + #if defined ini_openrewrite + /* we already have the start of the (raw) line, get the end too */ + ini_tell(&rfp, &tail); + /* create new buffer (without writing it to file) */ + writekey(LocalBuffer, Key, Value, NULL); + if (_tcslen(LocalBuffer) == (size_t)(tail - head)) { + /* length matches, close the file & re-open for read/write, then + * write at the correct position + */ + (void)ini_close(&rfp); + if (!ini_openrewrite(Filename, &wfp)) + return 0; + (void)ini_seek(&wfp, &head); + (void)ini_write(LocalBuffer, &wfp); + (void)ini_close(&wfp); + return 1; + } /* if */ + #endif + } /* if */ + /* key not found, or different value & length -> proceed (but rewind the + * input file first) + */ + (void)ini_seek(&rfp, &mark); + } /* if */ + + /* Get a temporary file name to copy to. Use the existing name, but with + * the last character set to a '~'. + */ + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + if (!ini_openwrite(LocalBuffer, &wfp)) { + (void)ini_close(&rfp); + return 0; + } /* if */ + (void)ini_tell(&rfp, &mark); + cachelen = 0; + + /* Move through the file one line at a time until a section is + * matched or until EOF. Copy to temp file as it is read. + */ + len = (Section != NULL) ? (int)_tcslen(Section) : 0; + if (len > 0) { + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* Failed to find section, so add one to the end */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key!=NULL && Value!=NULL) { + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + /* Copy the line from source to dest, but not if this is the section that + * we are looking for and this section must be removed + */ + sp = skipleading(LocalBuffer); + ep = _tcsrchr(sp, ']'); + match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0); + if (!match || Key != NULL) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } while (!match); + } /* if */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + /* when deleting a section, the section head that was just found has not been + * copied to the output file, but because this line was not "accumulated" in + * the cache, the position in the input file was reset to the point just + * before the section; this must now be skipped (again) + */ + if (Key == NULL) { + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + (void)ini_tell(&rfp, &mark); + } /* if */ + + /* Now that the section has been found, find the entry. Stop searching + * upon leaving the section's area. Copy the file as it is read + * and create an entry if one is not found. + */ + len = (Key != NULL) ? (int)_tcslen(Key) : 0; + for( ;; ) { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* EOF without an entry so make one */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key!=NULL && Value!=NULL) { + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + match = (ep != NULL && len > 0 && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0); + if ((Key != NULL && match) || *sp == '[') + break; /* found the key, or found a new section */ + /* copy other keys in the section */ + if (Key == NULL) { + (void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */ + } else { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } /* for */ + /* the key was found, or we just dropped on the next section (meaning that it + * wasn't found); in both cases we need to write the key, but in the latter + * case, we also need to write the line starting the new section after writing + * the key + */ + flag = (*sp == '['); + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (Key != NULL && Value != NULL) + writekey(LocalBuffer, Key, Value, &wfp); + /* cache_flush() reset the "read pointer" to the start of the line with the + * previous key or the new section; read it again (because writekey() destroyed + * the buffer) + */ + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + if (flag) { + /* the new section heading needs to be copied to the output file */ + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } else { + /* forget the old key line */ + (void)ini_tell(&rfp, &mark); + } /* if */ + /* Copy the rest of the INI file */ + while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* while */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ +} + +/* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */ +#define ABS(v) ((v) < 0 ? -(v) : (v)) + +static void strreverse(TCHAR *str) +{ + int i, j; + for (i = 0, j = (int)_tcslen(str) - 1; i < j; i++, j--) { + TCHAR t = str[i]; + str[i] = str[j]; + str[j] = t; + } /* for */ +} + +static void long2str(long value, TCHAR *str) +{ + int i = 0; + long sign = value; + + /* generate digits in reverse order */ + do { + int n = (int)(value % 10); /* get next lowest digit */ + str[i++] = (TCHAR)(ABS(n) + '0'); /* handle case of negative digit */ + } while (value /= 10); /* delete the lowest digit */ + if (sign < 0) + str[i++] = '-'; + str[i] = '\0'; + + strreverse(str); +} + +/** ini_putl() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename) +{ + TCHAR LocalBuffer[32]; + long2str(Value, LocalBuffer); + return ini_puts(Section, Key, LocalBuffer, Filename); +} + +#if defined INI_REAL +/** ini_putf() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putf(const TCHAR *Section, const TCHAR *Key, INI_REAL Value, const TCHAR *Filename) +{ + TCHAR LocalBuffer[64]; + ini_ftoa(LocalBuffer, Value); + return ini_puts(Section, Key, LocalBuffer, Filename); +} +#endif /* INI_REAL */ + +static void putsection(TCHAR *LocalBuffer, const TCHAR *Section, const TCHAR **Keys, const TCHAR **Values, INI_FILETYPE *fp) { + if(Keys && Values && *Keys && *Values) { + writesection(LocalBuffer, Section, fp); + while(*Keys && *Values) { + writekey(LocalBuffer, *Keys, *Values, fp); + Keys++; + Values++; + } + (void)ini_write(INI_LINETERM, fp); /* force a new line behind the last line of the section */ + } +} + +int ini_putsection(const TCHAR *Section, const TCHAR **Keys, const TCHAR **Values, const TCHAR *Filename) +{ + INI_FILETYPE rfp; + INI_FILETYPE wfp; + INI_FILEPOS mark; + TCHAR *sp = NULL; + TCHAR *ep; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + int len, match, flag, cachelen; + + assert(Filename != NULL); + if (!ini_openread(Filename, &rfp)) { + /* If the .ini file doesn't exist, make a new file */ + if (!ini_openwrite(Filename, &wfp)) + return 0; + putsection(LocalBuffer, Section, Keys, Values, &wfp); + (void)ini_close(&wfp); + return 1; + } /* if */ + + /* Get a temporary file name to copy to. Use the existing name, but with + * the last character set to a '~'. + */ + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + if (!ini_openwrite(LocalBuffer, &wfp)) { + (void)ini_close(&rfp); + return 0; + } /* if */ + (void)ini_tell(&rfp, &mark); + cachelen = 0; + + /* Move through the file one line at a time until a section is + * matched or until EOF. Copy to temp file as it is read. + */ + len = (Section != NULL) ? (int)_tcslen(Section) : 0; + if (len > 0) { + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* Failed to find section, so add one to the end */ + flag = cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + if (!flag) + (void)ini_write(INI_LINETERM, &wfp); /* force a new line behind the last line of the INI file */ + + putsection(LocalBuffer, Section, Keys, Values, &wfp); + + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ + } /* if */ + /* Copy the line from source to dest, but not if this is the section that + * we are looking for + */ + sp = skipleading(LocalBuffer); + ep = _tcsrchr(sp, ']'); + match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0); + if (!match) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* if */ + } while (!match); + } /* if */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + /* when deleting a section, the section head that was just found has not been + * copied to the output file, but because this line was not "accumulated" in + * the cache, the position in the input file was reset to the point just + * before the section; this must now be skipped (again) + */ + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + (void)ini_tell(&rfp, &mark); + + /* Now that the section has been found, find the entry. Stop searching + * upon leaving the section's area. Copy the file as it is read + * and create an entry if one is not found. + */ + len = 0; + for( ;; ) { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* EOF without an entry */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */ + break; + } /* if */ + sp = skipleading(LocalBuffer); + if (*sp == '[') + break; /* found a new section */ + /* copy other keys in the section */ + (void)ini_tell(&rfp, &mark); /* we are deleting the entire section, so update the read position */ + } /* for */ + /* we just dropped on the next section + * we also need to write the line starting the new section after writing + */ + flag = (sp && *sp == '['); + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + putsection(LocalBuffer, Section, Keys, Values, &wfp); + + /* cache_flush() reset the "read pointer" to the start of the line with the + * previous key or the new section; read it again (because writekey() destroyed + * the buffer) + */ + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + if (flag) { + /* the new section heading needs to be copied to the output file */ + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } else { + /* forget the old key line */ + (void)ini_tell(&rfp, &mark); + } /* if */ + /* Copy the rest of the INI file */ + while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + if (!cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE)) { + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + (void)ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp); + cache_accum(LocalBuffer, &cachelen, INI_BUFFERSIZE); + } /* if */ + } /* while */ + cache_flush(LocalBuffer, &cachelen, &rfp, &wfp, &mark); + return close_rename(&rfp, &wfp, Filename, LocalBuffer); /* clean up and rename */ +} + +#endif /* !INI_READONLY */ diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minIni.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minIni.h new file mode 100644 index 00000000..73e33c01 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/minIni.h @@ -0,0 +1,68 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * Copyright (c) CompuPhase, 2008-2017 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: minIni.h 53 2015-01-18 13:35:11Z thiadmer.riemersma@gmail.com $ + */ +#ifndef MININI_H +#define MININI_H + +#include "minGlue.h" + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY + #include + #define mTCHAR TCHAR +#else + /* force TCHAR to be "char", but only for minIni */ + #define mTCHAR char +#endif + +#if !defined INI_BUFFERSIZE + #define INI_BUFFERSIZE 512 +#endif + +#if defined __cplusplus + extern "C" { +#endif + +int ini_getbool(const mTCHAR *Section, const mTCHAR *Key, int DefValue, const mTCHAR *Filename); +long ini_getl(const mTCHAR *Section, const mTCHAR *Key, long DefValue, const mTCHAR *Filename); +int ini_gets(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *DefValue, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getsection(int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); +int ini_getkey(const mTCHAR *Section, int idx, mTCHAR *Buffer, int BufferSize, const mTCHAR *Filename); + +#if defined INI_REAL +INI_REAL ini_getf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL DefValue, const mTCHAR *Filename); +#endif + +#if !defined INI_READONLY +int ini_putl(const mTCHAR *Section, const mTCHAR *Key, long Value, const mTCHAR *Filename); +int ini_puts(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, const mTCHAR *Filename); +int ini_putsection(const mTCHAR *Section, const mTCHAR **Keys, const mTCHAR **Values, const mTCHAR *Filename); +#if defined INI_REAL +int ini_putf(const mTCHAR *Section, const mTCHAR *Key, INI_REAL Value, const mTCHAR *Filename); +#endif +#endif /* INI_READONLY */ + +#if !defined INI_NOBROWSE +typedef int (*INI_CALLBACK)(const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData); +int ini_browse(INI_CALLBACK Callback, void *UserData, const mTCHAR *Filename); +#endif /* INI_NOBROWSE */ + +#if defined __cplusplus + } +#endif + +#endif /* MININI_H */ diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test.c b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test.c new file mode 100644 index 00000000..80508cd7 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test.c @@ -0,0 +1,117 @@ +/* Simple test program + * + * gcc -o test test.c minIni.c + */ +#include +#include +#include +#include "minIni.h" + +#define sizearray(a) (sizeof(a) / sizeof((a)[0])) + +const char inifile[] = "test.ini"; +const char inifile2[] = "testplain.ini"; + +int Callback(const char *section, const char *key, const char *value, void *userdata) +{ + (void)userdata; /* this parameter is not used in this example */ + printf(" [%s]\t%s=%s\n", section, key, value); + return 1; +} + +int main(void) +{ + char str[100]; + long n; + int s, k; + char section[50]; + + /* string reading */ + n = ini_gets("first", "string", "dummy", str, sizearray(str), inifile); + assert(n==4 && strcmp(str,"noot")==0); + n = ini_gets("second", "string", "dummy", str, sizearray(str), inifile); + assert(n==4 && strcmp(str,"mies")==0); + n = ini_gets("first", "undefined", "dummy", str, sizearray(str), inifile); + assert(n==5 && strcmp(str,"dummy")==0); + /* ----- */ + n = ini_gets("", "string", "dummy", str, sizearray(str), inifile2); + assert(n==4 && strcmp(str,"noot")==0); + n = ini_gets(NULL, "string", "dummy", str, sizearray(str), inifile2); + assert(n==4 && strcmp(str,"noot")==0); + /* ----- */ + printf("1. String reading tests passed\n"); + + /* value reading */ + n = ini_getl("first", "val", -1, inifile); + assert(n==1); + n = ini_getl("second", "val", -1, inifile); + assert(n==2); + n = ini_getl("first", "undefined", -1, inifile); + assert(n==-1); + /* ----- */ + n = ini_getl(NULL, "val", -1, inifile2); + assert(n==1); + /* ----- */ + printf("2. Value reading tests passed\n"); + + /* string writing */ + n = ini_puts("first", "alt", "flagged as \"correct\"", inifile); + assert(n==1); + n = ini_gets("first", "alt", "dummy", str, sizearray(str), inifile); + assert(n==20 && strcmp(str,"flagged as \"correct\"")==0); + /* ----- */ + n = ini_puts("second", "alt", "correct", inifile); + assert(n==1); + n = ini_gets("second", "alt", "dummy", str, sizearray(str), inifile); + assert(n==7 && strcmp(str,"correct")==0); + /* ----- */ + n = ini_puts("third", "test", "correct", inifile); + assert(n==1); + n = ini_gets("third", "test", "dummy", str, sizearray(str), inifile); + assert(n==7 && strcmp(str,"correct")==0); + /* ----- */ + n = ini_puts("second", "alt", "overwrite", inifile); + assert(n==1); + n = ini_gets("second", "alt", "dummy", str, sizearray(str), inifile); + assert(n==9 && strcmp(str,"overwrite")==0); + /* ----- */ + n = ini_puts("second", "alt", "123456789", inifile); + assert(n==1); + n = ini_gets("second", "alt", "dummy", str, sizearray(str), inifile); + assert(n==9 && strcmp(str,"123456789")==0); + /* ----- */ + n = ini_puts(NULL, "alt", "correct", inifile2); + assert(n==1); + n = ini_gets(NULL, "alt", "dummy", str, sizearray(str), inifile2); + assert(n==7 && strcmp(str,"correct")==0); + /* ----- */ + printf("3. String writing tests passed\n"); + + /* section/key enumeration */ + printf("4. Section/key enumeration, file structure follows\n"); + for (s = 0; ini_getsection(s, section, sizearray(section), inifile) > 0; s++) { + printf(" [%s]\n", section); + for (k = 0; ini_getkey(section, k, str, sizearray(str), inifile) > 0; k++) { + printf("\t%s\n", str); + } /* for */ + } /* for */ + + /* browsing through the file */ + printf("5. browse through all settings, file field list follows\n"); + ini_browse(Callback, NULL, inifile); + + /* string deletion */ + n = ini_puts("first", "alt", NULL, inifile); + assert(n==1); + n = ini_puts("second", "alt", NULL, inifile); + assert(n==1); + n = ini_puts("third", NULL, NULL, inifile); + assert(n==1); + /* ----- */ + n = ini_puts(NULL, "alt", NULL, inifile2); + assert(n==1); + printf("6. String deletion tests passed\n"); + + return 0; +} + diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test.ini b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test.ini new file mode 100644 index 00000000..565aef78 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test.ini @@ -0,0 +1,8 @@ +[First] +String=noot # trailing commment +Val=1 + +[Second] +Val = 2 +#comment=3 +String = mies diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test2.cc b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test2.cc new file mode 100644 index 00000000..4e19b319 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/test2.cc @@ -0,0 +1,80 @@ +/* + gcc -o minIni.o -c minIni.c + g++ -o test2.o -c test2.cc + g++ -o test2 test2.o minIni.o + ./test2 +*/ + + +#include +#include +#include +using namespace std ; + +#include "minIni.h" + +int main(void) +{ + minIni ini("test.ini"); + string s; + + /* string reading */ + s = ini.gets( "first", "string" , "aap" ); + assert(s == "noot"); + s = ini.gets( "second", "string" , "aap" ); + assert(s == "mies"); + s = ini.gets( "first", "dummy" , "aap" ); + assert(s == "aap"); + cout << "1. String reading tests passed" << endl ; + + + /* value reading */ + long n; + n = ini.getl("first", "val", -1 ); + assert(n==1); + n = ini.getl("second", "val", -1); + assert(n==2); + n = ini.getl("first", "dummy", -1); + assert(n==-1); + cout << "2. Value reading tests passed" << endl ; + + + /* string writing */ + bool b; + b = ini.put("first", "alt", "flagged as \"correct\""); + assert(b); + s = ini.gets("first", "alt", "aap"); + assert(s=="flagged as \"correct\""); + + b = ini.put("second", "alt", "correct"); + assert(b); + s = ini.gets("second", "alt", "aap"); + assert(s=="correct"); + + b = ini.put("third", "alt", "correct"); + assert(b); + s = ini.gets("third", "alt", "aap" ); + assert(s=="correct"); + cout << "3. String writing tests passed" << endl; + + /* section/key enumeration */ + cout << "4. section/key enumeration; file contents follows" << endl; + string section; + for (int is = 0; section = ini.getsection(is), section.length() > 0; is++) { + cout << " [" << section.c_str() << "]" << endl; + for (int ik = 0; s = ini.getkey(section, ik), s.length() > 0; ik++) { + cout << "\t" << s.c_str() << endl; + } + } + + /* string deletion */ + b = ini.del("first", "alt"); + assert(b); + b = ini.del("second", "alt"); + assert(b); + b = ini.del("third"); + assert(b); + cout << "5. string deletion passed " << endl; + + return 0; +} diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/testplain.ini b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/testplain.ini new file mode 100644 index 00000000..2a5ce4b6 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/testplain.ini @@ -0,0 +1,3 @@ +String=noot # trailing commment +#comment=3 +Val=1 diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/wxMinIni.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/wxMinIni.h new file mode 100644 index 00000000..f932bb63 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/dev/wxMinIni.h @@ -0,0 +1,101 @@ +/* minIni - Multi-Platform INI file parser, wxWidgets interface + * + * Copyright (c) CompuPhase, 2008-2012 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: wxMinIni.h 44 2012-01-04 15:52:56Z thiadmer.riemersma@gmail.com $ + */ +#ifndef WXMININI_H +#define WXMININI_H + +#include +#include "minIni.h" + +class minIni +{ +public: + minIni(const wxString& filename) : iniFilename(filename) + { } + + bool getbool(const wxString& Section, const wxString& Key, bool DefValue=false) const + { return ini_getbool(Section.utf8_str(), Key.utf8_str(), int(DefValue), iniFilename.utf8_str()) != 0; } + + long getl(const wxString& Section, const wxString& Key, long DefValue=0) const + { return ini_getl(Section.utf8_str(), Key.utf8_str(), DefValue, iniFilename.utf8_str()); } + + int geti(const wxString& Section, const wxString& Key, int DefValue=0) const + { return static_cast(ini_getl(Section.utf8_str(), Key.utf8_str(), (long)DefValue, iniFilename.utf8_str())); } + + wxString gets(const wxString& Section, const wxString& Key, const wxString& DefValue=wxT("")) const + { + char buffer[INI_BUFFERSIZE]; + ini_gets(Section.utf8_str(), Key.utf8_str(), DefValue.utf8_str(), buffer, INI_BUFFERSIZE, iniFilename.utf8_str()); + wxString result = wxString::FromUTF8(buffer); + return result; + } + + wxString getsection(int idx) const + { + char buffer[INI_BUFFERSIZE]; + ini_getsection(idx, buffer, INI_BUFFERSIZE, iniFilename.utf8_str()); + wxString result = wxString::FromUTF8(buffer); + return result; + } + + wxString getkey(const wxString& Section, int idx) const + { + char buffer[INI_BUFFERSIZE]; + ini_getkey(Section.utf8_str(), idx, buffer, INI_BUFFERSIZE, iniFilename.utf8_str()); + wxString result = wxString::FromUTF8(buffer); + return result; + } + +#if defined INI_REAL + INI_REAL getf(const wxString& Section, wxString& Key, INI_REAL DefValue=0) const + { return ini_getf(Section.utf8_str(), Key.utf8_str(), DefValue, iniFilename.utf8_str()); } +#endif + +#if ! defined INI_READONLY + bool put(const wxString& Section, const wxString& Key, long Value) const + { return ini_putl(Section.utf8_str(), Key.utf8_str(), Value, iniFilename.utf8_str()) != 0; } + + bool put(const wxString& Section, const wxString& Key, int Value) const + { return ini_putl(Section.utf8_str(), Key.utf8_str(), (long)Value, iniFilename.utf8_str()) != 0; } + + bool put(const wxString& Section, const wxString& Key, bool Value) const + { return ini_putl(Section.utf8_str(), Key.utf8_str(), (long)Value, iniFilename.utf8_str()) != 0; } + + bool put(const wxString& Section, const wxString& Key, const wxString& Value) const + { return ini_puts(Section.utf8_str(), Key.utf8_str(), Value.utf8_str(), iniFilename.utf8_str()) != 0; } + + bool put(const wxString& Section, const wxString& Key, const char* Value) const + { return ini_puts(Section.utf8_str(), Key.utf8_str(), Value, iniFilename.utf8_str()) != 0; } + +#if defined INI_REAL + bool put(const wxString& Section, const wxString& Key, INI_REAL Value) const + { return ini_putf(Section.utf8_str(), Key.utf8_str(), Value, iniFilename.utf8_str()) != 0; } +#endif + + bool del(const wxString& Section, const wxString& Key) const + { return ini_puts(Section.utf8_str(), Key.utf8_str(), 0, iniFilename.utf8_str()) != 0; } + + bool del(const wxString& Section) const + { return ini_puts(Section.utf8_str(), 0, 0, iniFilename.utf8_str()) != 0; } +#endif + +private: + wxString iniFilename; +}; + +#endif /* WXMININI_H */ diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/doc/minIni.pdf b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/doc/minIni.pdf new file mode 100644 index 00000000..603d13ea Binary files /dev/null and b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/doc/minIni.pdf differ diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/minIni/include/minIni.h b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/include/minIni.h new file mode 100644 index 00000000..26520699 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/minIni/include/minIni.h @@ -0,0 +1,38 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "../dev/minIni.h" + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/.gitignore b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/.gitignore new file mode 100644 index 00000000..0806f7bf --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/.gitignore @@ -0,0 +1,13 @@ +# Editor files +*.swp +*~ + +# Objects +*.o +*.a +*.so + +# Lib +lib +release +debug \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/Makefile b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/Makefile new file mode 100644 index 00000000..34be0db8 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/Makefile @@ -0,0 +1,132 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +include $(DEVKITPRO)/libnx/switch_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +SOURCES := src +DATA := data +INCLUDES := include + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec + +CFLAGS := -g -Wall -Werror \ + -ffunction-sections \ + -fdata-sections \ + $(ARCH) \ + $(BUILD_CFLAGS) + +CFLAGS += $(INCLUDE) -std=gnu11 + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions + +ASFLAGS := -g $(ARCH) + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES_BIN := $(addsuffix .o,$(BINFILES)) +export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) +export OFILES := $(OFILES_BIN) $(OFILES_SRC) +export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +.PHONY: clean all lib/lib$(TARGET).a lib/lib$(TARGET)d.a + +#--------------------------------------------------------------------------------- +all: lib/lib$(TARGET).a lib/lib$(TARGET)d.a + +lib: + @[ -d $@ ] || mkdir -p $@ + +release: + @[ -d $@ ] || mkdir -p $@ + +debug: + @[ -d $@ ] || mkdir -p $@ + +lib/lib$(TARGET).a : lib release $(SOURCES) $(INCLUDES) + @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ + BUILD_CFLAGS="-DNDEBUG=1 -O2" \ + DEPSDIR=$(CURDIR)/release \ + --no-print-directory -C release \ + -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr release debug lib + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT) : $(OFILES) + +$(OFILES_SRC) : $(HFILES) + +#--------------------------------------------------------------------------------- +%_bin.h %.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt.h b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt.h new file mode 100644 index 00000000..29369887 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt.h @@ -0,0 +1,36 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include "nxExt/apm_ext.h" +#include "nxExt/i2c.h" +#include "nxExt/t210.h" +#include "nxExt/max17050.h" +#include "nxExt/tmp451.h" +#include "nxExt/ipc_server.h" +#include "nxExt/cpp/lockable_mutex.h" diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/apm_ext.h b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/apm_ext.h new file mode 100644 index 00000000..e032fb35 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/apm_ext.h @@ -0,0 +1,58 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +Result apmExtInitialize(void); +void apmExtExit(void); + +Result apmExtGetPerformanceMode(u32* out_mode); +Result apmExtSysRequestPerformanceMode(u32 mode); +Result apmExtGetCurrentPerformanceConfiguration(u32* out_conf); +Result apmExtSysRequestPerformanceMode(u32 mode); +Result apmExtSysSetCpuBoostMode(u32 mode); + +Result apmExtGetPerformanceMode(u32 *out_mode); +Result apmExtGetCurrentPerformanceConfiguration(u32 *out_conf); + +inline bool apmExtIsCPUBoosted(u32 conf_id) { // CPU boosted to 1785 MHz + return (conf_id == 0x92220009 || conf_id == 0x9222000A); +}; +inline bool apmExtIsBoostMode(u32 conf_id) { // GPU throttled to 76.8 MHz + return (conf_id >= 0x92220009 && conf_id <= 0x9222000C); +}; + +#ifdef __cplusplus +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/cpp/lockable_mutex.h b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/cpp/lockable_mutex.h new file mode 100644 index 00000000..f2c9f60a --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/cpp/lockable_mutex.h @@ -0,0 +1,81 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#ifdef __cplusplus + +#include +#include + +class LockableMutex +{ +public: + LockableMutex() + { + mutexInit(&this->m); + } + + virtual ~LockableMutex() {} + + void Lock() + { + mutexLock(&this->m); + } + + bool TryLock() + { + return mutexTryLock(&this->m); + } + + void Unlock() + { + mutexUnlock(&this->m); + } + + // snake_case aliases in order to implement Lockable + + void lock() + { + this->Lock(); + } + + bool try_lock() + { + return this->TryLock(); + } + + void unlock() + { + this->Unlock(); + } + +private: + Mutex m; +}; + +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/i2c.h b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/i2c.h new file mode 100644 index 00000000..c3dbe340 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/i2c.h @@ -0,0 +1,41 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +Result i2csessionExtRegReceive(I2cSession* s, u8 in, void* out, u8 out_size); + +#ifdef __cplusplus +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/ipc_server.h b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/ipc_server.h new file mode 100644 index 00000000..0c8f294b --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/ipc_server.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#define IPC_SERVER_EXT_RESPONSE_MAX_DATA_SIZE (0x100 - 0x10 - sizeof(IpcServerRawHeader)) + +typedef struct +{ + u64 magic; + union + { + u64 cmdId; + u64 result; + }; +} IpcServerRawHeader; + +typedef struct +{ + SmServiceName srvName; + Handle handles[MAX_WAIT_OBJECTS]; + u32 max; + u32 count; +} IpcServer; + +typedef struct +{ + u64 cmdId; + void* ptr; + size_t size; +} IpcServerRequestData; + +typedef struct +{ + HipcParsedRequest hipc; + IpcServerRequestData data; +} IpcServerRequest; + +typedef Result (*IpcServerRequestHandler)(void* userdata, const IpcServerRequest* r, u8* out_data, size_t* out_dataSize); + +Result ipcServerInit(IpcServer* server, const char* name, u32 max_sessions); +Result ipcServerExit(IpcServer* server); +Result ipcServerProcess(IpcServer* server, IpcServerRequestHandler handler, void* userdata); +Result ipcServerParseCommand(const IpcServerRequest* r, size_t* out_datasize, void** out_data, u64* out_cmd); + +#ifdef __cplusplus +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/max17050.h b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/max17050.h new file mode 100644 index 00000000..a22046db --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/max17050.h @@ -0,0 +1,44 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +Result max17050Initialize(void); +void max17050Exit(void); +s32 max17050PowerNow(void); +s32 max17050PowerAvg(void); + +#ifdef __cplusplus +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/t210.h b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/t210.h new file mode 100644 index 00000000..9e5b41e0 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/t210.h @@ -0,0 +1,45 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +u32 t210ClkCpuFreq(void); +u32 t210ClkMemFreq(void); +u32 t210ClkGpuFreq(void); +u32 t210EmcLoadAll(void); +u32 t210EmcLoadCpu(void); + +#ifdef __cplusplus +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/tmp451.h b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/tmp451.h new file mode 100644 index 00000000..9faf2480 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/include/nxExt/tmp451.h @@ -0,0 +1,44 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +Result tmp451Initialize(void); +void tmp451Exit(void); +s32 tmp451TempPcb(void); +s32 tmp451TempSoc(void); + +#ifdef __cplusplus +} +#endif diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/apm_ext.c b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/apm_ext.c new file mode 100644 index 00000000..fba71f4f --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/apm_ext.c @@ -0,0 +1,83 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "nxExt/apm_ext.h" + +#include + +static Service g_apmSrv; +static Service g_apmSysSrv; +static atomic_size_t g_refCnt; + +Result apmExtInitialize(void) +{ + g_refCnt++; + + if (serviceIsActive(&g_apmSrv)) + { + return 0; + } + + Result rc = 0; + + rc = smGetService(&g_apmSrv, "apm"); + if(R_SUCCEEDED(rc)) + { + rc = smGetService(&g_apmSysSrv, "apm:sys"); + } + + if (R_FAILED(rc)) + { + apmExtExit(); + } + + return rc; +} + +void apmExtExit(void) +{ + if (--g_refCnt == 0) + { + serviceClose(&g_apmSrv); + serviceClose(&g_apmSysSrv); + } +} + +Result apmExtGetPerformanceMode(u32* out_mode) +{ + return serviceDispatchOut(&g_apmSrv, 1, *out_mode); +} + +Result apmExtSysRequestPerformanceMode(u32 mode) +{ + return serviceDispatchIn(&g_apmSysSrv, 0, mode); +} + +Result apmExtGetCurrentPerformanceConfiguration(u32* out_conf) +{ + return serviceDispatchOut(&g_apmSysSrv, 7, *out_conf); +} diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/i2c.c b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/i2c.c new file mode 100644 index 00000000..d5db4ac1 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/i2c.c @@ -0,0 +1,45 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "nxExt/i2c.h" + +#define I2C_CMD_SND 0 +#define I2C_CMD_RCV 1 + +Result i2csessionExtRegReceive(I2cSession* s, u8 in, void* out, u8 out_size) +{ + u8 cmdlist[5] = { + I2C_CMD_SND | (I2cTransactionOption_Start << 6), + sizeof(in), + in, + + I2C_CMD_RCV | (I2cTransactionOption_All << 6), + out_size + }; + + return i2csessionExecuteCommandList(s, out, out_size, cmdlist, sizeof(cmdlist)); +} diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/ipc_server.c b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/ipc_server.c new file mode 100644 index 00000000..68e92ff8 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/ipc_server.c @@ -0,0 +1,221 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "nxExt/ipc_server.h" +#include + +Result ipcServerInit(IpcServer* server, const char* name, u32 max_sessions) +{ + if(max_sessions < 1 || max_sessions > (MAX_WAIT_OBJECTS - 1)) + { + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } + + server->srvName = smEncodeName(name); + server->max = max_sessions + 1; + server->count = 0; + + Result rc = smRegisterService(&server->handles[0], server->srvName, false, max_sessions); + if(R_SUCCEEDED(rc)) + { + server->count = 1; + } + return rc; +} + +Result ipcServerExit(IpcServer* server) +{ + for(u32 i = 0; i < server->count; i++) + { + svcCloseHandle(server->handles[i]); + } + server->count = 0; + return smUnregisterService(server->srvName); +} + +static Result _ipcServerAddSession(IpcServer* server, Handle session) +{ + if(server->count >= server->max) + { + return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + } + + server->handles[server->count] = session; + server->count++; + return 0; +} + +static Result _ipcServerDeleteSession(IpcServer* server, u32 index) +{ + if(!index || index >= server->count) + { + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } + + svcCloseHandle(server->handles[index]); + + for(u32 j = index; j < (server->count - 1); j++) + { + server->handles[j] = server->handles[j + 1]; + } + server->count--; + return 0; +} + +static Result _ipcServerParseRequest(IpcServerRequest* r) +{ + u8* base = armGetTls(); + + r->hipc = hipcParseRequest(base); + r->data.cmdId = 0; + r->data.size = 0; + r->data.ptr = NULL; + + if(r->hipc.meta.type == CmifCommandType_Request) + { + IpcServerRawHeader* header = cmifGetAlignedDataStart(r->hipc.data.data_words, base); + size_t dataSize = r->hipc.meta.num_data_words * 4; + + if(!header || dataSize < sizeof(IpcServerRawHeader) || header->magic != CMIF_IN_HEADER_MAGIC) + { + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } + + r->data.cmdId = header->cmdId; + if(dataSize > sizeof(IpcServerRawHeader)) + { + r->data.size = dataSize - sizeof(IpcServerRawHeader); + r->data.ptr = ((u8*)header) + sizeof(IpcServerRawHeader); + } + } + + return 0; +} + +static void _ipcServerPrepareResponse(Result rc, void* data, size_t dataSize) +{ + u8* base = armGetTls(); + HipcRequest hipc = hipcMakeRequestInline(base, + .type = CmifCommandType_Request, + .num_data_words = (sizeof(IpcServerRawHeader) + dataSize + 0x10) / 4, + ); + + IpcServerRawHeader* rawHeader = cmifGetAlignedDataStart(hipc.data_words, base); + rawHeader->magic = CMIF_OUT_HEADER_MAGIC; + rawHeader->result = rc; + + if(R_SUCCEEDED(rc)) + { + memcpy(((u8*)rawHeader) + sizeof(IpcServerRawHeader), data, dataSize); + } +} + +static Result _ipcServerProcessNewSession(IpcServer* server) +{ + Handle session; + Result rc = svcAcceptSession(&session, server->handles[0]); + if(R_SUCCEEDED(rc) && R_FAILED(rc = _ipcServerAddSession(server, session))) + { + svcCloseHandle(session); + } + return rc; +} + +static Result _ipcServerProcessSession(IpcServer* server, IpcServerRequestHandler handler, void* userdata, u32 handleIndex) +{ + s32 unusedIndex; + IpcServerRequest r; + size_t dataSize = 0; + u8 data[IPC_SERVER_EXT_RESPONSE_MAX_DATA_SIZE]; + bool close = false; + + Result rc = svcReplyAndReceive(&unusedIndex, &server->handles[handleIndex], 1, 0, UINT64_MAX); + if(R_SUCCEEDED(rc)) + { + rc = _ipcServerParseRequest(&r); + } + + if(R_SUCCEEDED(rc)) + { + switch(r.hipc.meta.type) + { + case CmifCommandType_Request: + _ipcServerPrepareResponse( + handler(userdata, &r, data, &dataSize), + data, + dataSize + ); + break; + case CmifCommandType_Close: + _ipcServerPrepareResponse(0, NULL, 0); + close = true; + break; + default: + _ipcServerPrepareResponse(MAKERESULT(11, 403), NULL, 0); + break; + } + + rc = svcReplyAndReceive(&unusedIndex, &server->handles[handleIndex], 0, server->handles[handleIndex], 0); + if(rc == KERNELRESULT(TimedOut)) + { + rc = 0; + } + } + + if(R_FAILED(rc) || close) + { + _ipcServerDeleteSession(server, handleIndex); + } + + return rc; +} + +Result ipcServerProcess(IpcServer* server, IpcServerRequestHandler handler, void* userdata) +{ + s32 handleIndex = -1; + Result rc = svcWaitSynchronization(&handleIndex, server->handles, server->count, UINT64_MAX); + + if(R_SUCCEEDED(rc) && (handleIndex < 0 || handleIndex >= server->count)) + { + rc = MAKERESULT(Module_Libnx, LibnxError_NotFound); + } + + if(R_SUCCEEDED(rc)) + { + if(handleIndex) + { + rc = _ipcServerProcessSession(server, handler, userdata, handleIndex); + } + else + { + rc = _ipcServerProcessNewSession(server); + } + } + + return rc; +} + diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/max17050.c b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/max17050.c new file mode 100644 index 00000000..fe6d00bc --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/max17050.c @@ -0,0 +1,124 @@ +/* + * Fuel gauge driver for Nintendo Switch's Maxim 17050 + * + * Copyright (c) 2011 Samsung Electronics + * MyungJoo Ham + * Copyright (c) 2018 CTCaer + * Copyright (c) 2022 p-sam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "nxExt/max17050.h" +#include "nxExt/i2c.h" + +#define MAX17050_WAIT_NS 1000000000UL + +#define MAX17050_VCELL 0x09 +#define MAX17050_Current 0x0A +#define MAX17050_AvgCurrent 0x0B +#define MAX17050_AvgVCELL 0x19 + +#define MAX17050_BOARD_CGAIN 2 +#define MAX17050_BOARD_SNS_RESISTOR_UOHM 5000 + +static I2cSession g_i2c_session; +static u64 g_update_ticks = 0; +static s32 g_power_now = 0; +static s32 g_power_avg = 0; + +static Result _max17050_get_power(s32 *out_mw_now, s32 *out_mw_avg) +{ + s64 ma, mv; + u16 values[3] = {0}; + + Result rc = i2csessionExtRegReceive(&g_i2c_session, MAX17050_VCELL, values, sizeof(values)); + + if (R_SUCCEEDED(rc)) + { + ma = (s16)values[1]; + ma = ma * 1562500 / (MAX17050_BOARD_SNS_RESISTOR_UOHM * MAX17050_BOARD_CGAIN); + + mv = (int)(values[0] >> 3) * 625 / 1000; + + *out_mw_now = ma * mv / 1000000; + } + + if (R_SUCCEEDED(rc)) + { + rc = i2csessionExtRegReceive(&g_i2c_session, MAX17050_AvgVCELL, values, sizeof(u16)); + } + + if (R_SUCCEEDED(rc)) + { + ma = (s16)values[2]; + ma = ma * 1562500 / (MAX17050_BOARD_SNS_RESISTOR_UOHM * MAX17050_BOARD_CGAIN); + + mv = (int)(values[0] >> 3) * 625 / 1000; + + *out_mw_avg = ma * mv / 1000000; + } + + return rc; +} + +static void _max17050_update() +{ + u64 ticks = armGetSystemTick(); + if(armTicksToNs(ticks - g_update_ticks) <= MAX17050_WAIT_NS) + { + return; + } + + g_update_ticks = ticks; + + if(!serviceIsActive(&g_i2c_session.s)) + { + return; + } + + _max17050_get_power(&g_power_now, &g_power_avg); +} + +Result max17050Initialize(void) +{ + Result rc = i2cInitialize(); + + if(R_SUCCEEDED(rc)) + { + rc = i2cOpenSession(&g_i2c_session, I2cDevice_Max17050); + } + + return rc; +} + +void max17050Exit(void) +{ + i2csessionClose(&g_i2c_session); + i2cExit(); +} + +s32 max17050PowerNow(void) +{ + _max17050_update(); + return g_power_now; +} + +s32 max17050PowerAvg(void) +{ + _max17050_update(); + return g_power_avg; +} diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/t210.c b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/t210.c new file mode 100644 index 00000000..91ce4ec2 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/t210.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2020-2023 CTCaer + * Copyright (c) 2023 p-sam + * Copyright (c) 2026 Souldbminer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "nxExt/t210.h" + +#define WAIT_NS 1000000000UL + +#define usleep(x) svcSleepThread(1000UL * x) + +#define GPU_TRIM_SYS_GPCPLL_COEFF 0x4 +#define GPU_TRIM_SYS_GPCPLL(x) (*(volatile u32 *)(g_gpu_base + 0x137000ul + (x))) + +#define CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL 0x60 +#define CLK_RST_CONTROLLER_PTO_CLK_CNT_STATUS 0x64 +#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X 0x280 +#define CLK_RST_CONTROLLER_RST_DEVICES_X 0x28C + +/*! PTO_CLK_CNT */ +#define PTO_REF_CLK_WIN_CFG_MASK 0xF +#define PTO_REF_CLK_WIN_CFG_16P 0xF +#define PTO_CNT_EN BIT(9) +#define PTO_CNT_RST BIT(10) +#define PTO_CLK_ENABLE BIT(13) +#define PTO_SRC_SEL_SHIFT 14 +#define PTO_SRC_SEL_MASK 0x1FF +#define PTO_DIV_SEL_MASK (3 << 23) +#define PTO_DIV_SEL_GATED (0 << 23) +#define PTO_DIV_SEL_DIV1 (1 << 23) +#define PTO_DIV_SEL_DIV2_RISING (2 << 23) +#define PTO_DIV_SEL_DIV2_FALLING (3 << 23) +#define PTO_DIV_SEL_CPU_EARLY (0 << 23) +#define PTO_DIV_SEL_CPU_LATE (1 << 23) + +#define PTO_CLK_CNT_BUSY BIT(31) +#define PTO_CLK_CNT 0xFFFFFF +#define CLK_PTO_CCLK_G_DIV2 0x13 +#define CLK_PTO_EMC 0x24 + +#define CLOCK(x) (*(volatile u32 *)(g_clk_base + (x))) + +/* Actmon Global registers */ +#define ACTMON_GLB_STATUS 0x0 +#define ACTMON_MCCPU_MON_ACT BIT(8) +#define ACTMON_MCALL_MON_ACT BIT(9) +#define ACTMON_CPU_FREQ_MON_ACT BIT(10) +#define ACTMON_BPMP_MON_ACT BIT(14) +#define ACTMON_CPU_MON_ACT BIT(15) + +#define ACTMON_GLB_PERIOD_CTRL 0x4 +#define ACTMON_GLB_PERIOD_USEC BIT(8) +#define ACTMON_GLB_PERIOD_SAMPLE(n) (((n) - 1) & 0xFF) + +/* Actmon Device Registers */ +#define ACTMON_DEV_SIZE 0x40 +/* Actmon CTRL */ +#define ACTMON_DEV_CTRL_K_VAL(k) (((k) & 7) << 10) +#define ACTMON_DEV_CTRL_ENB_PERIODIC BIT(18) +#define ACTMON_DEV_CTRL_ENB BIT(31) + +#define ACTMON_PERIOD_MS 20 +#define DEV_COUNT_WEIGHT 1024 + +#define ACTMON_BASE (g_act_base + 0x800) +#define ACTMON_DEV_BASE (ACTMON_BASE + 0x80) +#define ACTMON(x) (*(volatile u32 *)(ACTMON_BASE + (x))) + +typedef enum _actmon_dev_t +{ + ACTMON_DEV_CPU, + ACTMON_DEV_BPMP, + ACTMON_DEV_AHB, + ACTMON_DEV_APB, + ACTMON_DEV_CPU_FREQ, + ACTMON_DEV_MC_ALL, + ACTMON_DEV_MC_CPU, + + ACTMON_DEV_NUM, +} actmon_dev_t; + +typedef struct _actmon_dev_reg_t +{ + vu32 ctrl; + vu32 upper_wnark; + vu32 lower_wmark; + vu32 init_avg; + vu32 avg_upper_wmark; + vu32 avg_lower_wmark; + vu32 count_weight; + vu32 count; + vu32 avg_count; + vu32 intr_status; + vu32 ctrl2; + vu32 rsvd[5]; +} actmon_dev_reg_t; + +static uintptr_t g_clk_base = 0; +static uintptr_t g_gpu_base = 0; +static uintptr_t g_act_base = 0; +static u64 g_update_ticks = 0; +static u32 g_cpu_freq = 0; +static u32 g_gpu_freq = 0; +static u32 g_mem_freq = 0; +static u32 g_emc_lall = 0; +static u32 g_emc_lcpu = 0; + +static u32 _clock_get_dev_freq(u32 id, u32 multiplier) +{ + const u32 pto_win = 16; + const u32 pto_osc = 32768; + + u32 val = ((id & PTO_SRC_SEL_MASK) << PTO_SRC_SEL_SHIFT) | PTO_DIV_SEL_DIV1 | PTO_CLK_ENABLE | (pto_win - 1); + CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val; + (void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL); + usleep(2); + + CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val | PTO_CNT_RST; + (void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL); + usleep(2); + + CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val; + (void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL); + usleep(2); + + CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = val | PTO_CNT_EN; + (void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL); + usleep((1000000ULL * pto_win / pto_osc) + 12 + 2); // 502 us. + + while (CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_STATUS) & PTO_CLK_CNT_BUSY) + ; + + u32 cnt = CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_STATUS) & PTO_CLK_CNT; + + CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL) = 0; + (void)CLOCK(CLK_RST_CONTROLLER_PTO_CLK_CNT_CNTL); + usleep(2); + + u32 freq_khz = (u64)cnt * multiplier * pto_osc / pto_win; + + return freq_khz; +} + +static void _actmon_dev_enable(actmon_dev_t dev, u32 freq, u32 weight) +{ + actmon_dev_reg_t *regs = (actmon_dev_reg_t *)(ACTMON_DEV_BASE + (dev * ACTMON_DEV_SIZE)); + + regs->init_avg = (u32)freq * ACTMON_PERIOD_MS / 2; + regs->count_weight = weight; + + regs->ctrl = ACTMON_DEV_CTRL_ENB | ACTMON_DEV_CTRL_ENB_PERIODIC | ACTMON_DEV_CTRL_K_VAL(3); // 8 samples average. +} + +static u32 _actmon_dev_get_count_avg(actmon_dev_t dev) +{ + actmon_dev_reg_t *regs = (actmon_dev_reg_t *)(ACTMON_DEV_BASE + (dev * ACTMON_DEV_SIZE)); + + return regs->avg_count; +} + +static inline Result _svcQueryMemoryMappingFallback(u64* virtaddr, u64 physaddr, u64 size) +{ + if(hosversionAtLeast(10,0,0)) + { + u64 out_size; + return svcQueryMemoryMapping(virtaddr, &out_size, physaddr, size); + } + else + { + return svcLegacyQueryIoMapping(virtaddr, physaddr, size); + } +} + +static void _clock_update_freqs(void) +{ + u64 ticks = armGetSystemTick(); + if(armTicksToNs(ticks - g_update_ticks) <= WAIT_NS) + { + return; + } + + g_update_ticks = ticks; + + if (!g_clk_base) + { + _svcQueryMemoryMappingFallback(&g_clk_base, 0x60006000ul, 0x1000); + } + + if(!g_clk_base) + { + return; + } + + g_mem_freq = _clock_get_dev_freq(CLK_PTO_EMC, 1); + g_cpu_freq = _clock_get_dev_freq(CLK_PTO_CCLK_G_DIV2, 2); + + if (!g_gpu_base) + { + _svcQueryMemoryMappingFallback(&g_gpu_base, 0x57000000ul, 0x1000000); + } + + if (!g_gpu_base) + { + return; + } + + bool gpu_enabled = (CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_X) & BIT(24)) && !(CLOCK(CLK_RST_CONTROLLER_RST_DEVICES_X) & BIT(24)); + if(!gpu_enabled) + { + return; + } + + if (!g_act_base) + { + _svcQueryMemoryMappingFallback(&g_act_base, 0x6000C000ul, 0x1000); + } + + if(!g_act_base) + { + return; + } + + const u32 osc = 38400000; + u32 coeff = GPU_TRIM_SYS_GPCPLL(GPU_TRIM_SYS_GPCPLL_COEFF); + u32 divm = coeff & 0xFF; + u32 divn = (coeff >> 8) & 0xFF; + u32 divp = (coeff >> 16) & 0x3F; + g_gpu_freq = osc * divn / (divm * divp) / 2; + + u32 emc_freq = g_mem_freq / 1000; + + // Check if actmon is disabled + if (!(ACTMON(ACTMON_GLB_STATUS) & ACTMON_MCALL_MON_ACT)) + { + ACTMON(ACTMON_GLB_PERIOD_CTRL) = ACTMON_GLB_PERIOD_SAMPLE(ACTMON_PERIOD_MS); + _actmon_dev_enable(ACTMON_DEV_MC_ALL, emc_freq, 256 * 4); + } + + // Check if actmon is disabled + if (!(ACTMON(ACTMON_GLB_STATUS) & ACTMON_MCCPU_MON_ACT)) + _actmon_dev_enable(ACTMON_DEV_MC_CPU, emc_freq, 256 * 4); + + // Get 1000 -> 100.0. + g_emc_lall = (u64)_actmon_dev_get_count_avg(ACTMON_DEV_MC_ALL) * 10 * 100 / (emc_freq * ACTMON_PERIOD_MS); + g_emc_lcpu = (u64)_actmon_dev_get_count_avg(ACTMON_DEV_MC_CPU) * 10 * 100 / (emc_freq * ACTMON_PERIOD_MS); +} + + +u32 t210ClkCpuFreq(void) +{ + _clock_update_freqs(); + return g_cpu_freq; +} + +u32 t210ClkMemFreq(void) +{ + _clock_update_freqs(); + return g_mem_freq; +} + +u32 t210ClkGpuFreq(void) +{ + _clock_update_freqs(); + return g_gpu_freq; +} + +u32 t210EmcLoadAll() +{ + _clock_update_freqs(); + return g_emc_lall; +} + +u32 t210EmcLoadCpu() +{ + _clock_update_freqs(); + return g_emc_lcpu; +} diff --git a/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/tmp451.c b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/tmp451.c new file mode 100644 index 00000000..90c8d1c2 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/lib/nxExt/src/tmp451.c @@ -0,0 +1,102 @@ +/* + * SOC/PCB Temperature driver for Nintendo Switch's TI TMP451 + * + * Copyright (c) 2018-2024 CTCaer + * Copyright (c) 2024 p-sam + * + * 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 . + */ + +#include "nxExt/tmp451.h" +#include "nxExt/i2c.h" + +#define TMP451_WAIT_NS 1000000000UL + +#define TMP451_PCB_TEMP_REG 0x00 +#define TMP451_SOC_TEMP_REG 0x01 + +#define TMP451_SOC_TMP_DEC_REG 0x10 +#define TMP451_PCB_TMP_DEC_REG 0x15 + +static I2cSession g_i2c_session; +static u64 g_update_ticks = 0; +static s32 g_temp_pcb = 0; +static s32 g_temp_soc = 0; + +static Result _tmp451_get_temp(u8 reg, u8 dec_reg, s32* out) +{ + u8 val = 0; + Result rc = i2csessionExtRegReceive(&g_i2c_session, reg, &val, sizeof(val)); + + if(R_SUCCEEDED(rc)) + { + *out = (s32)val * 1000; + rc = i2csessionExtRegReceive(&g_i2c_session, dec_reg, &val, sizeof(val)); + } + + if(R_SUCCEEDED(rc)) + { + *out += ((s32)(val >> 4) * 625) / 10; + } + + return rc; +} + +static void _tmp451_update() +{ + u64 ticks = armGetSystemTick(); + if(armTicksToNs(ticks - g_update_ticks) <= TMP451_WAIT_NS) + { + return; + } + + g_update_ticks = ticks; + + if(!serviceIsActive(&g_i2c_session.s)) + { + return; + } + + _tmp451_get_temp(TMP451_PCB_TEMP_REG, TMP451_PCB_TMP_DEC_REG, &g_temp_pcb); + _tmp451_get_temp(TMP451_SOC_TEMP_REG, TMP451_SOC_TMP_DEC_REG, &g_temp_soc); +} + +Result tmp451Initialize(void) +{ + Result rc = i2cInitialize(); + + if(R_SUCCEEDED(rc)) + { + rc = i2cOpenSession(&g_i2c_session, I2cDevice_Tmp451); + } + + return rc; +} + +void tmp451Exit(void) +{ + i2csessionClose(&g_i2c_session); + i2cExit(); +} + +s32 tmp451TempPcb(void) +{ + _tmp451_update(); + return g_temp_pcb; +} + +s32 tmp451TempSoc(void) +{ + _tmp451_update(); + return g_temp_soc; +} diff --git a/Source/rewrite-hoc-clk/sysmodule/perms.json b/Source/rewrite-hoc-clk/sysmodule/perms.json new file mode 100644 index 00000000..b5c09894 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/perms.json @@ -0,0 +1,240 @@ +{ + "name": "hoc:clk", + "title_id": "0x00FF0000636C6BFF", + "title_id_range_min": "0x00FF0000636C6BFF", + "title_id_range_max": "0x00FF0000636C6BFF", + "main_thread_stack_size": "0x0000C000", + "main_thread_priority": 16, + "default_cpu_id": 3, + "process_category": 1, + "is_retail": true, + "pool_partition": 2, + "is_64_bit": true, + "address_space_type": 3, + "filesystem_access": { + "permissions": "0xFFFFFFFFFFFFFFFF" + }, + "service_access": [ + "*" + ], + "service_host": [ + "hoc:clk" + ], + "kernel_capabilities": [ + { + "type": "kernel_flags", + "value": { + "highest_thread_priority": 63, + "lowest_thread_priority": 16, + "lowest_cpu_id": 0, + "highest_cpu_id": 3 + } + }, + { + "type": "map", + "value": { + "address": "0x60006000", + "size": "0x1000", + "is_ro": false, + "is_io": true + } + }, + { + "type": "map", + "value": { + "address": "0x57000000", + "size": "0x1000000", + "is_ro": false, + "is_io": true + } + }, + { + "type": "map", + "value": { + "address": "0x6000C000", + "size": "0x1000", + "is_ro": false, + "is_io": true + } + }, + { + "type": "map", + "value": { + "address": "0x70110000", + "size": "0x1000", + "is_ro": false, + "is_io": true + } + }, + { + "type": "syscalls", + "value": { + "svcUnknown": "0x00", + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcFlushEntireDataCache": "0x2a", + "svcFlushDataCache": "0x2b", + "svcMapPhysicalMemory": "0x2c", + "svcUnmapPhysicalMemory": "0x2d", + "svcGetFutureThreadInfo": "0x2e", + "svcGetLastThreadInfo": "0x2f", + "svcGetResourceLimitLimitValue": "0x30", + "svcGetResourceLimitCurrentValue": "0x31", + "svcSetThreadActivity": "0x32", + "svcGetThreadContext3": "0x33", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcUnknown": "0x36", + "svcUnknown": "0x37", + "svcUnknown": "0x38", + "svcUnknown": "0x39", + "svcUnknown": "0x3a", + "svcUnknown": "0x3b", + "svcDumpInfo": "0x3c", + "svcDumpInfoNew": "0x3d", + "svcUnknown": "0x3e", + "svcUnknown": "0x3f", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcUnknown": "0x46", + "svcUnknown": "0x47", + "svcMapPhysicalMemoryUnsafe": "0x48", + "svcUnmapPhysicalMemoryUnsafe": "0x49", + "svcSetUnsafeLimit": "0x4a", + "svcCreateCodeMemory": "0x4b", + "svcControlCodeMemory": "0x4c", + "svcSleepSystem": "0x4d", + "svcReadWriteRegister": "0x4e", + "svcSetProcessActivity": "0x4f", + "svcCreateSharedMemory": "0x50", + "svcMapTransferMemory": "0x51", + "svcUnmapTransferMemory": "0x52", + "svcCreateInterruptEvent": "0x53", + "svcQueryPhysicalAddress": "0x54", + "svcQueryMemoryMapping": "0x55", + "svcCreateDeviceAddressSpace": "0x56", + "svcAttachDeviceAddressSpace": "0x57", + "svcDetachDeviceAddressSpace": "0x58", + "svcMapDeviceAddressSpaceByForce": "0x59", + "svcMapDeviceAddressSpaceAligned": "0x5a", + "svcMapDeviceAddressSpace": "0x5b", + "svcUnmapDeviceAddressSpace": "0x5c", + "svcInvalidateProcessDataCache": "0x5d", + "svcStoreProcessDataCache": "0x5e", + "svcFlushProcessDataCache": "0x5f", + "svcDebugActiveProcess": "0x60", + "svcBreakDebugProcess": "0x61", + "svcTerminateDebugProcess": "0x62", + "svcGetDebugEvent": "0x63", + "svcContinueDebugEvent": "0x64", + "svcGetProcessList": "0x65", + "svcGetThreadList": "0x66", + "svcGetDebugThreadContext": "0x67", + "svcSetDebugThreadContext": "0x68", + "svcQueryDebugProcessMemory": "0x69", + "svcReadDebugProcessMemory": "0x6a", + "svcWriteDebugProcessMemory": "0x6b", + "svcSetHardwareBreakPoint": "0x6c", + "svcGetDebugThreadParam": "0x6d", + "svcUnknown": "0x6e", + "svcGetSystemInfo": "0x6f", + "svcCreatePort": "0x70", + "svcManageNamedPort": "0x71", + "svcConnectToPort": "0x72", + "svcSetProcessMemoryPermission": "0x73", + "svcMapProcessMemory": "0x74", + "svcUnmapProcessMemory": "0x75", + "svcQueryProcessMemory": "0x76", + "svcMapProcessCodeMemory": "0x77", + "svcUnmapProcessCodeMemory": "0x78", + "svcCreateProcess": "0x79", + "svcStartProcess": "0x7a", + "svcTerminateProcess": "0x7b", + "svcGetProcessInfo": "0x7c", + "svcCreateResourceLimit": "0x7d", + "svcSetResourceLimitLimitValue": "0x7e", + "svcCallSecureMonitor": "0x7f" + } + }, { + "type": "min_kernel_version", + "value": "0x0060" + }, { + "type": "handle_table_size", + "value": 1023 + }, { + "type": "debug_flags", + "value": { + "allow_debug": false, + "force_debug_prod": false, + "force_debug": true + } + }, { + "type": "map", + "value": { + "address": "0x60006000", + "size": "0x1000", + "is_ro": false, + "is_io": true + } + }, { + "type": "map", + "value": { + "address": "0x54300000", + "size": "0x40000", + "is_ro": false, + "is_io": true + } + }, { + "type": "map", + "value": { + "address": "0x7001b000", + "size": "0x1000", + "is_ro": false, + "is_io": true + } + } + ] +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board.cpp new file mode 100644 index 00000000..4cafe2d3 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board.cpp @@ -0,0 +1,222 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include + +#include "board.hpp" +#include "board_fuse.hpp" +#include "board_load.hpp" +#include "board_ram_oc_dvfs.hpp" +#include "board_misc.hpp" + +namespace board { + + SysClkSocType gSocType; + u8 gDramID; + HorizonOCConsoleType gConsoleType = HorizonOCConsoleType_Iowa; + FuseSpeedoData gSpeedos; + u8 speedoBracket; + Result nvCheck = 1; + u23 fd = 0, fd2 = 0; + + void FetchHardwareInfos() { + FuseReadSpeedos(gSpeedos); + FuseSetGpuBracket(gSpeedos.gpuSpeedo, speedoBracket); + + u64 sku = 0, dramID = 0; + Result rc = splInitialize(); + ASSERT_RESULT_OK(rc, "splInitialize"); + + rc = splGetConfig(SplConfigItem_HardwareType, &sku); + ASSERT_RESULT_OK(rc, "splGetConfig"); + + rc = splGetConfig(SplConfigItem_DramId, &dramID); + ASSERT_RESULT_OK(rc, "splGetConfig"); + gDramID = dramID; + splExit(); + + switch(sku) { + case 2 ... 5: + gSocType = SysClkSocType_Mariko; + break; + default: + gSocType = SysClkSocType_Erista; + } + + if (gSocType == SysClkSocType_Mariko) { + CacheGpuVoltTable(); + } + + gConsoleType = static_cast sku; + g_dramID = dramID; + } + + /* TODO: Check for config */ + void Initialize() { + Result rc = 0; + + if (HOSSVC_HAS_CLKRST) { + rc = clkrstInitialize(); + ASSERT_RESULT_OK(rc, "clkrstInitialize"); + } else { + rc = pcvInitialize(); + ASSERT_RESULT_OK(rc, "pcvInitialize"); + } + + if(HOSSVC_HAS_TC) { + rc = tcInitialize(); + ASSERT_RESULT_OK(rc, "tcInitialize"); + } + + rc = max17050Initialize(); + ASSERT_RESULT_OK(rc, "max17050Initialize"); + + rc = tmp451Initialize(); + ASSERT_RESULT_OK(rc, "tmp451Initialize"); + + nvInitialize_rc = nvInitialize(); + if (R_SUCCEEDED(nvInitialize_rc)) { + nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu"); + nvCheck_sched = nvOpen(&fd2, "/dev/nvsched-ctrl"); + } + + rc = rgltrInitialize(); + ASSERT_RESULT_OK(rc, "rgltrInitialize"); + + rc = pmdmntInitialize(); + ASSERT_RESULT_OK(rc, "pmdmntInitialize"); + + StartGpuLoad(nvCheck, fd); + /* TODO: Add back fan. */ + StartMiscThread(pwmCheck) + + batteryInfoInitialize(); + FetchHardwareInfos(); + + Result pwmCheck = 1; + if (hosversionAtLeast(6,0,0) && R_SUCCEEDED(pwmInitialize())) { + pwmCheck = pwmOpenSession2(&g_ICon, 0x3D000001); + } + + StartMiscThread(pwmCheck); + + if (gConsoleType != HorizonOCConsoleType_Hoag) { + u64 clkVirtAddr, dsiVirtAddr, outsize; + + rc = svcQueryMemoryMapping(&clkVirtAddr, &outsize, 0x60006000, 0x1000); + ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (clk)"); + + rc = svcQueryMemoryMapping(&dsiVirtAddr, &outsize, 0x54300000, 0x40000); + ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (dsi)"); + + DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr}; + DisplayRefresh_Initialize(&cfg); + } + + // rc = svcQueryMemoryMapping(&cldvfs, &cldvfs_temp, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE); + // ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (cldvfs)"); + + // if (socType == SysClkSocType_Erista) { + // cachedEristaUvLowTune0 = *(u32*) (cldvfs + CL_DVFS_TUNE0_0); + // cachedEristaUvLowTune1 = *(u32*) (cldvfs + CL_DVFS_TUNE1_0); + // } else { + // SetHz(SysClkModule_CPU, 1785000000); + // cachedMarikoUvHighTune0 = *(u32*) (cldvfs + CL_DVFS_TUNE0_0); + // ResetToStockCpu(); + // } + } + + void Exit() { + if (HOSSVC_HAS_CLKRST) { + clkrstExit(); + } else { + pcvExit(); + } + + apmExtExit(); + psmExit(); + + if (HOSSVC_HAS_TC) { + tcExit(); + } + + max17050Exit(); + tmp451Exit(); + + ExitLoad(); + + ExitMiscThread(); + + pwmChannelSessionClose(&g_ICon); + pwmExit(); + rgltrExit(); + batteryInfoExit(); + pmdmntExit(); + nvExit(); + + if (gConsoleType != HorizonOCConsoleType_Hoag) { + DisplayRefresh_Shutdown(); + } + } + + SysClkSocType GetSocType() { + return gSocType; + } + + HorizonOCConsoleType GetConsoleType() { + return gConsoleType; + } + + u8 GetDramID() { + return dramID; + } + + bool IsDram8GB() { + SecmonArgs args = {}; + args.X[0] = 0xF0000002; + args.X[1] = MC_REGISTER_BASE + MC_EMEM_CFG_0; + svcCallSecureMonitor(&args); + + if (args.X[1] == (MC_REGISTER_BASE + MC_EMEM_CFG_0)) { // if param 1 is identical read failed + writeNotification("Horizon OC\nSecmon read failed!\n This may be a hardware issue!"); + return false; + } + + return args.X[1] == 0x00002000 ? true : false; + } + + /* TODO: Put this into a different file. */ + void SetDisplayRefreshDockedState(bool docked) { + if (GetConsoleType() != HorizonOCConsoleType_Hoag) { + DisplayRefresh_SetDockedState(docked); + } + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board.hpp new file mode 100644 index 00000000..05830e57 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board.hpp @@ -0,0 +1,44 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once + +#include + +#define HOSSVC_HAS_CLKRST (hosversionAtLeast(8,0,0)) +#define HOSSVC_HAS_TC (hosversionAtLeast(5,0,0)) + +namespace board { + + void Initialize(); + void Exit(); + SysClkSocType GetSocType(); + HorizonOCConsoleType GetConsoleType(); + u8 GetDramID(); + bool IsDram8GB(); + void SetDisplayRefreshDockedState(bool docked); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_freq.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_freq.cpp new file mode 100644 index 00000000..c0d26c17 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_freq.cpp @@ -0,0 +1,231 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include "board.hpp" +#include "board_name.hpp" +#include "../error.h" + +namespace board { + + PcvModuleId GetPcvModuleId(SysClkModule sysclkModule) { + PcvModuleId pcvModuleId; + Result rc = pcvGetModuleId(&pcvModuleId, GetPcvModule(sysclkModule)); + ASSERT_RESULT_OK(rc, "pcvGetModuleId"); + + return pcvModuleId; + } + + PcvModule GetPcvModule(SysClkModule sysclkModule) { + switch (sysclkModule) { + case SysClkModule_CPU: + return PcvModule_CpuBus; + case SysClkModule_GPU: + return PcvModule_GPU; + case SysClkModule_MEM: + return PcvModule_EMC; + default: + ASSERT_ENUM_VALID(SysClkModule, sysclkModule); + } + + return (PcvModule)0; + } + + void ClkrstSetHz(ClkrstSession &session, u32 hz) { + ASSERT_RESULT_OK(clkrstSetClockRate(&session, hz), "clkrstSetClockRate"); + } + + void PcvSetHz(PcvModuleId moduleID, u32 hz) { + ASSERT_RESULT_OK(pcvSetClockRate(moduleID, hz), "pcvSetClockRate"); + } + + void SetHz(SysClkModule module, u32 hz) { + Result rc = 0; + bool usesGovenor = module > SysClkModule_MEM; + + + if (module == HorizonOCModule_Display && HorizonOCConsoleType() != HorizonOCConsoleType_Hoag) { + DisplayRefresh_SetRate(hz); + return; + } + + if (usesGovenor) { + return; + } + + if (HOSSVC_HAS_CLKRST) { + ClkrstSession session = {}; + rc = clkrstOpenSession(&session, GetPcvModuleId(module), 3); + ASSERT_RESULT_OK(rc, "clkrstOpenSession"); + ClkrstSetHz(session, hz); + + /* Voltage bug workaround. */ + if (module == SysClkModule_CPU) { + svcSleepThread(200'000); + ClkrstSetHz(session, hz); + } + + clkrstCloseSession(&session); + } else { + PcvSetHz(GetPcvModule(module), hz); + + if (module == SysClkModule_CPU) { + svcSleepThread(200'000); + PcvSetHz(GetPcvModule(module), hz); + } + } + } + + u32 GetDisplayRate(u32 hz) { + if (GetConsoleType() != HorizonOCConsoleType_Hoag) { + DisplayRefresh_GetRate(&hz, false); + return hz; + } + return 60; + } + + u32 GetHz(SysClkModule module) { + Result rc = 0; + u32 hz = 0; + + if (module == HorizonOCModule_Display) { + return GetDisplayRate(); + } + + if (HOSSVC_HAS_CLKRST) { + ClkrstSession session = {}; + + rc = clkrstOpenSession(&session, GetPcvModuleId(module), 3); + ASSERT_RESULT_OK(rc, "clkrstOpenSession"); + + rc = clkrstGetClockRate(&session, &hz); + ASSERT_RESULT_OK(rc, "clkrstGetClockRate"); + + clkrstCloseSession(&session); + } else { + rc = pcvGetClockRate(GetPcvModule(module), &hz); + ASSERT_RESULT_OK(rc, "pcvGetClockRate"); + } + + return hz; + } + + u32 GetRealHz(SysClkModule module) { + u32 hz = 0; + switch (module) { + case SysClkModule_CPU: + return t210ClkCpuFreq(); + case SysClkModule_GPU: + return t210ClkGpuFreq(); + case SysClkModule_MEM: + return t210ClkMemFreq(); + case HorizonOCModule_Display: + return GetDisplayRate(); + return hz; + default: + ASSERT_ENUM_VALID(SysClkModule, module); + } + + return 0; + } + + void GetFreqList(SysClkModule module, u32 *outList, u32 maxCount, u32 *outCount) { + Result rc = 0; + PcvClockRatesListType type; + s32 tmpInMaxCount = maxCount; + s32 tmpOutCount = 0; + + + if (HOSSVC_HAS_CLKRST) { + ClkrstSession session = {}; + + rc = clkrstOpenSession(&session, GetPcvModuleId(module), 3); + ASSERT_RESULT_OK(rc, "clkrstOpenSession"); + + rc = clkrstGetPossibleClockRates(&session, outList, tmpInMaxCount, &type, &tmpOutCount); + ASSERT_RESULT_OK(rc, "clkrstGetPossibleClockRates"); + + clkrstCloseSession(&session); + } else { + rc = pcvGetPossibleClockRates(GetPcvModule(module), outList, tmpInMaxCount, &type, &tmpOutCount); + ASSERT_RESULT_OK(rc, "pcvGetPossibleClockRates"); + } + + if (type != PcvClockRatesListType_Discrete) { + ERROR_THROW("Unexpected PcvClockRatesListType: %u (module = %s)", type, GetModuleName(module, false)); + } + + *outCount = tmpOutCount; + } + + u32 GetHighestDockedDisplayRate() { + if (GetConsoleType() != HorizonOCConsoleType_Hoag) { + return DisplayRefresh_GetDockedHighestAllowed(); + } + + return 60; + } + + void ResetToStock() { + if (hosversionAtLeast(9,0,0)) { + std::uint32_t confId = 0; + rc = apmExtGetCurrentPerformanceConfiguration(&confId); + ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); + + SysClkApmConfiguration* apmConfiguration = nullptr; + for (size_t i = 0; sysclk_g_apm_configurations[i].id; ++i) { + if(sysclk_g_apm_configurations[i].id == confId) { + apmConfiguration = &sysclk_g_apm_configurations[i]; + break; + } + } + + if(!apmConfiguration) { + ERROR_THROW("Unknown apm configuration: %x", confId); + } + + SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz); + SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz); + SetHz(SysClkModule_MEM, apmConfiguration->mem_hz); + } else { + u32 mode = 0; + rc = apmExtGetPerformanceMode(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); + + rc = apmExtSysRequestPerformanceMode(mode); + ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); + } + } + + void ResetToStockDisplay() { + if (GetConsoleType != HorizonOCConsoleType_Hoag) { + DisplayRefresh_SetRate(60); + } + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_freq.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_freq.hpp new file mode 100644 index 00000000..30f42a66 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_freq.hpp @@ -0,0 +1,89 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include +#include +#include + +namespace board { + + void SetHz(SysClkModule module, u32 hz); + + u32 GetHz(SysClkModule module); + u32 GetRealHz(SysClkModule module); + u32 GetFreqList(SysClkModule module, u32 *outList, u32 maxCount, u32 *outCount); + u32 GetHighestDockedDisplayRate(); + + void ResetToStock(); + void ResetToStockDisplay(); + + template + void ResetToStockModule(Getter getHzFunc, SysClkModule module) { + Result rc = 0; + + if (hosversionAtLeast(9, 0, 0)) { + u32 confId = 0; + rc = apmExtGetCurrentPerformanceConfiguration(&confId); + ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); + + SysClkApmConfiguration* apmConfiguration = nullptr; + for (size_t i = 0; sysclk_g_apm_configurations[i].id; ++i) { + + if (sysclk_g_apm_configurations[i].id == confId) { + apmConfiguration = &sysclk_g_apm_configurations[i]; + break; + } + } + + if (!apmConfiguration) { + ERROR_THROW("Unknown apm configuration: %x", confId); + } + + SetHz(module, getHzFunc(*apmConfiguration)); + } else { + u32 mode = 0; + rc = apmExtGetPerformanceMode(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); + + rc = apmExtSysRequestPerformanceMode(mode); + ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); + } + } + + inline void ResetToStockCpu() { + ResetToStockModule([](const SysClkApmConfiguration& cfg) {return cfg.cpu_hz; }, SysClkModule_CPU); + } + + inline void ResetToStockGpu() { + ResetToStockModule([](const SysClkApmConfiguration& cfg){ return cfg.gpu_hz; }, SysClkModule_GPU); + } + + inline void ResetToStockMem() { + ResetToStockModule([](const SysClkApmConfiguration& cfg){ return cfg.mem_hz; }, SysClkModule_MEM); + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_fuse.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_fuse.cpp new file mode 100644 index 00000000..516cbe79 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_fuse.cpp @@ -0,0 +1,120 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include "board_fuse.hpp" + +namespace board { + + namespace { + constexpr u32 FuseCpuSpeedoCalib = 0x114; + // constexpr u32 FuseCpuSpeedo1Calib = 0x12C; + constexpr u32 FuseGpuSpeedoCalib = 0x130; + + constexpr u32 FuseSocSpeedoCalib = 0x134; + // constexpr u32 FuseSocSpeedo1Calib = 0x138; + // constexpr u32 FuseSocSpeedo2Calib = 0x13C; + + constexpr u32 FuseCpuIddqCalib = 0x118; + constexpr u32 FuseSocIddqCalib = 0x140; + constexpr u32 FuseGpuIddqCalib = 0x228; + } + + void SetGpuBracket(u8 speedo, u8 &gpuBracket) { + if (speedo <= 1624) { + gpuBracket = 0; + return; + } + + if (speedo <= 1689) { + gpuBracket = 1; + return; + } + + if (speedo <= 1753) { + gpuBracket = 2; + return; + } + + /* >= 1754 */ + gpuBracket = 3; + } + + FuseReadSpeedo(FuseSpeedoData &speedo) { + u64 pid = 0; + constexpr u64 UsbID = 0x0100000000000006; + if (R_FAILED(pmdmntGetProcessId(&pid, UsbID))) { + return; + } + + Handle debug; + if (R_FAILED(svcDebugActiveProcess(&debug, pid))) { + return; + } + + MemoryInfo mem_info = {}; + u32 pageinfo = 0; + u64 addr = 0; + + u8 stack[0x10] = {}; + const u8 compare[0x10] = {}; + u8 dump[0x400] = {}; + constexpr u64 PageSize = 0x1000; + + while (true) { + if (R_FAILED(svcQueryDebugProcessMemory(&mem_info, &pageinfo, debug, addr)) || mem_info.addr < addr) { + break; + } + + if (mem_info.type == MemType_Io && mem_info.size == PageSize) { + if (R_FAILED(svcReadDebugProcessMemory(stack, debug, mem_info.addr, sizeof(stack)))) { + break; + } + + if (memcmp(stack, compare, sizeof(stack)) == 0) { + if (R_FAILED(svcReadDebugProcessMemory(dump, debug, mem_info.addr + 0x800, sizeof(dump)))) { + break; + } + + speedo.cpuSpeedo = *reinterpret_cast(dump + FuseCpuSpeedoCalib); + speedo.gpuSpeedo = *reinterpret_cast(dump + FuseGpuSpeedoCalib); + speedo.socSpeedo = *reinterpret_cast(dump + FuseSocSpeedoCalib); + speedo.cpuIDDQ = *reinterpret_cast(dump + FuseCpuIddqCalib); + speedo.gpuIDDQ = *reinterpret_cast(dump + FuseSocIddqCalib); + speedo.socIDDQ = *reinterpret_cast(dump + FUSE_GPU_IDDQ_CALIB); + + svcCloseHandle(debug); + return; + } + } + + addr = mem_info.addr + mem_info.size; + } + + svcCloseHandle(debug); + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_fuse.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_fuse.hpp new file mode 100644 index 00000000..c67e0a2b --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_fuse.hpp @@ -0,0 +1,44 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include + +namespace board { + + struct FuseSpeedoData { + u16 cpuSpeedo; + u16 gpuSpeedo; + u16 socSpeedo; + + u16 cpuIDDQ; + u16 gpuIDDQ; + u16 socIDDQ; + }; + + FuseReadSpeedo(FuseSpeedoData speedo); + FuseSetGpuBracket(u8 gpuSpeedo, u8 &gpuBracket); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_load.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_load.cpp new file mode 100644 index 00000000..73e7cc5f --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_load.cpp @@ -0,0 +1,133 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include +#include "board_misc.hpp" + +namespace board { + + Thread gpuThread; + LEvent threadexit; + Thread cpuCore0Thread; + Thread cpuCore1Thread; + Thread cpuCore2Thread; + + u32 gpuLoad; + u32 _fd; + + u64 idletick0 = 0; + u64 idletick1 = 0; + u64 idletick2 = 0; + + constexpr u64 CpuTimeOutNs = 500'000'000; + constexpr double Systemtickfrequency = 19200000.0 * (static_cast(CpuTimeOutNs) / 1'000'000'000.0); + + void GpuLoadThread(void *nvCheckPtr) { + Result nvCheck = *static_cast(nvCheckPtr); + constexpr u32 GpuSamples = 8; + u32 gpu_load_array[GpuSamples] = {}; + size_t i = 0; + constexpr u32 NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD = 0x80044715; + + if (R_SUCCEEDED(nvCheck)) do { + u32 temp; + if (R_SUCCEEDED(nvIoctl(_fd, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &temp))) { + gpu_load_array[i++ % gpu_samples_average] = temp; + gpuLoad = std::accumulate(&gpu_load_array[0], &gpu_load_array[gpu_samples_average], 0) / gpu_samples_average; + } + svcSleepThread(16'666'000); // wait a bit (this is the perfect amount of time to keep the reading accurate) + } while(true); + } + + void CheckCore(void *idletickPtr) { + u64* idletick = static_cast(idletickPtr); + while(true) { + u64 idletickA; + u64 idletickB; + svcGetInfo(&idletickB, InfoType_IdleTickCount, INVALID_HANDLE, -1); + svcWaitForAddress(&threadexit, ArbitrationType_WaitIfEqual, 0, CpuTimeOutNs); + svcGetInfo(&idletickA, InfoType_IdleTickCount, INVALID_HANDLE, -1); + *idletick = idletickA - idletickB; + } + } + + void StartLoad() { + _fd = fd; + threadCreate(&gpuThread, GpuLoadThread, &nvCheck, NULL, 0x1000, 0x3F, -2); + threadStart(&gpuThread) + + leventClear(&threadexit); + threadCreate(&cpuCore0Thread, CheckCore, &idletick0, NULL, 0x1000, 0x10, 0); + threadCreate(&cpuCore1Thread, CheckCore, &idletick1, NULL, 0x1000, 0x10, 1); + threadCreate(&cpuCore2Thread, CheckCore, &idletick2, NULL, 0x1000, 0x10, 2); + + threadStart(&cpuCore0Thread); + threadStart(&cpuCore1Thread); + threadStart(&cpuCore2Thread); + } + + u32 GetMaxCpuLoad() { + float cpuUsage0 = std::clamp(((Systemtickfrequency - idletick0) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); + float cpuUsage1 = std::clamp(((Systemtickfrequency - idletick1) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); + float cpuUsage2 = std::clamp(((Systemtickfrequency - idletick2) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); + return std::round(std::max({cpuUsage0, cpuUsage1, cpuUsage2})); + } + + u32 GetPartLoad(SysClkPartLoad loadSource) { + switch(loadSource) { + case SysClkPartLoad_EMC: + return t210EmcLoadAll(); + case SysClkPartLoad_EMCCpu: + return t210EmcLoadCpu(); + case HocClkPartLoad_GPU: + return gpuLoad; + case HocClkPartLoad_CPUMax: + return GetMaxCpuLoad(); + case HocClkPartLoad_BAT: + BatteryChargeInfo info; + batteryInfoGetChargeInfo(&info); + return info.RawBatteryCharge; + case HocClkPartLoad_FAN: + return GetFanLevel(); + default: + ASSERT_ENUM_VALID(SysClkPartLoad, loadSource); + } + return 0; + } + + void ExitLoad() { + threadClose(&gpuThread); + threadClose(&cpuCore0Thread); + threadClose(&cpuCore1Thread); + threadClose(&cpuCore2Thread); + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_load.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_load.hpp new file mode 100644 index 00000000..a46fa7b7 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_load.hpp @@ -0,0 +1,36 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include + +namespace board { + + void StartLoad(Result nvCheck, u32 fd); + void ExitLoad(); + u32 GetPartLoad(); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_misc.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_misc.cpp new file mode 100644 index 00000000..f43b85a5 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_misc.cpp @@ -0,0 +1,67 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include + +namespace board { + + Thread miscThread; + u8 fanLevel = 0; + + void MiscThreadFunc(void *pwmCheckPtr) { + Result pwmCheck = *static_cast(pwmCheckPtr); + double temp = 0; + double rotationDuty = 0; + + while (true) { + if (R_SUCCEEDED(pwmCheck)) { + if (R_SUCCEEDED(pwmChannelSessionGetDutyCycle(&g_ICon, &temp))) { + temp *= 10; + temp = trunc(temp); + temp /= 10; + rotationDuty = 100.0 - temp; + } + } + + fanLevel = static_cast(Rotation_Duty); + svcSleepThread(300'000'000); + } + } + + u8 GetFanLevel() { + return fanLevel; + } + + void StartMiscThread(Result pwmCheck) { + threadCreate(&miscThread, MiscThreadFunc, &pwmCheck, NULL, 0x1000, 0x10, 3); + threadStart(&miscThread); + } + + void ExitMiscThread() { + threadClose(&miscThread); + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_misc.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_misc.hpp new file mode 100644 index 00000000..15421d45 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_misc.hpp @@ -0,0 +1,38 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once + +#include +#include + +namespace board { + + void StartMiscThread(Result pwmCheck); + void ExitMiscThread(); + u8 GetFanLevel(); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_name.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_name.cpp new file mode 100644 index 00000000..ca1e51d5 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_name.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include + +namespace board { + + const char *GetModuleName(SysClkModule module, bool pretty) { + ASSERT_ENUM_VALID(SysClkModule, module); + return sysclkFormatModule(module, pretty); + } + + const char *GetProfileName(SysClkProfile profile, bool pretty) { + ASSERT_ENUM_VALID(SysClkProfile, profile); + return sysclkFormatProfile(profile, pretty); + } + + const char *GetThermalSensorName(SysClkThermalSensor sensor, bool pretty) { + ASSERT_ENUM_VALID(SysClkThermalSensor, sensor); + return sysclkFormatThermalSensor(sensor, pretty); + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_name.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_name.hpp new file mode 100644 index 00000000..c2c4f29d --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_name.hpp @@ -0,0 +1,37 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include +#include + +namespace board { + + const char *GetModuleName(SysClkModule module, bool pretty); + const char *GetProfileName(SysClkProfile profile, bool pretty); + const char *GetThermalSensorName(SysClkThermalSensor sensor, bool pretty); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_power.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_power.cpp new file mode 100644 index 00000000..a0701043 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_power.cpp @@ -0,0 +1,46 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include +#include + +namespace board { + + u32 GetPowerMw(SysClkPowerSensor sensor) { + switch (sensor) { + case SysClkPowerSensor_Now: + return max17050PowerNow(); + case SysClkPowerSensor_Avg: + return max17050PowerAvg(); + default: + ASSERT_ENUM_VALID(SysClkPowerSensor, sensor); + } + + return 0; + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_power.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_power.hpp new file mode 100644 index 00000000..2d0f2616 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_power.hpp @@ -0,0 +1,35 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include +#include + +namespace board { + + u32 GetPowerMw(SysClkPowerSensor sensor) + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_profile.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_profile.cpp new file mode 100644 index 00000000..8c86828f --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_profile.cpp @@ -0,0 +1,56 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include +#include + +namespace board { + + SysClkProfile GetProfile() { + u32 mode = 0; + Result rc = apmExtGetPerformanceMode(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); + + if (mode) { + return SysClkProfile_Docked; + } + + PsmChargerType chargerType; + + rc = psmGetChargerType(&chargerType); + ASSERT_RESULT_OK(rc, "psmGetChargerType"); + + if (chargerType == PsmChargerType_EnoughPower) { + return SysClkProfile_HandheldChargingOfficial; + } else if (chargerType == PsmChargerType_LowPower) { + return SysClkProfile_HandheldChargingUSB; + } + + return SysClkProfile_Handheld; + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_profile.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_profile.hpp new file mode 100644 index 00000000..a36d990b --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_profile.hpp @@ -0,0 +1,34 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include + +namespace board { + + SysClkProfile GetProfile(); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.cpp new file mode 100644 index 00000000..cda16f4b --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.cpp @@ -0,0 +1,202 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include +#include "../file_utils.h" +#include +#include "board_ram_oc_dvfs.hpp" + +namespace board { + + GpuVoltData voltData = {}; + + Handle GetPcvHandle() { + constexpr u64 PcvID = 0x10000000000001a; + u64 processIDList[80]{}; + s32 processCount = 0; + Handle handle = INVALID_HANDLE; + + DebugEventInfo debugEvent{}; + + /* Get all running processes. */ + Result resultGetProcessList = svcGetProcessList(&processCount, processIDList, std::size(processIDList)); + if (R_FAILED(resultGetProcessList)) { + return INVALID_HANDLE; + } + + /* Try to find pcv. */ + for (int i = 0; i < processCount; ++i) { + if (handle != INVALID_HANDLE) { + svcCloseHandle(handle); + handle = INVALID_HANDLE; + } + + /* Try to debug process, if it fails, try next process. */ + Result resultSvcDebugProcess = svcDebugActiveProcess(&handle, processIDList[i]); + if (R_FAILED(resultSvcDebugProcess)) { + continue; + } + + /* Try to get a debug event. */ + Result resultDebugEvent = svcGetDebugEvent(&debugEvent, handle); + if (R_SUCCEEDED(resultDebugEvent)) { + if (debugEvent.info.create_process.program_id == PcvID) { + return handle; + } + } + } + + /* Failed to get handle. */ + return INVALID_HANDLE; + } + + void CacheGpuVoltTable() { + UnkRegulator reg = { + .voltageMinUV = 600000, + .voltageStep = 12500, + .voltageMax = 1400000, + }; + + Handle handle = GetPcvHandle(); + if (handle == INVALID_HANDLE) { + FileUtils::LogLine("[dvfs] Invalid handle!"); + return; + } + + MemoryInfo memoryInfo = {}; + u64 address = 0; + u32 pageInfo = 0; + constexpr u32 PageSize = 0x1000; + u8 buffer[PageSize]; + + /* Loop until failure. */ + while (true) { + /* Find pcv heap. */ + while (true) { + Result resultProcessMemory = svcQueryDebugProcessMemory(&memoryInfo, &pageInfo, handle, address); + address = memoryInfo.addr + memoryInfo.size; + + if (R_FAILED(resultProcessMemory) || !address) { + svcCloseHandle(handle); + FileUtils::LogLine("[dvfs] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory)); + handle = INVALID_HANDLE; + return; + } + + if (memoryInfo.size && (memoryInfo.perm & 3) == 3 && static_cast(memoryInfo.type) == 0x4) { + /* Found valid memory. */ + break; + } + } + + for (u64 base = 0; base < memoryInfo.size; base += PageSize) { + u32 memorySize = std::min(memoryInfo.size, static_cast(PageSize)); + if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, base + memoryInfo.addr, memorySize))) { + break; + } + + u8 *resultFindReg = static_cast(memmem_impl(buffer, sizeof(buffer), ®, sizeof(reg))); + u32 index = resultFindReg - buffer; + + if (!resultFindReg) { + continue; + } + + /* Assuming mariko. */ + const u32 vmax = 800; + constexpr u32 VoltageTableOffset = 312; + if (!std::memcmp(&buffer[index + VoltageTableOffset], &vmax, sizeof(vmax))) { + std::memcpy(voltData.voltTable, &buffer[index + VoltageTableOffset], sizeof(voltData.voltTable)); + voltData.voltTableAddress = base + memoryInfo.addr + VoltageTableOffset + index; + } + + svcCloseHandle(handle); + handle = INVALID_HANDLE; + return; + } + } + + svcCloseHandle(handle); + handle = INVALID_HANDLE; + return; + } + + void PcvHijackGpuVolts(u32 vmin) { + u32 table[192]; + static_assert(sizeof(table) == sizeof(voltData.voltTable)); + std::memcpy(table, voltData.voltTable, sizeof(voltData.voltTable)); + + if (voltData.ramVmin == vmin) { + return; + } + + for (u32 i = 0; i < std::size(table); ++i) { + if (table[i] && table[i] <= vmin) { + table[i] = vmin; + } + } + + Handle handle = GetPcvHandle(); + if (handle == INVALID_HANDLE) { + FileUtils::LogLine("Invalid handle!"); + return; + } + + Result rc = svcWriteDebugProcessMemory(handle, table, voltData.dvfsAddress, sizeof(table)); + + if (R_SUCCEEDED(rc)) { + voltData.ramVmin = vmin; + } + + svcCloseHandle(handle); + FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin); + } + + u32 GetMinimumVmin(u32 freqMhz, u32 bracket) { + static const u32 ramTable[][22] = { + { 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, }, + { 2300, 2366, 2433, 2466, 2533, 2566, 2633, 2700, 2733, 2800, 2833, 2900, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3266, }, + { 2433, 2466, 2533, 2600, 2666, 2733, 2766, 2800, 2833, 2866, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3300, 3333, 3366, }, + { 2500, 2533, 2600, 2633, 2666, 2733, 2800, 2866, 2900, 2966, 3033, 3100, 3166, 3200, 3233, 3266, 3300, 3333, 3366, 3400, 3400, 3400, }, + }; + + static const u32 gpuVoltArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, }; + + if (freqMhz <= 1600) { + return 0; + } + + for (u32 i = 0; std::size(gpuDvfsArray) < 22; ++i) { + if (freqMhz <= ramTable[bracket][i]) { + return gpuVoltArray[i]; + } + } + + return gpuVoltArray[std::size(gpuVoltArray) - 1]; + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.hpp new file mode 100644 index 00000000..d61a4b5b --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.hpp @@ -0,0 +1,49 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include + +namespace board { + + struct GpuVoltData { + u32 voltTable[6][32] = {}; + u64 voltTableAddress; + u32 ramVmin; + }; + + /* TODO: Find out what component this actually targets. */ + struct UnkRegulator { + u32 voltageMinUV; + u32 voltageStep; + u32 voltageMax; + } + + void CacheGpuVoltTable(); + void PcvHijackGpuVolts(u32 vmin); + u32 GetMinimumVmin(u32 freqMhz, u32 bracket); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_temp.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_temp.cpp new file mode 100644 index 00000000..d102241a --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_temp.cpp @@ -0,0 +1,61 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include +#include +#include +#include +#include "board.hpp" + +namespace board { + + u32 GetTemperatureMilli(SysClkThermalSensor sensor) { + u32 millis = 0; + + if (sensor == SysClkThermalSensor_SOC) { + millis = tmp451TempSoc(); + } else if (sensor == SysClkThermalSensor_PCB) { + millis = tmp451TempPcb(); + } else if (sensor == SysClkThermalSensor_Skin) { + if (HOSSVC_HAS_TC) { + Result rc; + rc = tcGetSkinTemperatureMilliC(&millis); + ASSERT_RESULT_OK(rc, "tcGetSkinTemperatureMilliC"); + } + } else if (sensor == HorizonOCThermalSensor_Battery) { + batteryInfoGetChargeInfo(&info); + millis = batteryInfoGetTemperatureMiliCelsius(&info); + /* } else if (sensor == HorizonOCThermalSensor_PMIC) { + millis = 50000; */ + } else { + ASSERT_ENUM_VALID(SysClkThermalSensor, sensor); + } + + return std::max(0, millis); + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_temp.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_temp.hpp new file mode 100644 index 00000000..71b02d3d --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_temp.hpp @@ -0,0 +1,35 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include +#include + +namespace board { + + u32 GetTemperatureMilli(SysClkThermalSensor sensor); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.cpp new file mode 100644 index 00000000..8be8b891 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.cpp @@ -0,0 +1,138 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include +#include "board.hpp" + +namespace board { + + /* + * Switch Power domains (max77620): + * Name | Usage | uV step | uV min | uV default | uV max | Init + *-------+---------------+---------+--------+------------+---------+------------------ + * sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) + * sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) + * sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) + * sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | + * ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) + * ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) + * ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | + * ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv) + * ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | 0.85V (AO, pcv) + * ldo5 | GC Card | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv) + * ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V (pcv) + * ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) + * ldo8 | XUSB, DP, MCU | 50000 | 800000 | 1050000 | 2800000 | 1.05V/2.8V (pcv) + + typedef enum { + PcvPowerDomainId_Max77620_Sd0 = 0x3A000080, + PcvPowerDomainId_Max77620_Sd1 = 0x3A000081, // vdd2 + PcvPowerDomainId_Max77620_Sd2 = 0x3A000082, + PcvPowerDomainId_Max77620_Sd3 = 0x3A000083, + PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0, + PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1, + PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2, + PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3, + PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4, + PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5, + PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6, + PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7, + PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8, + PcvPowerDomainId_Max77621_Cpu = 0x3A000003, + PcvPowerDomainId_Max77621_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Cpu = 0x3A000003, + PcvPowerDomainId_Max77812_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Dram = 0x3A000005, // vddq + } PowerDomainId; + */ + u32 GetVoltage(HocClkVoltage voltage) { + RgltrSession session; + Result rc = 0; + u32 out = 0; + BatteryChargeInfo info; + + switch (voltage) { + case HocClkVoltage_SOC: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Sd0); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_EMCVDD2: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Sd1); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_CPU: + if (GetSocType() == SysClkSocType_Mariko) { + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77621_Cpu); + } else { + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Cpu); + } + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_GPU: + if (GetSocType() == SysClkSocType_Mariko) { + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77621_Gpu); + } else { + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Gpu); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + } + break; + case HocClkVoltage_EMCVDDQ_MarikoOnly: + if (GetSocType() == SysClkSocType_Mariko) { + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Dram); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + } else { + out = GetVoltage(HocClkVoltage_EMCVDD2); + } + break; + case HocClkVoltage_Display: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Ldo0); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_Battery: + batteryInfoGetChargeInfo(&info); + out = info.VoltageAvg; + break; + default: + ASSERT_ENUM_VALID(HocClkVoltage, voltage); + } + + return out > 0 ? out : 0; + } + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.hpp new file mode 100644 index 00000000..64e9872b --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.hpp @@ -0,0 +1,35 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#pragma once +#include +#include + +namespace board { + + u32 GetVoltage(HocClkVoltage voltage); + +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/clock_manager.cpp b/Source/rewrite-hoc-clk/sysmodule/src/clock_manager.cpp new file mode 100644 index 00000000..1e1c98bb --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/clock_manager.cpp @@ -0,0 +1,1299 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "clock_manager.h" +#include +#include "file_utils.h" +#include "board.h" +#include "process_management.h" +#include "errors.h" +#include "ipc_service.h" +#include "kip.h" +#include +#include "notification.h" +#include +#include +#include +#include + +#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0)) + +// governor constants +#define POLL_NS 5'000'000 // 5 ms – governor poll rate +#define DOWN_HOLD_TICKS 10 // 50 ms – how long to in POLL_NS to hold while ramping down +#define STEP_UTIL 900 // multiplier for step calculations + +bool isGpuGovernorEnabled = false; +bool isCpuGovernorEnabled = false; +bool lastGpuGovernorState = false; +bool lastCpuGovernorState = false; +bool lastVrrGovernorState = false; +bool hasChanged = true; +ClockManager *ClockManager::instance = NULL; +Thread cpuGovernorTHREAD; +Thread gpuGovernorTHREAD; +Thread vrrTHREAD; +u32 initialConfigValues[SysClkConfigValue_EnumMax]; // initial config. used for safety checks +bool kipAvailable = false; +bool isCpuGovernorInBoostMode = false; +bool isVRREnabled = false; +ClockManager *ClockManager::GetInstance() +{ + return instance; +} + +void ClockManager::Exit() +{ + if (instance) + { + delete instance; + } +} + +void ClockManager::Initialize() +{ + if (!instance) + { + instance = new ClockManager(); + } +} + +ClockManager::ClockManager() +{ + this->config = Config::CreateDefault(); + this->context = new SysClkContext; + this->context->applicationId = 0; + this->context->profile = SysClkProfile_Handheld; + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + this->context->freqs[module] = 0; + this->context->realFreqs[module] = 0; + this->context->overrideFreqs[module] = 0; + this->RefreshFreqTableRow((SysClkModule)module); + } + + this->running = false; + this->lastTempLogNs = 0; + this->lastCsvWriteNs = 0; + + this->sysDockIntegration = new SysDockIntegration; + this->saltyNXIntegration = new SaltyNXIntegration; + + memset(&initialConfigValues, 0, sizeof(initialConfigValues)); + this->GetKipData(); + + threadCreate( + &cpuGovernorTHREAD, + ClockManager::CpuGovernorThread, + this, + NULL, + 0x2000, + 0x3F, + -2 + ); + + threadCreate( + &gpuGovernorTHREAD, + ClockManager::GovernorThread, + this, + NULL, + 0x2000, + 0x3F, + -2 + ); + + threadCreate( + &vrrTHREAD, + ClockManager::VRRThread, + this, + NULL, + 0x2000, + 0x3F, + -2 + ); + + for(int i = 0; i < HorizonOCSpeedo_EnumMax; i++) { + this->context->speedos[i] = Board::getSpeedo((HorizonOCSpeedo)i); + this->context->iddq[i] = Board::getIDDQ((HorizonOCSpeedo)i); + } + + this->context->dramID = Board::GetDramID(); + this->context->isDram8GB = Board::IsDram8GB(); + Board::SetGpuSchedulingMode((GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling), (GpuSchedulingOverrideMethod)this->config->GetConfigValue(HorizonOCConfigValue_GPUSchedulingMethod)); + this->context->gpuSchedulingMode = (GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling); + + this->context->isSysDockInstalled = this->sysDockIntegration->getCurrentSysDockState(); + this->context->isSaltyNXInstalled = this->saltyNXIntegration->getCurrentSaltyNXState(); + if(this->context->isSaltyNXInstalled) { + this->saltyNXIntegration->LoadSaltyNX(); + } + + + threadStart(&cpuGovernorTHREAD); + threadStart(&gpuGovernorTHREAD); + threadStart(&vrrTHREAD); +} + +ClockManager::~ClockManager() +{ + threadClose(&cpuGovernorTHREAD); + threadClose(&gpuGovernorTHREAD); + threadClose(&vrrTHREAD); + + delete this->sysDockIntegration; + delete this->saltyNXIntegration; + delete this->config; + delete this->context; +} + +SysClkContext ClockManager::GetCurrentContext() +{ + std::scoped_lock lock{this->contextMutex}; + return *this->context; +} + +Config *ClockManager::GetConfig() +{ + return this->config; +} + +void ClockManager::SetRunning(bool running) +{ + this->running = running; +} + +bool ClockManager::Running() +{ + return this->running; +} + +void ClockManager::GetFreqList(SysClkModule module, std::uint32_t *list, std::uint32_t maxCount, std::uint32_t *outCount) +{ + ASSERT_ENUM_VALID(SysClkModule, module); + + *outCount = std::min(maxCount, this->freqTable[module].count); + memcpy(list, &this->freqTable[module].list[0], *outCount * sizeof(this->freqTable[0].list[0])); +} + +bool ClockManager::IsAssignableHz(SysClkModule module, std::uint32_t hz) +{ + switch (module) + { + case SysClkModule_CPU: + return hz >= 500000000; + case SysClkModule_MEM: + return hz >= 665600000; + default: + return true; + } +} + +std::uint32_t ClockManager::GetMaxAllowedHz(SysClkModule module, SysClkProfile profile) +{ + if (this->config->GetConfigValue(HocClkConfigValue_UncappedClocks)) + { + return ~0; // Integer limit, uncapped clocks ON + } + else + { + if (module == SysClkModule_GPU) + { + if (profile < SysClkProfile_HandheldCharging) + { + switch(Board::GetSocType()) { + case SysClkSocType_Erista: + return 460800000; + case SysClkSocType_Mariko: + switch(this->config->GetConfigValue(KipConfigValue_marikoGpuUV)) { + case 0: + return 614400000; + case 1: + return 691200000; + case 2: + return 768000000; + default: + return 614400000; + } + default: + return 460800000; + } + } + else if (profile <= SysClkProfile_HandheldChargingUSB) + { + switch(Board::GetSocType()) { + case SysClkSocType_Erista: + return 768000000; + case SysClkSocType_Mariko: + switch(this->config->GetConfigValue(KipConfigValue_marikoGpuUV)) { + case 0: + return 844800000; + case 1: + return 921600000; + case 2: + return 998400000; + default: + return 844800000; + } + default: + return 768000000; + } + } + } else if(module == SysClkModule_CPU) { + if(profile < SysClkProfile_HandheldCharging && Board::GetSocType() == SysClkSocType_Erista) { + return 1581000000; + } else { + return ~0; + } + } + } + return 0; +} + +std::uint32_t ClockManager::GetNearestHz(SysClkModule module, std::uint32_t inHz, std::uint32_t maxHz) +{ + std::uint32_t *freqs = &this->freqTable[module].list[0]; + size_t count = this->freqTable[module].count - 1; + + size_t i = 0; + while (i < count) + { + if (maxHz > 0 && freqs[i] >= maxHz) + { + break; + } + + if (inHz <= ((std::uint64_t)freqs[i] + freqs[i + 1]) / 2) + { + break; + } + + i++; + } + + return freqs[i]; +} + +bool ClockManager::ConfigIntervalTimeout(SysClkConfigValue intervalMsConfigValue, std::uint64_t ns, std::uint64_t *lastLogNs) +{ + std::uint64_t logInterval = this->GetConfig()->GetConfigValue(intervalMsConfigValue) * 1000000ULL; + bool shouldLog = logInterval && ((ns - *lastLogNs) > logInterval); + + if (shouldLog) + { + *lastLogNs = ns; + } + + return shouldLog; +} + +void ClockManager::RefreshFreqTableRow(SysClkModule module) +{ + std::scoped_lock lock{this->contextMutex}; + + std::uint32_t freqs[SYSCLK_FREQ_LIST_MAX]; + std::uint32_t count; + + FileUtils::LogLine("[mgr] %s freq list refresh", Board::GetModuleName(module, true)); + Board::GetFreqList(module, &freqs[0], SYSCLK_FREQ_LIST_MAX, &count); + + std::uint32_t *hz = &this->freqTable[module].list[0]; + this->freqTable[module].count = 0; + for (std::uint32_t i = 0; i < count; i++) + { + if (!this->IsAssignableHz(module, freqs[i])) + { + continue; + } + + *hz = freqs[i]; + FileUtils::LogLine("[mgr] %02u - %u - %u.%u MHz", this->freqTable[module].count, *hz, *hz / 1000000, *hz / 100000 - *hz / 1000000 * 10); + + this->freqTable[module].count++; + hz++; + } + + FileUtils::LogLine("[mgr] count = %u", this->freqTable[module].count); +} + +u32 ClockManager::SchedutilTargetHz(u32 util, u32 tableMaxHz) { + u64 hz = (u64)tableMaxHz * util / STEP_UTIL; + return (u32)(std::min(hz, static_cast(tableMaxHz))); +} + +u32 ClockManager::TableIndexForHz(const FreqTable& table, u32 targetHz) { // must pass in a freqTable as tables are different for cpu/gpu + for (u32 i = 0; i < table.count; i++) + if (table.list[i] >= targetHz) + return i; + return table.count - 1; +} + +u32 ClockManager::ResolveTargetHz(ClockManager* mgr, SysClkModule module) { + u32 hz = mgr->context->overrideFreqs[module]; + if (!hz) + hz = mgr->config->GetAutoClockHz( + mgr->context->applicationId, module, + mgr->context->profile, false); + if (!hz) + hz = mgr->config->GetAutoClockHz( + GLOBAL_PROFILE_ID, module, + mgr->context->profile, false); + return hz; +} + +void ClockManager::CpuGovernorThread(void* arg) { + ClockManager* mgr = static_cast(arg); + + u32 downHoldRemaining = 0; + u32 lastHz = 0; + + for (;;) { + if (!mgr->running || !isCpuGovernorEnabled) { + downHoldRemaining = 0; + lastHz = 0; + svcSleepThread(POLL_NS); + continue; + } + + u32 mode = 0; + Result rc = apmExtGetCurrentPerformanceConfiguration(&mode); + + if (R_SUCCEEDED(rc) && apmExtIsBoostMode(mode)) { + isCpuGovernorInBoostMode = true; + downHoldRemaining = 0; + lastHz = 0; + continue; // TODO: figure out a way to get boost clock easily and set it instead of just skipping the governor + } else if(!apmExtIsBoostMode(mode)) { + isCpuGovernorInBoostMode = false; + } + + auto& table = mgr->freqTable[SysClkModule_CPU]; + + if (table.count == 0) + continue; + + std::scoped_lock lock{mgr->contextMutex}; + + u32 cpuLoad = Board::GetPartLoad(HocClkPartLoad_CPUMax); + + u32 tableMaxHz = table.list[table.count - 1]; + u32 desiredHz = ClockManager::SchedutilTargetHz(cpuLoad, tableMaxHz); + u32 targetHz = ClockManager::ResolveTargetHz(mgr, SysClkModule_CPU); + u32 maxHz = mgr->GetMaxAllowedHz(SysClkModule_CPU, mgr->context->profile); + + if (targetHz && desiredHz > targetHz) + desiredHz = targetHz; + + if (maxHz && desiredHz > maxHz) + desiredHz = maxHz; + + u32 newHz = table.list[ClockManager::TableIndexForHz(table, desiredHz)]; + + // ramp up fast, go down slow + bool goingDown = (lastHz != 0) && (newHz < lastHz); + + if (!goingDown) + downHoldRemaining = 0; + else if (downHoldRemaining == 0) + downHoldRemaining = DOWN_HOLD_TICKS; + + if (downHoldRemaining > 0) + downHoldRemaining--; + + if ((!goingDown || (downHoldRemaining == 0)) && mgr->IsAssignableHz(SysClkModule_CPU, newHz)) { + Board::SetHz(SysClkModule_CPU, newHz); + mgr->context->freqs[SysClkModule_CPU] = newHz; + lastHz = newHz; + } + + svcSleepThread(POLL_NS); + } +} + +void ClockManager::GovernorThread(void* arg) { + ClockManager* mgr = static_cast(arg); + + u32 downHoldRemaining = 0; + u32 lastHz = 0; + + for (;;) { + if (!mgr->running || !isGpuGovernorEnabled) { + downHoldRemaining = 0; + lastHz = 0; + svcSleepThread(POLL_NS); + continue; + } + + auto& table = mgr->freqTable[SysClkModule_GPU]; + if (table.count == 0) + continue; + + std::scoped_lock lock{mgr->contextMutex}; + + u32 gpuLoad = Board::GetPartLoad(HocClkPartLoad_GPU); + u32 tableMaxHz = table.list[table.count - 1]; + u32 desiredHz = ClockManager::SchedutilTargetHz(gpuLoad, tableMaxHz); + u32 targetHz = ClockManager::ResolveTargetHz(mgr, SysClkModule_GPU); + u32 maxHz = mgr->GetMaxAllowedHz(SysClkModule_GPU, mgr->context->profile); + + if (targetHz && desiredHz > targetHz) + desiredHz = targetHz; + + if (maxHz && desiredHz > maxHz) + desiredHz = maxHz; + + u32 newHz = table.list[ClockManager::TableIndexForHz(table, desiredHz)]; + bool goingDown = (lastHz != 0) && (newHz < lastHz); + + if (!goingDown) + downHoldRemaining = 0; + else if (downHoldRemaining == 0) + downHoldRemaining = DOWN_HOLD_TICKS; + + if (downHoldRemaining > 0) + downHoldRemaining--; + + if ((!goingDown || (downHoldRemaining == 0)) && mgr->IsAssignableHz(SysClkModule_GPU, newHz)) { + Board::SetHz(SysClkModule_GPU, newHz); + mgr->context->freqs[SysClkModule_GPU] = newHz; + lastHz = newHz; + } + + svcSleepThread(POLL_NS); + } +} + +void ClockManager::VRRThread(void* arg) { + ClockManager* mgr = static_cast(arg); + u8 tick = 0; + for (;;) { + if (!mgr->running || mgr->context->profile == SysClkProfile_Docked || !isVRREnabled) { + svcSleepThread(POLL_NS); + continue; + } + + if(Board::IsHoag()) { // don't do anything on lite + svcSleepThread(~0ULL); + continue; + } + + std::scoped_lock lock{mgr->contextMutex}; + + u8 fps; + + if(mgr->context->isSaltyNXInstalled) { + fps = mgr->saltyNXIntegration->GetFPS(); + } else { + svcSleepThread(~0ULL); // effectively disable the thread if SaltyNX isn't installed, as there's no point in it running + continue; + } + + + if(fps == 254) { + svcSleepThread(POLL_NS); + continue; + } + // if(appletGetFocusState() != AppletFocusState_InFocus) { + // Board::ResetToStockDisplay(); + // continue; + // } + + u32 targetHz = mgr->context->overrideFreqs[HorizonOCModule_Display]; + if (!targetHz) + { + targetHz = mgr->config->GetAutoClockHz(mgr->context->applicationId, HorizonOCModule_Display, mgr->context->profile, false); + if(!targetHz) + targetHz = mgr->config->GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Display, mgr->context->profile, false); + } + + u8 maxDisplay; + if(targetHz) { + maxDisplay = targetHz; + } else { + if(Board::GetConsoleType() == HorizonOCConsoleType_Aula) { + maxDisplay = mgr->config->GetConfigValue(HorizonOCConfigValue_EnableUnsafeDisplayFreqs) ? 65 : 60; + } else { + maxDisplay = mgr->config->GetConfigValue(HorizonOCConfigValue_EnableUnsafeDisplayFreqs) ? 72 : 60; + } + } + + u8 minDisplay = Board::GetConsoleType() == HorizonOCConsoleType_Aula ? 45 : 40; + if(maxDisplay == minDisplay) + continue; + + if(fps >= minDisplay && fps <= maxDisplay) + Board::SetHz(HorizonOCModule_Display, fps); + else { + for(u32 i = 0; i < 10; i++) { + u32 compareHz = fps * i; + if(compareHz >= minDisplay && compareHz <= maxDisplay) { + Board::SetHz(HorizonOCModule_Display, compareHz); + break; + } + } + } + if(++tick > 50) { + Board::ResetToStockDisplay(); + tick = 0; + svcSleepThread(25'000'000); + } + + svcSleepThread(POLL_NS); + } +} + + + +void ClockManager::HandleSafetyFeatures() { + AppletOperationMode opMode = appletGetOperationMode(); + if(this->config->GetConfigValue(HocClkConfigValue_HandheldTDP) && opMode == AppletOperationMode_Handheld) { + if(Board::GetConsoleType() == HorizonOCConsoleType_Hoag) { + if(Board::GetPowerMw(SysClkPowerSensor_Avg) < -(int)this->config->GetConfigValue(HocClkConfigValue_LiteTDPLimit)) { + ResetToStockClocks(); + return; + } + } else { + if(Board::GetPowerMw(SysClkPowerSensor_Avg) < -(int)this->config->GetConfigValue(HocClkConfigValue_HandheldTDPLimit)) { + ResetToStockClocks(); + return; + } + } + } + + if(((tmp451TempSoc() / 1000) > (int)this->config->GetConfigValue(HocClkConfigValue_ThermalThrottleThreshold)) && this->config->GetConfigValue(HocClkConfigValue_ThermalThrottle)) { + ResetToStockClocks(); + return; + } +} + +void ClockManager::HandleMiscFeatures() { + if(this->config->GetConfigValue(HorizonOCConfigValue_BatteryChargeCurrent)) { + I2c_Bq24193_SetFastChargeCurrentLimit(this->config->GetConfigValue(HorizonOCConfigValue_BatteryChargeCurrent)); + } +} + +void ClockManager::HandleGovernor(uint32_t targetHz) { + u32 tempTargetHz = this->context->overrideFreqs[HorizonOCModule_Governor]; + if (!tempTargetHz) + { + tempTargetHz = this->config->GetAutoClockHz(this->context->applicationId, HorizonOCModule_Governor, this->context->profile, true); + if (!tempTargetHz) + tempTargetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Governor, this->context->profile, true); + } + + auto resolve = [](u8 app, u8 temp) -> u8 { + if (temp == ComponentGovernor_Disabled) return ComponentGovernor_Disabled; + if (temp != ComponentGovernor_DoNotOverride) return temp; + return app; + }; + + u8 effectiveCpu = resolve(GovernorStateCpu(targetHz), GovernorStateCpu(tempTargetHz)); + u8 effectiveGpu = resolve(GovernorStateGpu(targetHz), GovernorStateGpu(tempTargetHz)); + u8 effectiveVrr = resolve(GovernorStateVrr(targetHz), GovernorStateVrr(tempTargetHz)); + + bool newCpuGovernorState = (effectiveCpu == ComponentGovernor_Enabled); + bool newGpuGovernorState = (effectiveGpu == ComponentGovernor_Enabled); + bool newVrrGovernorState = (effectiveVrr == ComponentGovernor_Enabled); + + isCpuGovernorEnabled = newCpuGovernorState; + isGpuGovernorEnabled = newGpuGovernorState; + isVRREnabled = newVrrGovernorState; + + if(newCpuGovernorState == false && lastCpuGovernorState == true) { + svcSleepThread(100'000'000); // thread syncing. probably a cleaner way to do this but hey, it works! + Board::ResetToStockCpu(); + } + if(newGpuGovernorState == false && lastGpuGovernorState == true) { + svcSleepThread(100'000'000); + Board::ResetToStockGpu(); + } + if (newVrrGovernorState == false && lastVrrGovernorState == true) { + svcSleepThread(100'000'000); + Board::ResetToStockDisplay(); + } + if(newCpuGovernorState != lastCpuGovernorState || newGpuGovernorState != lastGpuGovernorState || newVrrGovernorState != lastVrrGovernorState) { + FileUtils::LogLine("[mgr] Governor state changed: CPU %s, GPU %s, VRR %s", newCpuGovernorState ? "enabled" : "disabled", newGpuGovernorState ? "enabled" : "disabled", newVrrGovernorState ? "enabled" : "disabled"); + lastCpuGovernorState = newCpuGovernorState; + lastGpuGovernorState = newGpuGovernorState; + lastVrrGovernorState = newVrrGovernorState; + } +} + +void ClockManager::DVFSBeforeSet(u32 targetHz) { + s32 dvfsOffset = this->config->GetConfigValue(HorizonOCConfigValue_DVFSOffset); + u32 vmin = Board::GetMinimumGpuVoltage(targetHz / 1000000) + dvfsOffset; + + Board::PcvHijackDvfs(vmin); + + /* Update the voltage. */ + if (I2c_BuckConverter_GetMvOut(&I2c_Mariko_GPU) < vmin) { + I2c_BuckConverter_SetMvOut(&I2c_Mariko_GPU, vmin); + } + + this->context->voltages[HocClkVoltage_GPU] = vmin * 1000; +} + +void ClockManager::DVFSAfterSet(u32 targetHz) { + s32 dvfsOffset = this->config->GetConfigValue(HorizonOCConfigValue_DVFSOffset); + dvfsOffset = std::max(dvfsOffset, -80); + u32 vmin = Board::GetMinimumGpuVoltage(targetHz / 1000000); + + if (vmin) { + vmin += dvfsOffset; + } + + u32 maxHz = this->GetMaxAllowedHz(SysClkModule_GPU, this->context->profile); + u32 nearestHz = this->GetNearestHz(SysClkModule_GPU, targetHz, maxHz); + Board::PcvHijackDvfs(vmin); + + if (targetHz) { + Board::SetHz(SysClkModule_GPU, ~0); + Board::SetHz(SysClkModule_GPU, nearestHz); + } else { + Board::SetHz(SysClkModule_GPU, ~0); + Board::ResetToStockGpu(); + } +} + +void ClockManager::HandleCpuUv() { + if(Board::GetSocType() == SysClkSocType_Erista) + Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000); + else + Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf))); +} + +void ClockManager::DVFSReset() { + if (Board::GetSocType() == SysClkSocType_Mariko && this->config->GetConfigValue(HorizonOCConfigValue_DVFSMode) == DVFSMode_Hijack) { + Board::PcvHijackDvfs(0); + + u32 targetHz = this->context->overrideFreqs[SysClkModule_GPU]; + if (!targetHz) { + targetHz = this->config->GetAutoClockHz(this->context->applicationId, SysClkModule_GPU, this->context->profile, false); + if(!targetHz) { + targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, SysClkModule_GPU, this->context->profile, false); + } + } + u32 maxHz = this->GetMaxAllowedHz(SysClkModule_GPU, this->context->profile); + u32 nearestHz = this->GetNearestHz(SysClkModule_GPU, targetHz, maxHz); + + Board::SetHz(SysClkModule_GPU, ~0); + if(targetHz) { + Board::SetHz(SysClkModule_GPU, nearestHz); + } else { + Board::ResetToStockGpu(); + } + } +} + +void ClockManager::HandleFreqReset(SysClkModule module, bool isBoost) { + switch (module) + { + case SysClkModule_CPU: + if(!(isBoost || (this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode) && isBoost))) + Board::ResetToStockCpu(); + if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv)) { + if(Board::GetSocType() == SysClkSocType_Erista) + Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000); + else + Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf))); + } + + break; + case SysClkModule_GPU: + Board::ResetToStockGpu(); + break; + case SysClkModule_MEM: + Board::ResetToStockMem(); + DVFSReset(); + break; + case HorizonOCModule_Display: + if(this->config->GetConfigValue(HorizonOCConfigValue_OverwriteRefreshRate) && !Board::IsHoag()) { + Board::ResetToStockDisplay(); + } + break; + default: + break; + } + +} + +void ClockManager::SetClocks(bool isBoost) { + std::uint32_t targetHz = 0; + std::uint32_t maxHz = 0; + std::uint32_t nearestHz = 0; + + if(isBoost && !this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode)) { + u32 boostFreq = Board::GetHz(SysClkModule_CPU); + if (boostFreq / 1000000 > 1785) { + Board::SetHz(SysClkModule_CPU, boostFreq); + } + return; // Return if we are't overwriting boost mode + } + + bool returnRaw = false; // Return a value scaled to MHz instead of raw value + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + u32 oldHz = Board::GetHz((SysClkModule)module); // Get Old hz (used primarily for DVFS Logic) + + if(module > SysClkModule_MEM) + returnRaw = true; + else + returnRaw = false; + targetHz = this->context->overrideFreqs[module]; + if (!targetHz) + { + targetHz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile, returnRaw); + if(!targetHz) + targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, (SysClkModule)module, this->context->profile, returnRaw); + } + + if(module == HorizonOCModule_Governor) { + HandleGovernor(targetHz); + } + + bool noCPU = isCpuGovernorEnabled; + bool noGPU = isGpuGovernorEnabled; + if(!Board::IsHoag()) { + bool noDisp = isVRREnabled; + if(noDisp && module == HorizonOCModule_Display) + continue; + + if(module == HorizonOCModule_Display && this->config->GetConfigValue(HorizonOCConfigValue_OverwriteRefreshRate)) { + if(targetHz) + Board::SetHz(HorizonOCModule_Display, targetHz); + else + Board::ResetToStockDisplay(); + } + } + + // Skip GPU and CPU if governors handle them + if(module > SysClkModule_MEM) { + continue; + } + + + if(noCPU && module == SysClkModule_CPU) + continue; + if(noGPU && module == SysClkModule_GPU) + continue; + + if (targetHz) + { + maxHz = this->GetMaxAllowedHz((SysClkModule)module, this->context->profile); + nearestHz = this->GetNearestHz((SysClkModule)module, targetHz, maxHz); + + if (nearestHz != this->context->freqs[module]) { + FileUtils::LogLine( + "[mgr] %s clock set : %u.%u MHz (target = %u.%u MHz)", + Board::GetModuleName((SysClkModule)module, true), + nearestHz / 1000000, nearestHz / 100000 - nearestHz / 1000000 * 10, + targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10 + ); + + if(module == SysClkModule_MEM && Board::GetSocType() == SysClkSocType_Mariko && targetHz > oldHz && this->config->GetConfigValue(HorizonOCConfigValue_DVFSMode) == DVFSMode_Hijack) { + DVFSBeforeSet(targetHz); + } + + Board::SetHz((SysClkModule)module, nearestHz); + this->context->freqs[module] = nearestHz; + + if(module == SysClkModule_CPU && (this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv))) { + HandleCpuUv(); + } + + if(module == SysClkModule_MEM && Board::GetSocType() == SysClkSocType_Mariko && targetHz < oldHz && this->config->GetConfigValue(HorizonOCConfigValue_DVFSMode) == DVFSMode_Hijack) { + DVFSAfterSet(targetHz); + } + } + } else { + HandleFreqReset((SysClkModule)module, isBoost); + } + } + +} + +void ClockManager::Tick() +{ + std::scoped_lock lock{this->contextMutex}; + std::uint32_t mode = 0; + Result rc = apmExtGetCurrentPerformanceConfiguration(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); + + bool isBoost = apmExtIsBoostMode(mode); + + HandleSafetyFeatures(); + + if (this->RefreshContext() || this->config->Refresh()) + { + HandleMiscFeatures(); + SetClocks(isBoost); + } +} + +void ClockManager::ResetToStockClocks() { + Board::ResetToStockCpu(); + if(this->config->GetConfigValue(HorizonOCConfigValue_LiveCpuUv)) + { + if(Board::GetSocType() == SysClkSocType_Erista) + Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_eristaCpuUV), 0, 1581000000); + else + Board::SetCpuUvLevel(this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow), this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh), Board::CalculateTbreak(this->config->GetConfigValue(KipConfigValue_tableConf))); + } + + Board::ResetToStockGpu(); +} + +void ClockManager::WaitForNextTick() +{ + if(!(Board::GetHz(SysClkModule_MEM) < 665000000)) + svcSleepThread(this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) * 1000000ULL); + else + svcSleepThread(5000 * 1000000ULL); // 5 seconds in sleep mode +} + +bool ClockManager::RefreshContext() +{ + bool hasChanged = false; + + std::uint32_t mode = 0; + Result rc = apmExtGetCurrentPerformanceConfiguration(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); + + std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId(); + if (applicationId != this->context->applicationId) + { + FileUtils::LogLine("[mgr] TitleID change: %016lX", applicationId); + this->context->applicationId = applicationId; + hasChanged = true; + } + + SysClkProfile profile = Board::GetProfile(); + if (profile != this->context->profile) + { + FileUtils::LogLine("[mgr] Profile change: %s", Board::GetProfileName(profile, true)); + this->context->profile = profile; + hasChanged = true; + } + + // restore clocks to stock values on app or profile change + if (hasChanged) + { + Board::ResetToStock(); + if (Board::GetSocType() == SysClkSocType_Mariko && this->config->GetConfigValue(HorizonOCConfigValue_DVFSMode) == DVFSMode_Hijack) { + Board::PcvHijackDvfs(0); + Board::SetHz(SysClkModule_GPU, ~0); + Board::ResetToStockGpu(); + } + this->WaitForNextTick(); + } + + std::uint32_t hz = 0; + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + hz = Board::GetHz((SysClkModule)module); + if (hz != 0 && hz != this->context->freqs[module]) + { + FileUtils::LogLine("[mgr] %s clock change: %u.%u MHz", Board::GetModuleName((SysClkModule)module, true), hz / 1000000, hz / 100000 - hz / 1000000 * 10); + this->context->freqs[module] = hz; + hasChanged = true; + } + + hz = this->GetConfig()->GetOverrideHz((SysClkModule)module); + if (hz != this->context->overrideFreqs[module]) + { + if (hz) + { + FileUtils::LogLine("[mgr] %s override change: %u.%u MHz", Board::GetModuleName((SysClkModule)module, true), hz / 1000000, hz / 100000 - hz / 1000000 * 10); + } + this->context->overrideFreqs[module] = hz; + hasChanged = true; + } + } + + std::uint64_t ns = armTicksToNs(armGetSystemTick()); + + // temperatures do not and should not force a refresh, hasChanged untouched + std::uint32_t millis = 0; + bool shouldLogTemp = this->ConfigIntervalTimeout(SysClkConfigValue_TempLogIntervalMs, ns, &this->lastTempLogNs); + for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++) + { + millis = Board::GetTemperatureMilli((SysClkThermalSensor)sensor); + if (shouldLogTemp) + { + FileUtils::LogLine("[mgr] %s temp: %u.%u °C", Board::GetThermalSensorName((SysClkThermalSensor)sensor, true), millis / 1000, (millis - millis / 1000 * 1000) / 100); + } + this->context->temps[sensor] = millis; + } + + // power stats do not and should not force a refresh, hasChanged untouched + std::int32_t mw = 0; + bool shouldLogPower = this->ConfigIntervalTimeout(SysClkConfigValue_PowerLogIntervalMs, ns, &this->lastPowerLogNs); + for (unsigned int sensor = 0; sensor < SysClkPowerSensor_EnumMax; sensor++) + { + mw = Board::GetPowerMw((SysClkPowerSensor)sensor); + if (shouldLogPower) + { + FileUtils::LogLine("[mgr] Power %s: %d mW", Board::GetPowerSensorName((SysClkPowerSensor)sensor, false), mw); + } + this->context->power[sensor] = mw; + } + + // real freqs do not and should not force a refresh, hasChanged untouched + std::uint32_t realHz = 0; + bool shouldLogFreq = this->ConfigIntervalTimeout(SysClkConfigValue_FreqLogIntervalMs, ns, &this->lastFreqLogNs); + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + realHz = Board::GetRealHz((SysClkModule)module); + if (shouldLogFreq) + { + FileUtils::LogLine("[mgr] %s real freq: %u.%u MHz", Board::GetModuleName((SysClkModule)module, true), realHz / 1000000, realHz / 100000 - realHz / 1000000 * 10); + } + this->context->realFreqs[module] = realHz; + } + + // ram load do not and should not force a refresh, hasChanged untouched + for (unsigned int loadSource = 0; loadSource < SysClkPartLoad_EnumMax; loadSource++) + { + this->context->partLoad[loadSource] = Board::GetPartLoad((SysClkPartLoad)loadSource); + } + + for (unsigned int voltageSource = 0; voltageSource < HocClkVoltage_EnumMax; voltageSource++) + { + this->context->voltages[voltageSource] = Board::GetVoltage((HocClkVoltage)voltageSource); + } + + if (this->ConfigIntervalTimeout(SysClkConfigValue_CsvWriteIntervalMs, ns, &this->lastCsvWriteNs)) + { + FileUtils::WriteContextToCsv(this->context); + } + + // this->context->maxDisplayFreq = Board::GetHighestDockedDisplayRate(); + if(!Board::IsHoag()) { + u32 targetHz = this->context->overrideFreqs[HorizonOCModule_Display]; + if (!targetHz) + { + targetHz = this->config->GetAutoClockHz(this->context->applicationId, HorizonOCModule_Display, this->context->profile, true); + if(!targetHz) + targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, HorizonOCModule_Display, this->context->profile, true); + } + + if(targetHz && this->context->realFreqs[HorizonOCModule_Display] > targetHz && this->context->profile != SysClkProfile_Docked) + this->context->realFreqs[HorizonOCModule_Display] = targetHz; // clean up display real freqs, should probably be moved to the real freqs loop? + + Board::SetDisplayRefreshDockedState(this->context->profile == SysClkProfile_Docked); + } + if(this->context->isSaltyNXInstalled) + this->context->fps = saltyNXIntegration->GetFPS(); + else + this->context->fps = 254; // N/A + + if(this->context->isSaltyNXInstalled) + this->context->resolutionHeight = saltyNXIntegration->GetResolutionHeight(); + else + this->context->resolutionHeight = 0; // N/A + + return hasChanged; +} + +void ClockManager::SetKipData() { + // TODO: figure out if this REALLY causes issues (i doubt it) + // if(Board::GetSocType() == SysClkSocType_Mariko) { + // if(R_FAILED(I2c_BuckConverter_SetMvOut(&I2c_Mariko_DRAM_VDDQ, this->config->GetConfigValue(KipConfigValue_marikoEmcVddqVolt) / 1000))) { + // FileUtils::LogLine("[clock_manager] Failed set i2c vddq"); + // writeNotification("Horizon OC\nFailed to write I2C\nwhile setting vddq"); + // } + // } + CustomizeTable table; + FILE* fp; + fp = fopen("sdmc:/atmosphere/kips/hoc.kip", "r"); + + if (fp == NULL) { + writeNotification("Horizon OC\nKip opening failed"); + kipAvailable = false; + return; + } else { + kipAvailable = true; + fclose(fp); + } + + if (!cust_read_and_cache("sdmc:/atmosphere/kips/hoc.kip", &table)) { + FileUtils::LogLine("[clock_manager] Failed to read KIP file"); + writeNotification("Horizon OC\nKip read failed"); + return; + } + + CUST_WRITE_FIELD_BATCH(&table, custRev, this->config->GetConfigValue(KipConfigValue_custRev)); + // CUST_WRITE_FIELD_BATCH(&table, mtcConf, this->config->GetConfigValue(KipConfigValue_mtcConf)); + CUST_WRITE_FIELD_BATCH(&table, hpMode, this->config->GetConfigValue(KipConfigValue_hpMode)); + + CUST_WRITE_FIELD_BATCH(&table, commonEmcMemVolt, this->config->GetConfigValue(KipConfigValue_commonEmcMemVolt)); + CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock, this->config->GetConfigValue(KipConfigValue_eristaEmcMaxClock)); + CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock1, this->config->GetConfigValue(KipConfigValue_eristaEmcMaxClock1)); + CUST_WRITE_FIELD_BATCH(&table, eristaEmcMaxClock2, this->config->GetConfigValue(KipConfigValue_eristaEmcMaxClock2)); + CUST_WRITE_FIELD_BATCH(&table, marikoEmcMaxClock, this->config->GetConfigValue(KipConfigValue_marikoEmcMaxClock)); + CUST_WRITE_FIELD_BATCH(&table, marikoEmcVddqVolt, this->config->GetConfigValue(KipConfigValue_marikoEmcVddqVolt)); + CUST_WRITE_FIELD_BATCH(&table, emcDvbShift, this->config->GetConfigValue(KipConfigValue_emcDvbShift)); + + CUST_WRITE_FIELD_BATCH(&table, t1_tRCD, this->config->GetConfigValue(KipConfigValue_t1_tRCD)); + CUST_WRITE_FIELD_BATCH(&table, t2_tRP, this->config->GetConfigValue(KipConfigValue_t2_tRP)); + CUST_WRITE_FIELD_BATCH(&table, t3_tRAS, this->config->GetConfigValue(KipConfigValue_t3_tRAS)); + CUST_WRITE_FIELD_BATCH(&table, t4_tRRD, this->config->GetConfigValue(KipConfigValue_t4_tRRD)); + CUST_WRITE_FIELD_BATCH(&table, t5_tRFC, this->config->GetConfigValue(KipConfigValue_t5_tRFC)); + CUST_WRITE_FIELD_BATCH(&table, t6_tRTW, this->config->GetConfigValue(KipConfigValue_t6_tRTW)); + CUST_WRITE_FIELD_BATCH(&table, t7_tWTR, this->config->GetConfigValue(KipConfigValue_t7_tWTR)); + CUST_WRITE_FIELD_BATCH(&table, t8_tREFI, this->config->GetConfigValue(KipConfigValue_t8_tREFI)); + CUST_WRITE_FIELD_BATCH(&table, mem_burst_read_latency, this->config->GetConfigValue(KipConfigValue_mem_burst_read_latency)); + CUST_WRITE_FIELD_BATCH(&table, mem_burst_write_latency, this->config->GetConfigValue(KipConfigValue_mem_burst_write_latency)); + CUST_WRITE_FIELD_BATCH(&table, eristaCpuUV, this->config->GetConfigValue(KipConfigValue_eristaCpuUV)); + CUST_WRITE_FIELD_BATCH(&table, eristaCpuVmin, this->config->GetConfigValue(KipConfigValue_eristaCpuVmin)); + CUST_WRITE_FIELD_BATCH(&table, eristaCpuMaxVolt, this->config->GetConfigValue(KipConfigValue_eristaCpuMaxVolt)); + CUST_WRITE_FIELD_BATCH(&table, eristaCpuUnlock, this->config->GetConfigValue(KipConfigValue_eristaCpuUnlock)); + + CUST_WRITE_FIELD_BATCH(&table, marikoCpuUVLow, this->config->GetConfigValue(KipConfigValue_marikoCpuUVLow)); + CUST_WRITE_FIELD_BATCH(&table, marikoCpuUVHigh, this->config->GetConfigValue(KipConfigValue_marikoCpuUVHigh)); + CUST_WRITE_FIELD_BATCH(&table, tableConf, this->config->GetConfigValue(KipConfigValue_tableConf)); + CUST_WRITE_FIELD_BATCH(&table, marikoCpuLowVmin, this->config->GetConfigValue(KipConfigValue_marikoCpuLowVmin)); + CUST_WRITE_FIELD_BATCH(&table, marikoCpuHighVmin, this->config->GetConfigValue(KipConfigValue_marikoCpuHighVmin)); + CUST_WRITE_FIELD_BATCH(&table, marikoCpuMaxVolt, this->config->GetConfigValue(KipConfigValue_marikoCpuMaxVolt)); + CUST_WRITE_FIELD_BATCH(&table, marikoCpuMaxClock, this->config->GetConfigValue(KipConfigValue_marikoCpuMaxClock)); + + CUST_WRITE_FIELD_BATCH(&table, eristaCpuBoostClock, this->config->GetConfigValue(KipConfigValue_eristaCpuBoostClock)); + CUST_WRITE_FIELD_BATCH(&table, marikoCpuBoostClock, this->config->GetConfigValue(KipConfigValue_marikoCpuBoostClock)); + + CUST_WRITE_FIELD_BATCH(&table, eristaGpuUV, this->config->GetConfigValue(KipConfigValue_eristaGpuUV)); + CUST_WRITE_FIELD_BATCH(&table, eristaGpuVmin, this->config->GetConfigValue(KipConfigValue_eristaGpuVmin)); + + CUST_WRITE_FIELD_BATCH(&table, marikoGpuUV, this->config->GetConfigValue(KipConfigValue_marikoGpuUV)); + CUST_WRITE_FIELD_BATCH(&table, marikoGpuVmin, this->config->GetConfigValue(KipConfigValue_marikoGpuVmin)); + CUST_WRITE_FIELD_BATCH(&table, marikoGpuVmax, this->config->GetConfigValue(KipConfigValue_marikoGpuVmax)); + + CUST_WRITE_FIELD_BATCH(&table, commonGpuVoltOffset, this->config->GetConfigValue(KipConfigValue_commonGpuVoltOffset)); + CUST_WRITE_FIELD_BATCH(&table, gpuSpeedo, this->config->GetConfigValue(KipConfigValue_gpuSpeedo)); + + for (int i = 0; i < 24; i++) { + table.marikoGpuVoltArray[i] = this->config->GetConfigValue((SysClkConfigValue)(KipConfigValue_g_volt_76800 + i)); + } + + for (int i = 0; i < 27; i++) { + table.eristaGpuVoltArray[i] = this->config->GetConfigValue((SysClkConfigValue)(KipConfigValue_g_volt_e_76800 + i)); + } + + CUST_WRITE_FIELD_BATCH(&table, t6_tRTW_fine_tune, this->config->GetConfigValue(KipConfigValue_t6_tRTW_fine_tune)); + CUST_WRITE_FIELD_BATCH(&table, t7_tWTR_fine_tune, this->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"); + writeNotification("Horizon OC\nKip write failed"); + } + + SysClkConfigValueList configValues; + this->config->GetConfigValues(&configValues); + + configValues.values[KipCrc32] = (u64)checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum + + if (this->config->SetConfigValues(&configValues, false)) { + FileUtils::LogLine("[clock_manager] Successfully loaded KIP data into config"); + } else { + FileUtils::LogLine("[clock_manager] Warning: Failed to set config values from KIP"); + writeNotification("Horizon OC\nKip config set failed"); + } +} + +// I know this is very hacky, but the config system in the sysmodule doesn't really support writing + +void ClockManager::GetKipData() { + FILE* fp; + if(this->config->Refresh()) { + + fp = fopen("sdmc:/atmosphere/kips/hoc.kip", "r"); + + if (fp == NULL) { + writeNotification("Horizon OC\nKip opening failed"); + kipAvailable = false; + return; + } else { + kipAvailable = true; + fclose(fp); + } + + + + SysClkConfigValueList configValues; + this->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"); + writeNotification("Horizon OC\nKip read failed"); + return; + } + + if((u64)checksum_file("sdmc:/atmosphere/kips/hoc.kip") != this->config->GetConfigValue(KipCrc32) && !this->config->GetConfigValue(HocClkConfigValue_IsFirstLoad)) { + SetKipData(); + writeNotification("Horizon OC\nKIP has been updated"); + writeNotification("Horizon OC\nPlease reboot your console"); + writeNotification("Horizon OC\nto complete the update"); + return; + } + if(this->config->GetConfigValue(HocClkConfigValue_IsFirstLoad) == true) { + configValues.values[HocClkConfigValue_IsFirstLoad] = (u64)false; + writeNotification("Horizon OC has been installed"); + } + static bool writeBootConfigValues = true; + + configValues.values[KipCrc32] = (u64)checksum_file("sdmc:/atmosphere/kips/hoc.kip"); // write checksum + + + if(writeBootConfigValues) { + writeBootConfigValues = false; + + // initialConfigValues[KipConfigValue_mtcConf] = cust_get_mtc_conf(&table); + initialConfigValues[KipConfigValue_hpMode] = cust_get_hp_mode(&table); + + initialConfigValues[KipConfigValue_commonEmcMemVolt] = cust_get_common_emc_volt(&table); + initialConfigValues[KipConfigValue_eristaEmcMaxClock] = cust_get_erista_emc_max(&table); + initialConfigValues[KipConfigValue_eristaEmcMaxClock1] = cust_get_erista_emc_max1(&table); + initialConfigValues[KipConfigValue_eristaEmcMaxClock2] = cust_get_erista_emc_max2(&table); + initialConfigValues[KipConfigValue_marikoEmcMaxClock] = cust_get_mariko_emc_max(&table); + initialConfigValues[KipConfigValue_marikoEmcVddqVolt] = cust_get_mariko_emc_vddq(&table); + initialConfigValues[KipConfigValue_emcDvbShift] = cust_get_emc_dvb_shift(&table); + + initialConfigValues[KipConfigValue_t1_tRCD] = cust_get_tRCD(&table); + initialConfigValues[KipConfigValue_t2_tRP] = cust_get_tRP(&table); + initialConfigValues[KipConfigValue_t3_tRAS] = cust_get_tRAS(&table); + initialConfigValues[KipConfigValue_t4_tRRD] = cust_get_tRRD(&table); + initialConfigValues[KipConfigValue_t5_tRFC] = cust_get_tRFC(&table); + initialConfigValues[KipConfigValue_t6_tRTW] = cust_get_tRTW(&table); + initialConfigValues[KipConfigValue_t7_tWTR] = cust_get_tWTR(&table); + initialConfigValues[KipConfigValue_t8_tREFI] = cust_get_tREFI(&table); + initialConfigValues[KipConfigValue_mem_burst_read_latency] = cust_get_burst_read_lat(&table); + initialConfigValues[KipConfigValue_mem_burst_write_latency] = cust_get_burst_write_lat(&table); + + initialConfigValues[KipConfigValue_eristaCpuUV] = cust_get_erista_cpu_uv(&table); + initialConfigValues[KipConfigValue_eristaCpuVmin] = cust_get_eristaCpuVmin(&table); + initialConfigValues[KipConfigValue_eristaCpuMaxVolt] = cust_get_erista_cpu_max_volt(&table); + initialConfigValues[KipConfigValue_eristaCpuUnlock] = cust_get_eristaCpuUnlock(&table); + + initialConfigValues[KipConfigValue_marikoCpuUVLow] = cust_get_mariko_cpu_uv_low(&table); + initialConfigValues[KipConfigValue_marikoCpuUVHigh] = cust_get_mariko_cpu_uv_high(&table); + initialConfigValues[KipConfigValue_tableConf] = cust_get_table_conf(&table); + initialConfigValues[KipConfigValue_marikoCpuLowVmin] = cust_get_mariko_cpu_low_vmin(&table); + initialConfigValues[KipConfigValue_marikoCpuHighVmin] = cust_get_mariko_cpu_high_vmin(&table); + initialConfigValues[KipConfigValue_marikoCpuMaxVolt] = cust_get_mariko_cpu_max_volt(&table); + initialConfigValues[KipConfigValue_marikoCpuMaxClock] = cust_get_marikoCpuMaxClock(&table); + initialConfigValues[KipConfigValue_eristaCpuBoostClock] = cust_get_erista_cpu_boost(&table); + initialConfigValues[KipConfigValue_marikoCpuBoostClock] = cust_get_mariko_cpu_boost(&table); + + initialConfigValues[KipConfigValue_eristaGpuUV] = cust_get_erista_gpu_uv(&table); + initialConfigValues[KipConfigValue_eristaGpuVmin] = cust_get_erista_gpu_vmin(&table); + initialConfigValues[KipConfigValue_marikoGpuUV] = cust_get_mariko_gpu_uv(&table); + initialConfigValues[KipConfigValue_marikoGpuVmin] = cust_get_mariko_gpu_vmin(&table); + initialConfigValues[KipConfigValue_marikoGpuVmax] = cust_get_mariko_gpu_vmax(&table); + initialConfigValues[KipConfigValue_commonGpuVoltOffset] = cust_get_common_gpu_offset(&table); + initialConfigValues[KipConfigValue_gpuSpeedo] = cust_get_gpu_speedo(&table); + initialConfigValues[KipConfigValue_t6_tRTW_fine_tune] = cust_get_tRTW_fine_tune(&table); + initialConfigValues[KipConfigValue_t7_tWTR_fine_tune] = cust_get_tWTR_fine_tune(&table); + } + + // configValues.values[KipConfigValue_mtcConf] = cust_get_mtc_conf(&table); + configValues.values[KipConfigValue_hpMode] = cust_get_hp_mode(&table); + + configValues.values[KipConfigValue_commonEmcMemVolt] = cust_get_common_emc_volt(&table); + configValues.values[KipConfigValue_eristaEmcMaxClock] = cust_get_erista_emc_max(&table); + configValues.values[KipConfigValue_eristaEmcMaxClock1] = cust_get_erista_emc_max1(&table); + configValues.values[KipConfigValue_eristaEmcMaxClock2] = cust_get_erista_emc_max2(&table); + configValues.values[KipConfigValue_marikoEmcMaxClock] = cust_get_mariko_emc_max(&table); + configValues.values[KipConfigValue_marikoEmcVddqVolt] = cust_get_mariko_emc_vddq(&table); + configValues.values[KipConfigValue_emcDvbShift] = cust_get_emc_dvb_shift(&table); + + configValues.values[KipConfigValue_t1_tRCD] = cust_get_tRCD(&table); + configValues.values[KipConfigValue_t2_tRP] = cust_get_tRP(&table); + configValues.values[KipConfigValue_t3_tRAS] = cust_get_tRAS(&table); + configValues.values[KipConfigValue_t4_tRRD] = cust_get_tRRD(&table); + configValues.values[KipConfigValue_t5_tRFC] = cust_get_tRFC(&table); + 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_mem_burst_read_latency] = cust_get_burst_read_lat(&table); + configValues.values[KipConfigValue_mem_burst_write_latency] = cust_get_burst_write_lat(&table); + + configValues.values[KipConfigValue_eristaCpuUV] = cust_get_erista_cpu_uv(&table); + configValues.values[KipConfigValue_eristaCpuVmin] = cust_get_eristaCpuVmin(&table); + configValues.values[KipConfigValue_eristaCpuMaxVolt] = cust_get_erista_cpu_max_volt(&table); + configValues.values[KipConfigValue_eristaCpuUnlock] = cust_get_eristaCpuUnlock(&table); + + + configValues.values[KipConfigValue_marikoCpuUVLow] = cust_get_mariko_cpu_uv_low(&table); + configValues.values[KipConfigValue_marikoCpuUVHigh] = cust_get_mariko_cpu_uv_high(&table); + configValues.values[KipConfigValue_tableConf] = cust_get_table_conf(&table); + configValues.values[KipConfigValue_marikoCpuLowVmin] = cust_get_mariko_cpu_low_vmin(&table); + configValues.values[KipConfigValue_marikoCpuHighVmin] = cust_get_mariko_cpu_high_vmin(&table); + configValues.values[KipConfigValue_marikoCpuMaxVolt] = cust_get_mariko_cpu_max_volt(&table); + configValues.values[KipConfigValue_marikoCpuMaxClock] = cust_get_marikoCpuMaxClock(&table); + configValues.values[KipConfigValue_eristaCpuBoostClock] = cust_get_erista_cpu_boost(&table); + configValues.values[KipConfigValue_marikoCpuBoostClock] = cust_get_mariko_cpu_boost(&table); + + configValues.values[KipConfigValue_eristaGpuUV] = cust_get_erista_gpu_uv(&table); + configValues.values[KipConfigValue_eristaGpuVmin] = cust_get_erista_gpu_vmin(&table); + configValues.values[KipConfigValue_marikoGpuUV] = cust_get_mariko_gpu_uv(&table); + configValues.values[KipConfigValue_marikoGpuVmin] = cust_get_mariko_gpu_vmin(&table); + configValues.values[KipConfigValue_marikoGpuVmax] = cust_get_mariko_gpu_vmax(&table); + configValues.values[KipConfigValue_commonGpuVoltOffset] = cust_get_common_gpu_offset(&table); + configValues.values[KipConfigValue_gpuSpeedo] = Board::getSpeedo(HorizonOCSpeedo_GPU); // cust_get_gpu_speedo(&table); + + for (int i = 0; i < 24; i++) { + configValues.values[KipConfigValue_g_volt_76800 + i] = cust_get_mariko_gpu_volt(&table, i); + initialConfigValues[KipConfigValue_g_volt_76800 + i] = cust_get_mariko_gpu_volt(&table, i); + } + + for (int i = 0; i < 27; i++) { + configValues.values[KipConfigValue_g_volt_e_76800 + i] = cust_get_erista_gpu_volt(&table, i); + initialConfigValues[KipConfigValue_g_volt_e_76800 + i] = cust_get_erista_gpu_volt(&table, i); + } + + 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(SysClkConfigValueList) <= sizeof(configValues)) { + if (this->config->SetConfigValues(&configValues, false)) { + FileUtils::LogLine("[clock_manager] Successfully loaded KIP data into config"); + } else { + FileUtils::LogLine("[clock_manager] Warning: Failed to set config values from KIP"); + writeNotification("Horizon OC\nKip config set failed"); + } + } else { + FileUtils::LogLine("[clock_manager] Error: Config value list buffer size mismatch"); + writeNotification("Horizon OC\nConfig Buffer Mismatch"); + } + } else { + FileUtils::LogLine("[clock_manager] Config refresh error in GetKipData!"); + writeNotification("Horizon OC\nConfig refresh failed"); + } +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/clock_manager.h b/Source/rewrite-hoc-clk/sysmodule/src/clock_manager.h new file mode 100644 index 00000000..b09d10e1 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/clock_manager.h @@ -0,0 +1,261 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ +#pragma once +#include +#include +#include +#include "config.h" +#include "board.h" +#include +#include "integrations.h" + +class SysDockIntegration; +class SaltyNXIntegration; +class ClockManager +{ + public: + /** + * Get instance + * @return Pointer to a ClockManager instance + */ + static ClockManager* GetInstance(); + static void Initialize(); + static void Exit(); + ClockManager(); + virtual ~ClockManager(); + + /** + * Get context object + * @return Context instance + */ + SysClkContext GetCurrentContext(); + + /** + * Get config object + * @return Pointer to a config instance + */ + Config* GetConfig(); + + /** + * Set clock manager running + * @param running Is running or not? + */ + void SetRunning(bool running); + + /** + * Is clock manager running + * @return running or not? + */ + bool Running(); + + /** + * Get frequency list from clkrst + * + * @param module Module to get frequency list for + * @param list List of frequencies + * @param maxCount How many entries to expect in list. Usually 32 + * @param outCount How many entries were retrived + */ + void GetFreqList(SysClkModule module, std::uint32_t* list, std::uint32_t maxCount, std::uint32_t* outCount); + + /** + * Handles safety features + * + */ + void HandleSafetyFeatures(); + + /** + * Handles misc features (currently only battery charge current). + * + */ + void HandleMiscFeatures(); + + /** + * Handles governor state resolution and applies CPU/GPU governor transitions. + * + * @param targetHz Governor override value for the current profile. + */ + void HandleGovernor(uint32_t targetHz); + + /** + * Handles DVFS logic before the frequency set + * + * @param targetHz Governor override value for the current profile. + */ + void DVFSBeforeSet(u32 targetHz); + + /** + * Handles DVFS logic after the frequency set + * + * @param targetHz Governor override value for the current profile. + */ + void DVFSAfterSet(u32 targetHz); + + /** + * Reset the GPU vMin + * + */ + void DVFSReset(); + + /** + * Handles the Live CPU UV Feature + * + */ + void HandleCpuUv(); + + /** + * Handles frequency resets + * + * @param module The module to reset frequency for + * @param isBoost Is in boost mode + */ + void HandleFreqReset(SysClkModule module, bool isBoost); + + /** + * Sets clocks + * + * @param isBoost Is in boost mode + */ + void SetClocks(bool isBoost); + + /** + * Main function, runs every 5s in sleep mode, and a user specified amount when awake + * + */ + void Tick(); + + /** + * Reset CPU/GPU to stock values + * + */ + void ResetToStockClocks(); + + /** + * Wait for the next tick event + * + */ + void WaitForNextTick(); + + /** + * Set the data in the KIP + * + */ + void SetKipData(); + + /** + * Get the data from the KIP + * + */ + void GetKipData(); + + /** + * Runs the CPU Governor + * + * @param arg Cast to ClockManager* for context + */ + static void CpuGovernorThread(void* arg); + + /** + * Runs the GPU Governor + * + * @param arg Cast to ClockManager* for context + */ + static void GovernorThread(void* arg); + + /** + * Runs the VRR Algorithm + * + * @param arg Cast to ClockManager* for context + */ + static void VRRThread(void* arg); + + /** + * Frequency table + * + */ + struct FreqTable { + std::uint32_t count; + std::uint32_t list[SYSCLK_FREQ_LIST_MAX]; + } freqTable[SysClkModule_EnumMax]; + + /** + * Gets the current GPU speedo bracket + * + * @param speedo GPU Speedo + */ + int GetSpeedoBracket (int speedo); + + /** + * Gets the required vMin for a ram frequency for a speedo + * + * @param freq RAM Freq in MHz + * @param speedo GPU Speedo + */ + unsigned int GetGpuVoltage (unsigned int freq, int speedo); + + /** + * Gets the required vMin for a ram frequency for a speedo + * + * @param util Utilization in percentile + * @param tableMaxHz Table Max Hz + */ + static u32 SchedutilTargetHz(u32 util, u32 tableMaxHz); + + /** + * Gets the required vMin for a ram frequency for a speedo + * + * @param table FreqTable for module + * @param targetHz Hz to search for + */ + static u32 TableIndexForHz(const FreqTable& table, u32 targetHz); + + /** + * Gets the required vMin for a ram frequency for a speedo + * + * @param mgr ClockManager instance (runs in a thread so must be passed) + * @param module Module for which to resolve target Hz + */ + static u32 ResolveTargetHz(ClockManager* mgr, SysClkModule module); + + protected: + bool IsAssignableHz(SysClkModule module, std::uint32_t hz); + inline std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile); + std::uint32_t GetNearestHz(SysClkModule module, std::uint32_t inHz, std::uint32_t maxHz); + bool ConfigIntervalTimeout(SysClkConfigValue intervalMsConfigValue, std::uint64_t ns, std::uint64_t* lastLogNs); + void RefreshFreqTableRow(SysClkModule module); + bool RefreshContext(); + static ClockManager *instance; + std::atomic_bool running; + LockableMutex contextMutex; + Config* config; + SysClkContext* context; + std::uint64_t lastTempLogNs; + std::uint64_t lastFreqLogNs; + std::uint64_t lastPowerLogNs; + std::uint64_t lastCsvWriteNs; + SysDockIntegration *sysDockIntegration; + SaltyNXIntegration *saltyNXIntegration; +}; \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/src/config.cpp b/Source/rewrite-hoc-clk/sysmodule/src/config.cpp new file mode 100644 index 00000000..06722105 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/config.cpp @@ -0,0 +1,520 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "file_utils.h" + +Config::Config(std::string path) +{ + this->path = path; + this->loaded = false; + this->profileMHzMap = std::map, std::uint32_t>(); + this->profileCountMap = std::map(); + this->mtime = 0; + this->enabled = false; + for(unsigned int i = 0; i < SysClkModule_EnumMax; i++) + { + this->overrideFreqs[i] = 0; + } + + for(unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++) + { + this->configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i); + } +} + +Config::~Config() +{ + std::scoped_lock lock{this->configMutex}; + this->Close(); +} + +Config* Config::CreateDefault() +{ + return new Config(FILE_CONFIG_DIR "/config.ini"); +} + +void Config::Load() +{ + FileUtils::LogLine("[cfg] Reading %s", this->path.c_str()); + + this->Close(); + this->mtime = this->CheckModificationTime(); + if(!this->mtime) + { + FileUtils::LogLine("[cfg] Error finding file"); + } + else if (!ini_browse(&BrowseIniFunc, this, this->path.c_str())) + { + FileUtils::LogLine("[cfg] Error loading file"); + } + + this->loaded = true; +} + +void Config::Close() +{ + this->loaded = false; + this->profileMHzMap.clear(); + this->profileCountMap.clear(); + + for(unsigned int i = 0; i < SysClkConfigValue_EnumMax; i++) + { + this->configValues[i] = sysclkDefaultConfigValue((SysClkConfigValue)i); + } +} + +bool Config::Refresh() +{ + std::scoped_lock lock{this->configMutex}; + if (!this->loaded || this->mtime != this->CheckModificationTime()) + { + this->Load(); + return true; + } + return false; +} + +bool Config::HasProfilesLoaded() +{ + std::scoped_lock lock{this->configMutex}; + return this->loaded; +} + +time_t Config::CheckModificationTime() +{ + time_t mtime = 0; + struct stat st; + if (stat(this->path.c_str(), &st) == 0) + { + mtime = st.st_mtime; + } + + return mtime; +} + +std::uint32_t Config::FindClockMHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile) +{ + if (this->loaded) + { + std::map, std::uint32_t>::const_iterator it = this->profileMHzMap.find(std::make_tuple(tid, profile, module)); + if (it != this->profileMHzMap.end()) + { + return it->second; + } + } + + return 0; +} + +std::uint32_t Config::FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list profiles, u32 mhzMultiplier) +{ + std::uint32_t mhz = 0; + + if (this->loaded) + { + for(auto profile: profiles) + { + mhz = FindClockMHz(tid, module, profile); + + if(mhz) + { + break; + } + } + } + + return std::max((std::uint32_t)0, mhz * mhzMultiplier); +} + +std::uint32_t Config::GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile, bool returnRaw) +{ + std::scoped_lock lock{this->configMutex}; + switch(profile) + { + case SysClkProfile_Handheld: + return FindClockHzFromProfiles(tid, module, {SysClkProfile_Handheld}, returnRaw ? 1 : 1000000); + case SysClkProfile_HandheldCharging: + case SysClkProfile_HandheldChargingUSB: + return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingUSB, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}, returnRaw ? 1 : 1000000); + case SysClkProfile_HandheldChargingOfficial: + return FindClockHzFromProfiles(tid, module, {SysClkProfile_HandheldChargingOfficial, SysClkProfile_HandheldCharging, SysClkProfile_Handheld}, returnRaw ? 1 : 1000000); + case SysClkProfile_Docked: + return FindClockHzFromProfiles(tid, module, {SysClkProfile_Docked}, returnRaw ? 1 : 1000000); + default: + ERROR_THROW("Unhandled SysClkProfile: %u", profile); + } + + return 0; +} + +void Config::GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles) +{ + std::scoped_lock lock{this->configMutex}; + + for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++) + { + for(unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + out_profiles->mhzMap[profile][module] = FindClockMHz(tid, (SysClkModule)module, (SysClkProfile)profile); + } + } +} + +bool Config::SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate) +{ + std::scoped_lock lock{this->configMutex}; + uint8_t numProfiles = 0; + + char section[17] = {0}; + snprintf(section, sizeof(section), "%016lX", tid); + + std::vector keys; + std::vector values; + keys.reserve(SysClkProfile_EnumMax * SysClkModule_EnumMax); + values.reserve(SysClkProfile_EnumMax * SysClkModule_EnumMax); + + std::uint32_t* mhz = &profiles->mhz[0]; + + for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++) + { + for(unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + if(*mhz) + { + numProfiles++; + + std::string key = std::string(Board::GetProfileName((SysClkProfile)profile, false)) + + "_" + + Board::GetModuleName((SysClkModule)module, false); + std::string value = std::to_string(*mhz); + + keys.push_back(key); + values.push_back(value); + } + mhz++; + } + } + + std::vector keyPointers; + std::vector valuePointers; + keyPointers.reserve(keys.size() + 1); + valuePointers.reserve(values.size() + 1); + + for(size_t i = 0; i < keys.size(); i++) { + keyPointers.push_back(keys[i].c_str()); + valuePointers.push_back(values[i].c_str()); + } + keyPointers.push_back(NULL); + valuePointers.push_back(NULL); + + if(!ini_putsection(section, keyPointers.data(), valuePointers.data(), this->path.c_str())) + { + return false; + } + + if(immediate) + { + mhz = &profiles->mhz[0]; + this->profileCountMap[tid] = numProfiles; + for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++) + { + for(unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + if(*mhz) + { + this->profileMHzMap[std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module)] = *mhz; + } + else + { + this->profileMHzMap.erase(std::make_tuple(tid, (SysClkProfile)profile, (SysClkModule)module)); + } + mhz++; + } + } + } + + return true; +} + +std::uint8_t Config::GetProfileCount(std::uint64_t tid) +{ + std::map::iterator it = this->profileCountMap.find(tid); + if (it == this->profileCountMap.end()) + { + return 0; + } + + return it->second; +} + +int Config::BrowseIniFunc(const char* section, const char* key, const char* value, void* userdata) +{ + Config* config = (Config*)userdata; + std::uint64_t input; + if(!strcmp(section, CONFIG_VAL_SECTION)) + { + for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++) + { + if(!strcmp(key, sysclkFormatConfigValue((SysClkConfigValue)kval, false))) + { + input = strtoul(value, NULL, 0); + if(!sysclkValidConfigValue((SysClkConfigValue)kval, input)) + { + input = sysclkDefaultConfigValue((SysClkConfigValue)kval); + FileUtils::LogLine("[cfg] Invalid value for key '%s' in section '%s': using default %d", key, section, input); + } + config->configValues[kval] = input; + return 1; + } + } + + FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized config value", key, section); + return 1; + } + + std::uint64_t tid = strtoul(section, NULL, 16); + + if(!tid || strlen(section) != 16) + { + FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid TitleID", key, section); + return 1; + } + + SysClkProfile parsedProfile = SysClkProfile_EnumMax; + SysClkModule parsedModule = SysClkModule_EnumMax; + + for(unsigned int profile = 0; profile < SysClkProfile_EnumMax; profile++) + { + const char* profileCode = Board::GetProfileName((SysClkProfile)profile, false); + size_t profileCodeLen = strlen(profileCode); + + if(!strncmp(key, profileCode, profileCodeLen) && key[profileCodeLen] == '_') + { + const char* subkey = key + profileCodeLen + 1; + + for(unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + const char* moduleCode = Board::GetModuleName((SysClkModule)module, false); + size_t moduleCodeLen = strlen(moduleCode); + if(!strncmp(subkey, moduleCode, moduleCodeLen) && subkey[moduleCodeLen] == '\0') + { + parsedProfile = (SysClkProfile)profile; + parsedModule = (SysClkModule)module; + } + } + } + } + + if(parsedModule == SysClkModule_EnumMax || parsedProfile == SysClkProfile_EnumMax) + { + FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Unrecognized key", key, section); + return 1; + } + + std::uint32_t mhz = strtoul(value, NULL, 10); + if(!mhz) + { + FileUtils::LogLine("[cfg] Skipping key '%s' in section '%s': Invalid value", key, section); + return 1; + } + + config->profileMHzMap[std::make_tuple(tid, parsedProfile, parsedModule)] = mhz; + std::map::iterator it = config->profileCountMap.find(tid); + if (it == config->profileCountMap.end()) + { + config->profileCountMap[tid] = 1; + } + else + { + it->second++; + } + + return 1; +} + +void Config::SetEnabled(bool enabled) +{ + this->enabled = enabled; +} + +bool Config::Enabled() +{ + return this->enabled; +} + +void Config::SetOverrideHz(SysClkModule module, std::uint32_t hz) +{ + ASSERT_ENUM_VALID(SysClkModule, module); + + std::scoped_lock lock{this->overrideMutex}; + + this->overrideFreqs[module] = hz; +} + +std::uint32_t Config::GetOverrideHz(SysClkModule module) +{ + ASSERT_ENUM_VALID(SysClkModule, module); + + std::scoped_lock lock{this->overrideMutex}; + + return this->overrideFreqs[module]; +} + +std::uint64_t Config::GetConfigValue(SysClkConfigValue kval) +{ + ASSERT_ENUM_VALID(SysClkConfigValue, kval); + + std::scoped_lock lock{this->configMutex}; + + return this->configValues[kval]; +} + +const char* Config::GetConfigValueName(SysClkConfigValue kval, bool pretty) +{ + ASSERT_ENUM_VALID(SysClkConfigValue, kval); + + const char* result = sysclkFormatConfigValue(kval, pretty); + + return result; +} + +void Config::GetConfigValues(SysClkConfigValueList* out_configValues) +{ + std::scoped_lock lock{this->configMutex}; + + for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++) + { + out_configValues->values[kval] = this->configValues[kval]; + } +} + +bool Config::SetConfigValues(SysClkConfigValueList* configValues, bool immediate) +{ + std::scoped_lock lock{this->configMutex}; + + std::vector iniKeys; + std::vector iniValues; + + iniKeys.reserve(SysClkConfigValue_EnumMax + 1); + iniValues.reserve(SysClkConfigValue_EnumMax); + + for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++) + { + if(!sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval]) || + configValues->values[kval] == sysclkDefaultConfigValue((SysClkConfigValue)kval)) + { + continue; + } + + iniValues.push_back(std::to_string(configValues->values[kval])); + iniKeys.push_back(sysclkFormatConfigValue((SysClkConfigValue)kval, false)); + } + + // Null terminate + iniKeys.push_back(NULL); + + // Build pointer array for ini function + std::vector valuePointers; + valuePointers.reserve(iniValues.size() + 1); + for(const auto& val : iniValues) { + valuePointers.push_back(val.c_str()); + } + valuePointers.push_back(NULL); + + if(!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), this->path.c_str())) + { + return false; + } + + // Only actually apply changes in memory after a successful save + if(immediate) + { + for(unsigned int kval = 0; kval < SysClkConfigValue_EnumMax; kval++) + { + if(sysclkValidConfigValue((SysClkConfigValue)kval, configValues->values[kval])) + { + this->configValues[kval] = configValues->values[kval]; + } + else + { + this->configValues[kval] = sysclkDefaultConfigValue((SysClkConfigValue)kval); + } + } + } + + return true; +} + +bool Config::ResetConfigValue(SysClkConfigValue kval) +{ + if (!SYSCLK_ENUM_VALID(SysClkConfigValue, kval)) { + FileUtils::LogLine("[cfg] Invalid SysClkConfigValue: %u", kval); + return false; + } + + std::scoped_lock lock{this->configMutex}; + + std::uint64_t defaultValue = sysclkDefaultConfigValue(kval); + + std::vector iniKeys; + std::vector iniValues; + + iniKeys.reserve(2); + iniValues.reserve(1); + + const char* keyStr = sysclkFormatConfigValue(kval, false); + + iniKeys.push_back(keyStr); + iniValues.push_back(""); + + iniKeys.push_back(NULL); + + std::vector valuePointers; + valuePointers.reserve(iniValues.size() + 1); + for (const auto& val : iniValues) { + valuePointers.push_back(val.c_str()); + } + valuePointers.push_back(NULL); + + if (!ini_putsection(CONFIG_VAL_SECTION, iniKeys.data(), valuePointers.data(), this->path.c_str())) { + FileUtils::LogLine("[cfg] Failed to reset config value %u in INI", kval); + return false; + } + + this->configValues[kval] = defaultValue; + FileUtils::LogLine("[cfg] Reset config value %u to default: %llu", kval, defaultValue); + + return true; +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/src/config.h b/Source/rewrite-hoc-clk/sysmodule/src/config.h new file mode 100644 index 00000000..23a72acf --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/config.h @@ -0,0 +1,91 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "board.h" + +#define CONFIG_VAL_SECTION "values" + +class Config +{ + public: + Config(std::string path); + virtual ~Config(); + + static Config* CreateDefault(); + + bool Refresh(); + + bool HasProfilesLoaded(); + + std::uint8_t GetProfileCount(std::uint64_t tid); + void GetProfiles(std::uint64_t tid, SysClkTitleProfileList* out_profiles); + bool SetProfiles(std::uint64_t tid, SysClkTitleProfileList* profiles, bool immediate); + std::uint32_t GetAutoClockHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile, bool returnRaw); + + void SetEnabled(bool enabled); + bool Enabled(); + void SetOverrideHz(SysClkModule module, std::uint32_t hz); + std::uint32_t GetOverrideHz(SysClkModule module); + + std::uint64_t GetConfigValue(SysClkConfigValue val); + const char* GetConfigValueName(SysClkConfigValue val, bool pretty); + void GetConfigValues(SysClkConfigValueList* out_configValues); + bool SetConfigValues(SysClkConfigValueList* configValues, bool immediate); + bool ResetConfigValue(SysClkConfigValue kval); + bool SetInternalValues(bool immediate); + bool SetConfigValue(SysClkConfigValue kval, std::uint64_t value, bool immediate = true); + + uint64_t configValues[SysClkConfigValue_EnumMax]; + protected: + void Load(); + void Close(); + bool kipOverride[SysClkConfigValue_EnumMax]; + time_t CheckModificationTime(); + std::uint32_t FindClockMHz(std::uint64_t tid, SysClkModule module, SysClkProfile profile); + std::uint32_t FindClockHzFromProfiles(std::uint64_t tid, SysClkModule module, std::initializer_list profiles, u32 mhzMultiplier = 1000000); + static int BrowseIniFunc(const char* section, const char* key, const char* value, void* userdata); + + std::map, std::uint32_t> profileMHzMap; + std::map profileCountMap; + bool loaded; + std::string path; + time_t mtime; + LockableMutex configMutex; + LockableMutex overrideMutex; + std::atomic_bool enabled; + std::uint32_t overrideFreqs[SysClkModule_EnumMax]; +}; diff --git a/Source/rewrite-hoc-clk/sysmodule/src/errors.cpp b/Source/rewrite-hoc-clk/sysmodule/src/errors.cpp new file mode 100644 index 00000000..b7949432 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/errors.cpp @@ -0,0 +1,54 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "errors.h" +#include +#include +#include "file_utils.h" +void Errors::ThrowException(const char* format, ...) +{ + va_list args; + va_start(args, format); + const char* msg = Errors::FormatMessage(format, args); + va_end(args); + FileUtils::LogLine(format, args); + throw std::runtime_error(msg); +} + +const char* Errors::FormatMessage(const char* format, va_list args) +{ + size_t len = vsnprintf(NULL, 0, format, args) * sizeof(char); + char* buf = (char*)malloc(len + 1); + if (buf == NULL) + { + return format; + } + + vsnprintf(buf, len + 1, format, args); + + return buf; +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/errors.h b/Source/rewrite-hoc-clk/sysmodule/src/errors.h new file mode 100644 index 00000000..f7575cf1 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/errors.h @@ -0,0 +1,52 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include +#include + +#define ERROR_THROW(format, ...) Errors::ThrowException(format "\n in %s:%u", ##__VA_ARGS__, __FILE__, __LINE__) +#define ERROR_RESULT_THROW(rc, format, ...) ERROR_THROW(format "\n RC: [0x%x] %04d-%04d", ##__VA_ARGS__, rc, R_MODULE(rc), R_DESCRIPTION(rc)) +#define ASSERT_RESULT_OK(rc, format, ...) \ + if (R_FAILED(rc)) \ + { \ + ERROR_RESULT_THROW(rc, "ASSERT_RESULT_OK: " format, ##__VA_ARGS__); \ + } +#define ASSERT_ENUM_VALID(n, v) \ + if(!SYSCLK_ENUM_VALID(n, v)) { \ + ERROR_THROW("No such %s: %u", #n, v); \ + } + +class Errors +{ + public: + static void ThrowException(const char* format, ...); + + protected: + static const char* FormatMessage(const char* format, va_list args); +}; diff --git a/Source/rewrite-hoc-clk/sysmodule/src/file_utils.cpp b/Source/rewrite-hoc-clk/sysmodule/src/file_utils.cpp new file mode 100644 index 00000000..a99494e8 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/file_utils.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) Souldbminer 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "file_utils.h" +#include + +static LockableMutex g_log_mutex; +static LockableMutex g_csv_mutex; +static std::atomic_bool g_has_initialized = false; +static bool g_log_enabled = false; +static std::uint64_t g_last_flag_check = 0; + +extern "C" void __libnx_init_time(void); + +static void _FileUtils_InitializeThreadFunc(void* args) +{ + FileUtils::Initialize(); +} + +bool FileUtils::IsInitialized() +{ + return g_has_initialized; +} + +void FileUtils::LogLine(const char* format, ...) +{ + std::scoped_lock lock{g_log_mutex}; + + va_list args; + va_start(args, format); + if (g_has_initialized) + { + FileUtils::RefreshFlags(false); + + if(g_log_enabled) + { + FILE* file = fopen(FILE_LOG_FILE_PATH, "a"); + + if (file) + { + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + struct tm* nowTm = localtime(&now.tv_sec); + + fprintf(file, "[%04d-%02d-%02d %02d:%02d:%02d.%03ld] ", nowTm->tm_year+1900, nowTm->tm_mon+1, nowTm->tm_mday, nowTm->tm_hour, nowTm->tm_min, nowTm->tm_sec, now.tv_nsec / 1000000UL); + vfprintf(file, format, args); + fprintf(file, "\n"); + fclose(file); + } + } + } + va_end(args); +} + +void FileUtils::WriteContextToCsv(const SysClkContext* context) +{ + std::scoped_lock lock{g_csv_mutex}; + + FILE* file = fopen(FILE_CONTEXT_CSV_PATH, "a"); + + if (file) + { + // Print header + if(!ftell(file)) + { + fprintf(file, "timestamp,profile,app_tid"); + + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + fprintf(file, ",%s_hz", sysclkFormatModule((SysClkModule)module, false)); + } + + for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++) + { + fprintf(file, ",%s_milliC", sysclkFormatThermalSensor((SysClkThermalSensor)sensor, false)); + } + + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + fprintf(file, ",%s_real_hz", sysclkFormatModule((SysClkModule)module, false)); + } + + for (unsigned int sensor = 0; sensor < SysClkPowerSensor_EnumMax; sensor++) + { + fprintf(file, ",%s_mw", sysclkFormatPowerSensor((SysClkPowerSensor)sensor, false)); + } + + fprintf(file, "\n"); + } + + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + + fprintf(file, "%ld%03ld,%s,%016lx", now.tv_sec, now.tv_nsec / 1000000UL, sysclkFormatProfile(context->profile, false), context->applicationId); + + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + fprintf(file, ",%d", context->freqs[module]); + } + + for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++) + { + fprintf(file, ",%d", context->temps[sensor]); + } + + for (unsigned int module = 0; module < SysClkModule_EnumMax; module++) + { + fprintf(file, ",%d", context->realFreqs[module]); + } + + for (unsigned int sensor = 0; sensor < SysClkPowerSensor_EnumMax; sensor++) + { + fprintf(file, ",%d", context->power[sensor]); + } + + fprintf(file, "\n"); + fclose(file); + } +} + +void FileUtils::RefreshFlags(bool force) +{ + std::uint64_t now = armTicksToNs(armGetSystemTick()); + if(!force && (now - g_last_flag_check) < FILE_FLAG_CHECK_INTERVAL_NS) + { + return; + } + + FILE* file = fopen(FILE_LOG_FLAG_PATH, "r"); + if (file) + { + g_log_enabled = true; + fclose(file); + } else { + g_log_enabled = false; + } + + g_last_flag_check = now; +} + +void FileUtils::InitializeAsync() +{ + Thread initThread = {0}; + threadCreate(&initThread, _FileUtils_InitializeThreadFunc, NULL, NULL, 0x4000, 0x15, 0); + threadStart(&initThread); +} + +Result FileUtils::Initialize() +{ + Result rc = 0; + + if (R_SUCCEEDED(rc)) + { + rc = timeInitialize(); + } + + __libnx_init_time(); + timeExit(); + + if (R_SUCCEEDED(rc)) + { + rc = fsInitialize(); + } + + if (R_SUCCEEDED(rc)) + { + rc = fsdevMountSdmc(); + } + + if (R_SUCCEEDED(rc)) + { + FileUtils::RefreshFlags(true); + g_has_initialized = true; + FileUtils::LogLine("=== hoc-clk " TARGET_VERSION " ==="); + FileUtils::LogLine("by m4xw, natinusala, p-sam, Souldbminer and Lightos_"); + } + + return rc; +} + +void FileUtils::Exit() +{ + if (!g_has_initialized) + { + return; + } + + g_has_initialized = false; + g_log_enabled = false; + + fsdevUnmountAll(); + fsExit(); +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/file_utils.h b/Source/rewrite-hoc-clk/sysmodule/src/file_utils.h new file mode 100644 index 00000000..993420de --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/file_utils.h @@ -0,0 +1,55 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#define FILE_CONFIG_DIR "/config/" TARGET +#define FILE_FLAG_CHECK_INTERVAL_NS (10000ULL * 1000000000ULL) +#define FILE_CONTEXT_CSV_PATH FILE_CONFIG_DIR "/context.csv" +#define FILE_LOG_FLAG_PATH FILE_CONFIG_DIR "/log.flag" +#define FILE_LOG_FILE_PATH FILE_CONFIG_DIR "/log.txt" + +class FileUtils +{ + public: + static void Exit(); + static Result Initialize(); + static bool IsInitialized(); + static bool IsLogEnabled(); + static void InitializeAsync(); + static void LogLine(const char* format, ...); + static void WriteContextToCsv(const SysClkContext* context); + protected: + static void RefreshFlags(bool force); +}; diff --git a/Source/rewrite-hoc-clk/sysmodule/src/integrations.cpp b/Source/rewrite-hoc-clk/sysmodule/src/integrations.cpp new file mode 100644 index 00000000..64077895 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/integrations.cpp @@ -0,0 +1,137 @@ +/* + * 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 . + * + */ + + +#include "integrations.h" +#include +#include +#include "process_management.h" + +SysDockIntegration::SysDockIntegration() { +} + +bool SysDockIntegration::getCurrentSysDockState() { + struct stat st = {0}; + return stat("sdmc:/atmosphere/contents/42000000000000A0/flags/boot2.flag", &st) == 0; +} + +SaltyNXIntegration::SaltyNXIntegration() { +} + +void SaltyNXIntegration::LoadSaltyNX() { + if (!CheckPort()) + return; + LoadSharedMemory(); +} + +bool SaltyNXIntegration::getCurrentSaltyNXState() { + struct stat st = {0}; + return stat("sdmc:/atmosphere/contents/0000000000534C56/flags/boot2.flag", &st) == 0; +} + +bool SaltyNXIntegration::CheckPort() { + Handle saltysd; + + for (int i = 0; i < 67; i++) { + if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) { + svcCloseHandle(saltysd); + break; + } + if (i == 66) return false; + svcSleepThread(1'000'000); + } + + for (int i = 0; i < 67; i++) { + if (R_SUCCEEDED(svcConnectToNamedPort(&saltysd, "InjectServ"))) { + svcCloseHandle(saltysd); + return true; + } + svcSleepThread(1'000'000); + } + + return false; +} + +void SaltyNXIntegration::LoadSharedMemory() { + if (SaltySD_Connect()) + return; + SaltySD_GetSharedMemoryHandle(&remoteSharedMemory); + SaltySD_Term(); + shmemLoadRemote(&_sharedmemory, remoteSharedMemory, 0x1000, Perm_Rw); + if (!shmemMap(&_sharedmemory)) + SharedMemoryUsed = true; +} + +void SaltyNXIntegration::searchSharedMemoryBlock(uintptr_t base) { + ptrdiff_t search_offset = 0; + while (search_offset < 0x1000) { + NxFps = (NxFpsSharedBlock*)(base + search_offset); + if (NxFps->MAGIC == 0x465053) + return; + search_offset += 4; + } + NxFps = 0; +} + +u64 prevTid = 0; + +u8 SaltyNXIntegration::GetFPS() { + if (!SharedMemoryUsed) + return 254; + + u64 tid = ProcessManagement::GetCurrentApplicationId(); + if (tid == 0) + return 254; + + if (prevTid != tid) { + NxFps = 0; + prevTid = tid; + } + + if (!NxFps) { + uintptr_t base = (uintptr_t)shmemGetAddr(&_sharedmemory); + searchSharedMemoryBlock(base); + } + + return NxFps ? NxFps->FPS : 254; +} + +u16 SaltyNXIntegration::GetResolutionHeight() { + if (!SharedMemoryUsed) + return 0; + + u64 tid = ProcessManagement::GetCurrentApplicationId(); + if (tid == 0) + return 0; + + if (prevTid != tid) { + NxFps = 0; + prevTid = tid; + } + + if (!NxFps) { + uintptr_t base = (uintptr_t)shmemGetAddr(&_sharedmemory); + searchSharedMemoryBlock(base); + } + if(NxFps) { + NxFps->renderCalls[0].calls = 0xFFFF; + svcSleepThread(10*1000); + + return NxFps->renderCalls[0].height == 0 ? NxFps->viewportCalls[0].height : NxFps->renderCalls[0].height; + } + return 0; +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/src/integrations.h b/Source/rewrite-hoc-clk/sysmodule/src/integrations.h new file mode 100644 index 00000000..466e2f7d --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/integrations.h @@ -0,0 +1,94 @@ +/* + * 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 . + * + */ + + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "errors.h" +#include "file_utils.h" + +#include "clock_manager.h" + +class SysDockIntegration { +public: + SysDockIntegration(); + + bool getCurrentSysDockState(); +}; + +class SaltyNXIntegration { +public: + struct resolutionCalls { + uint16_t width; + uint16_t height; + uint16_t calls; + }; + + struct NxFpsSharedBlock { + uint32_t MAGIC; + uint8_t FPS; + float FPSavg; + bool pluginActive; + uint8_t FPSlocked; + uint8_t FPSmode; + uint8_t ZeroSync; + uint8_t patchApplied; + uint8_t API; + uint32_t FPSticks[10]; + uint8_t Buffers; + uint8_t SetBuffers; + uint8_t ActiveBuffers; + uint8_t SetActiveBuffers; + union { + struct { + bool handheld: 1; + bool docked: 1; + unsigned int reserved: 6; + } NX_PACKED ds; + uint8_t general; + } displaySync; + resolutionCalls renderCalls[8]; + resolutionCalls viewportCalls[8]; + bool forceOriginalRefreshRate; + bool dontForce60InDocked; + bool forceSuspend; + uint8_t currentRefreshRate; + float readSpeedPerSecond; + uint8_t FPSlockedDocked; + uint64_t frameNumber; + } NX_PACKED; + + NxFpsSharedBlock* NxFps = 0; + SharedMemory _sharedmemory = {}; + bool SharedMemoryUsed = false; + Handle remoteSharedMemory = 1; + SaltyNXIntegration(); + void LoadSaltyNX(); + bool getCurrentSaltyNXState(); + + bool CheckPort(); + void LoadSharedMemory(); + void searchSharedMemoryBlock(uintptr_t base); + u8 GetFPS(); + u16 GetResolutionHeight(); +}; \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/src/ipc_service.cpp b/Source/rewrite-hoc-clk/sysmodule/src/ipc_service.cpp new file mode 100644 index 00000000..4dea5685 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/ipc_service.cpp @@ -0,0 +1,374 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "ipc_service.h" +#include +#include +#include "file_utils.h" +#include "errors.h" +#include "clock_manager.h" +IpcService::IpcService(ClockManager* clockMgr) +{ + std::int32_t priority; + Result rc = svcGetThreadPriority(&priority, CUR_THREAD_HANDLE); + ASSERT_RESULT_OK(rc, "svcGetThreadPriority"); + rc = ipcServerInit(&this->server, SYSCLK_IPC_SERVICE_NAME, 42); + ASSERT_RESULT_OK(rc, "ipcServerInit"); + rc = threadCreate(&this->thread, &IpcService::ProcessThreadFunc, this, NULL, 0x2000, priority, -2); + ASSERT_RESULT_OK(rc, "threadCreate"); + + this->running = false; + this->clockMgr = clockMgr; + +} + +void IpcService::SetRunning(bool running) +{ + std::scoped_lock lock{this->threadMutex}; + if(this->running == running) + { + return; + } + + this->running = running; + + if(running) + { + Result rc = threadStart(&this->thread); + ASSERT_RESULT_OK(rc, "threadStart"); + } + else + { + svcCancelSynchronization(this->thread.handle); + threadWaitForExit(&this->thread); + } +} + +IpcService::~IpcService() +{ + this->SetRunning(false); + Result rc = threadClose(&this->thread); + ASSERT_RESULT_OK(rc, "threadClose"); + rc = ipcServerExit(&this->server); + ASSERT_RESULT_OK(rc, "ipcServerExit"); +} + +void IpcService::ProcessThreadFunc(void* arg) +{ + Result rc; + IpcService* ipcSrv = (IpcService*)arg; + while(true) + { + rc = ipcServerProcess(&ipcSrv->server, &IpcService::ServiceHandlerFunc, arg); + if(R_FAILED(rc)) + { + if(rc == KERNELRESULT(Cancelled)) + { + return; + } + if(rc != KERNELRESULT(ConnectionClosed)) + { + FileUtils::LogLine("[ipc] ipcServerProcess: [0x%x] %04d-%04d", rc, R_MODULE(rc), R_DESCRIPTION(rc)); + } + } + } +} + +Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8* out_data, size_t* out_dataSize) +{ + IpcService* ipcSrv = (IpcService*)arg; + + switch(r->data.cmdId) + { + case SysClkIpcCmd_GetApiVersion: + *out_dataSize = sizeof(u32); + return ipcSrv->GetApiVersion((u32*)out_data); + + case SysClkIpcCmd_GetVersionString: + if(r->hipc.meta.num_recv_buffers >= 1) + { + return ipcSrv->GetVersionString( + (char*)hipcGetBufferAddress(r->hipc.data.recv_buffers), + hipcGetBufferSize(r->hipc.data.recv_buffers) + ); + } + break; + + case SysClkIpcCmd_GetCurrentContext: + if(r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1) + { + size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers); + if(bufSize >= sizeof(SysClkContext)) + { + return ipcSrv->GetCurrentContext((SysClkContext*)hipcGetBufferAddress(r->hipc.data.recv_buffers)); + } + } + break; + case SysClkIpcCmd_Exit: + return ipcSrv->Exit(); + + case SysClkIpcCmd_GetProfileCount: + if(r->data.size >= sizeof(std::uint64_t)) + { + *out_dataSize = sizeof(std::uint8_t); + return ipcSrv->GetProfileCount((std::uint64_t*)r->data.ptr, (std::uint8_t*)out_data); + } + break; + + case SysClkIpcCmd_GetProfiles: + if(r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1) + { + size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers); + if(bufSize >= sizeof(SysClkTitleProfileList)) + { + return ipcSrv->GetProfiles((std::uint64_t*)r->data.ptr, (SysClkTitleProfileList*)hipcGetBufferAddress(r->hipc.data.recv_buffers)); + } + } + break; + + case SysClkIpcCmd_SetProfiles: + if(r->data.size >= sizeof(SysClkIpc_SetProfiles_Args)) + { + return ipcSrv->SetProfiles((SysClkIpc_SetProfiles_Args*)r->data.ptr); + } + break; + + case SysClkIpcCmd_SetEnabled: + if(r->data.size >= sizeof(std::uint8_t)) + { + return ipcSrv->SetEnabled((std::uint8_t*)r->data.ptr); + } + break; + + case SysClkIpcCmd_SetOverride: + if(r->data.size >= sizeof(SysClkIpc_SetOverride_Args)) + { + return ipcSrv->SetOverride((SysClkIpc_SetOverride_Args*)r->data.ptr); + } + break; + + case SysClkIpcCmd_GetConfigValues: + if(r->hipc.meta.num_recv_buffers >= 1) + { + size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers); + if(bufSize >= sizeof(SysClkConfigValueList)) + { + return ipcSrv->GetConfigValues((SysClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.recv_buffers)); + } + } + break; + + case SysClkIpcCmd_SetConfigValues: + if(r->hipc.meta.num_send_buffers >= 1) + { + size_t bufSize = hipcGetBufferSize(r->hipc.data.send_buffers); + if(bufSize >= sizeof(SysClkConfigValueList)) + { + return ipcSrv->SetConfigValues((SysClkConfigValueList*)hipcGetBufferAddress(r->hipc.data.send_buffers)); + } + } + break; + case SysClkIpcCmd_GetFreqList: + if(r->data.size >= sizeof(SysClkIpc_GetFreqList_Args) && r->hipc.meta.num_recv_buffers >= 1) + { + *out_dataSize = sizeof(std::uint32_t); + return ipcSrv->GetFreqList( + (SysClkIpc_GetFreqList_Args*)r->data.ptr, + (std::uint32_t*)hipcGetBufferAddress(r->hipc.data.recv_buffers), + hipcGetBufferSize(r->hipc.data.recv_buffers), + (std::uint32_t*)out_data + ); + } + break; + case HocClkIpcCmd_SetKipData: + if (r->data.size >= 0) { + return ipcSrv->SetKipData(); + } + break; + } + + return SYSCLK_ERROR(Generic); +} + +Result IpcService::GetApiVersion(u32* out_version) +{ + *out_version = SYSCLK_IPC_API_VERSION; + + return 0; +} + +Result IpcService::GetVersionString(char* out_buf, size_t bufSize) +{ + if(bufSize) + { + strncpy(out_buf, TARGET_VERSION, bufSize-1); + } + + return 0; +} + +Result IpcService::GetCurrentContext(SysClkContext* out_ctx) +{ + *out_ctx = this->clockMgr->GetCurrentContext(); + + return 0; +} + +Result IpcService::Exit() +{ + this->clockMgr->SetRunning(false); + + return 0; +} + +Result IpcService::GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count) +{ + Config* config = this->clockMgr->GetConfig(); + if(!config->HasProfilesLoaded()) + { + return SYSCLK_ERROR(ConfigNotLoaded); + } + + *out_count = config->GetProfileCount(*tid); + + return 0; +} + +Result IpcService::GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles) +{ + Config* config = this->clockMgr->GetConfig(); + if(!config->HasProfilesLoaded()) + { + return SYSCLK_ERROR(ConfigNotLoaded); + } + + config->GetProfiles(*tid, out_profiles); + + return 0; +} + +Result IpcService::SetProfiles(SysClkIpc_SetProfiles_Args* args) +{ + Config* config = this->clockMgr->GetConfig(); + if(!config->HasProfilesLoaded()) + { + return SYSCLK_ERROR(ConfigNotLoaded); + } + + SysClkTitleProfileList profiles = args->profiles; + + if(!config->SetProfiles(args->tid, &profiles, true)) + { + return SYSCLK_ERROR(ConfigSaveFailed); + } + + return 0; +} + +Result IpcService::SetEnabled(std::uint8_t* enabled) +{ + Config* config = this->clockMgr->GetConfig(); + config->SetEnabled(*enabled); + + return 0; +} + +Result IpcService::SetOverride(SysClkIpc_SetOverride_Args* args) +{ + SysClkModule module = args->module; + std::uint32_t hz = args->hz; + + if(!SYSCLK_ENUM_VALID(SysClkModule, args->module)) + { + return SYSCLK_ERROR(Generic); + } + + Config* config = this->clockMgr->GetConfig(); + config->SetOverrideHz(module, hz); + + return 0; +} + +Result IpcService::GetConfigValues(SysClkConfigValueList* out_configValues) +{ + Config* config = this->clockMgr->GetConfig(); + if(!config->HasProfilesLoaded()) + { + return SYSCLK_ERROR(ConfigNotLoaded); + } + + config->GetConfigValues(out_configValues); + + return 0; +} + +Result IpcService::SetConfigValues(SysClkConfigValueList* configValues) +{ + Config* config = this->clockMgr->GetConfig(); + if(!config->HasProfilesLoaded()) + { + return SYSCLK_ERROR(ConfigNotLoaded); + } + + SysClkConfigValueList configValuesCopy = *configValues; + + if(!config->SetConfigValues(&configValuesCopy, true)) + { + return SYSCLK_ERROR(ConfigSaveFailed); + } + + return 0; +} + +Result IpcService::GetFreqList(SysClkIpc_GetFreqList_Args* args, std::uint32_t* out_list, std::size_t size, std::uint32_t* out_count) +{ + if(!SYSCLK_ENUM_VALID(SysClkModule, args->module)) + { + return SYSCLK_ERROR(Generic); + } + + if(args->maxCount != size/sizeof(*out_list)) + { + return SYSCLK_ERROR(Generic); + } + + this->clockMgr->GetFreqList(args->module, out_list, args->maxCount, out_count); + + return 0; +} + +Result IpcService::SetKipData() { + this->clockMgr->SetKipData(); + + return 0; +} + +Result IpcService::GetKipData() { + this->clockMgr->GetKipData(); + + return 0; +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/src/ipc_service.h b/Source/rewrite-hoc-clk/sysmodule/src/ipc_service.h new file mode 100644 index 00000000..cded785c --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/ipc_service.h @@ -0,0 +1,66 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once + +#include +#include +#include +#include "clock_manager.h" + +class IpcService +{ + public: + + IpcService(ClockManager* clockMgr); + virtual ~IpcService(); + void SetRunning(bool running); + static void ProcessThreadFunc(void* arg); + static Result ServiceHandlerFunc(void* arg, const IpcServerRequest* r, std::uint8_t* out_data, size_t* out_dataSize); + + Result GetApiVersion(u32* out_version); + Result GetVersionString(char* out_buf, size_t bufSize); + Result GetCurrentContext(SysClkContext* out_ctx); + Result Exit(); + Result GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count); + Result GetProfiles(std::uint64_t* tid, SysClkTitleProfileList* out_profiles); + Result SetProfiles(SysClkIpc_SetProfiles_Args* args); + Result SetEnabled(std::uint8_t* enabled); + Result SetOverride(SysClkIpc_SetOverride_Args* args); + Result GetConfigValues(SysClkConfigValueList* out_configValues); + Result SetConfigValues(SysClkConfigValueList* configValues); + Result GetFreqList(SysClkIpc_GetFreqList_Args* args, std::uint32_t* out_list, std::size_t size, std::uint32_t* out_count); + Result SetKipData(); + Result GetKipData(); + bool running; + Thread thread; + LockableMutex threadMutex; + IpcServer server; + ClockManager* clockMgr; + protected: + +}; diff --git a/Source/rewrite-hoc-clk/sysmodule/src/kip.h b/Source/rewrite-hoc-clk/sysmodule/src/kip.h new file mode 100644 index 00000000..818b4937 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/kip.h @@ -0,0 +1,450 @@ +/* + * 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 . + * + */ + + +#pragma once +#include +#include +#include +#include +#include + +#pragma pack(push, 1) + +typedef struct { + u8 cust[4]; + u32 custRev; + u32 placeholder; + u32 hpMode; + u32 commonEmcMemVolt; + u32 eristaEmcMaxClock; + u32 eristaEmcMaxClock1; + u32 eristaEmcMaxClock2; + u32 marikoEmcMaxClock; + u32 marikoEmcVddqVolt; + u32 emcDvbShift; + // advanced config + u32 t1_tRCD; + u32 t2_tRP; + u32 t3_tRAS; + u32 t4_tRRD; + u32 t5_tRFC; + u32 t6_tRTW; + u32 t7_tWTR; + u32 t8_tREFI; + + u32 mem_burst_read_latency; + u32 mem_burst_write_latency; + + u32 eristaCpuUV; + u32 eristaCpuVmin; + u32 eristaCpuMaxVolt; + u32 eristaCpuUnlock; + + u32 marikoCpuUVLow; + u32 marikoCpuUVHigh; + u32 tableConf; + u32 marikoCpuLowVmin; + u32 marikoCpuHighVmin; + u32 marikoCpuMaxVolt; + u32 marikoCpuMaxClock; + + u32 eristaCpuBoostClock; + u32 marikoCpuBoostClock; + + u32 eristaGpuUV; + u32 eristaGpuVmin; + + u32 marikoGpuUV; + u32 marikoGpuVmin; + u32 marikoGpuVmax; + + u32 commonGpuVoltOffset; + + u32 gpuSpeedo; + + u32 eristaGpuVoltArray[27]; + u32 marikoGpuVoltArray[24]; + + u32 t6_tRTW_fine_tune; + u32 t7_tWTR_fine_tune; + + u32 reserved[60]; +} CustomizeTable; + +#pragma pack(pop) + +#define CUST_MAGIC "CUST" +#define CUST_MAGIC_LEN 4 + +typedef struct { + FILE* file; + long offset; + CustomizeTable cached_table; + bool has_cache; +} CustHandle; + +static inline bool cust_find_offset(FILE* f, long* out_offset) { + u8 buf[512]; + long pos = 0; + fseek(f, 0, SEEK_SET); + + while (1) { + size_t r = fread(buf, 1, sizeof(buf), f); + if (r < CUST_MAGIC_LEN) break; + + for (size_t i = 0; i <= r - CUST_MAGIC_LEN; i++) { + if (memcmp(&buf[i], CUST_MAGIC, CUST_MAGIC_LEN) == 0) { + *out_offset = pos + (long)i; + return true; + } + } + pos += (long)(r - (CUST_MAGIC_LEN - 1)); + fseek(f, pos, SEEK_SET); + } + return false; +} + +static inline bool cust_read_table(const char* path, CustomizeTable* out) { + FILE* f = fopen(path, "rb"); + if (!f) return false; + + long off; + if (!cust_find_offset(f, &off)) { + fclose(f); + return false; + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + + if (off + (long)sizeof(CustomizeTable) > size) { + fclose(f); + return false; + } + + fseek(f, off, SEEK_SET); + bool ok = fread(out, 1, sizeof(CustomizeTable), f) == sizeof(CustomizeTable); + fclose(f); + + return ok && memcmp(out->cust, CUST_MAGIC, CUST_MAGIC_LEN) == 0; +} + +static inline bool cust_write_table(const char* path, const CustomizeTable* in) { + FILE* f = fopen(path, "r+b"); + if (!f) return false; + + long off; + if (!cust_find_offset(f, &off)) { + fclose(f); + return false; + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + + if (off + (long)sizeof(CustomizeTable) > size) { + fclose(f); + return false; + } + + fseek(f, off, SEEK_SET); + bool ok = fwrite(in, 1, sizeof(CustomizeTable), f) == sizeof(CustomizeTable); + fflush(f); + fclose(f); + + return ok; +} + + +static inline bool cust_read_and_cache(const char* path, CustomizeTable* out) { + return cust_read_table(path, out); +} + +#define CUST_WRITE_FIELD_BATCH(table, field, val) \ + do { \ + (table)->field = (val); \ + } while (0) + +#define CUST_WRITE_FIELD(path, field, val) \ + do { \ + CustomizeTable t; \ + if (!cust_read_table(path, &t)) return false; \ + t.field = (val); \ + return cust_write_table(path, &t); \ + } while (0) + +static inline bool cust_set_cust_rev(const char* p, u32 v) { CUST_WRITE_FIELD(p, custRev, v); } +// static inline bool cust_set_mtc_conf(const char* p, u32 v) { CUST_WRITE_FIELD(p, mtcConf, v); } +static inline bool cust_set_hp_mode(const char* p, u32 v) { CUST_WRITE_FIELD(p, hpMode, v); } + +static inline bool cust_set_common_emc_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, commonEmcMemVolt, v); } +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_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); } + +static inline bool cust_set_tRCD(const char* p, u32 v) { CUST_WRITE_FIELD(p, t1_tRCD, v); } +static inline bool cust_set_tRP(const char* p, u32 v) { CUST_WRITE_FIELD(p, t2_tRP, v); } +static inline bool cust_set_tRAS(const char* p, u32 v) { CUST_WRITE_FIELD(p, t3_tRAS, v); } +static inline bool cust_set_tRRD(const char* p, u32 v) { CUST_WRITE_FIELD(p, t4_tRRD, v); } +static inline bool cust_set_tRFC(const char* p, u32 v) { CUST_WRITE_FIELD(p, t5_tRFC, v); } +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_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_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); } + +static inline bool cust_set_erista_cpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuUV, v); } +static inline bool cust_set_eristaCpuVmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuVmin, v); } +static inline bool cust_set_erista_cpu_max_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuMaxVolt, v); } +static inline bool cust_set_eristaCpuUnlock(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuUnlock, v); } + +static inline bool cust_set_mariko_cpu_uv_low(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuUVLow, v); } +static inline bool cust_set_mariko_cpu_uv_high(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuUVHigh, v); } +static inline bool cust_set_mariko_cpu_low_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuLowVmin, v); } +static inline bool cust_set_mariko_cpu_high_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuHighVmin, v); } +static inline bool cust_set_mariko_cpu_max_volt(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuMaxVolt, v); } +static inline bool cust_set_erista_cpu_boost(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaCpuBoostClock, v); } +static inline bool cust_set_mariko_cpu_boost(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuBoostClock, v); } + +static inline bool cust_set_erista_gpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaGpuUV, v); } +static inline bool cust_set_erista_gpu_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, eristaGpuVmin, v); } +static inline bool cust_set_mariko_gpu_uv(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuUV, v); } +static inline bool cust_set_mariko_gpu_vmin(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuVmin, v); } +static inline bool cust_set_mariko_gpu_vmax(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoGpuVmax, v); } +static inline bool cust_set_common_gpu_offset(const char* p, u32 v) { CUST_WRITE_FIELD(p, commonGpuVoltOffset, v); } +static inline bool cust_set_gpu_speedo(const char* p, u32 v) { CUST_WRITE_FIELD(p, gpuSpeedo, v); } +static inline bool cust_set_marikoCpuMaxClock(const char* p, u32 v) { CUST_WRITE_FIELD(p, marikoCpuMaxClock, v); } + +/* GPU VOLT ARRAY HELPERS */ +static inline bool cust_set_erista_gpu_volt(const char* p, int idx, u32 v) { + if (idx < 0 || idx >= 27) return false; + CustomizeTable t; + if (!cust_read_table(p, &t)) return false; + t.eristaGpuVoltArray[idx] = v; + return cust_write_table(p, &t); +} + +static inline bool cust_set_mariko_gpu_volt(const char* p, int idx, u32 v) { + if (idx < 0 || idx >= 24) return false; + CustomizeTable t; + if (!cust_read_table(p, &t)) return false; + t.marikoGpuVoltArray[idx] = v; + return cust_write_table(p, &t); +} + +static inline u32 cust_get_field(const CustomizeTable* t, u32 offset) { + if (!t) return 0; + return *(u32*)((u8*)t + offset); +} + +#define CUST_GET_FIELD(table, field) ((table) ? (table)->field : 0) + +static inline u32 cust_get_cust_rev(const CustomizeTable* t) { return CUST_GET_FIELD(t, custRev); } +// static inline u32 cust_get_mtc_conf(const CustomizeTable* t) { return CUST_GET_FIELD(t, mtcConf); } +static inline u32 cust_get_hp_mode(const CustomizeTable* t) { return CUST_GET_FIELD(t, hpMode); } + +static inline u32 cust_get_common_emc_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, commonEmcMemVolt); } +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_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); } + +static inline u32 cust_get_tRCD(const CustomizeTable* t) { return CUST_GET_FIELD(t, t1_tRCD); } +static inline u32 cust_get_tRP(const CustomizeTable* t) { return CUST_GET_FIELD(t, t2_tRP); } +static inline u32 cust_get_tRAS(const CustomizeTable* t) { return CUST_GET_FIELD(t, t3_tRAS); } +static inline u32 cust_get_tRRD(const CustomizeTable* t) { return CUST_GET_FIELD(t, t4_tRRD); } +static inline u32 cust_get_tRFC(const CustomizeTable* t) { return CUST_GET_FIELD(t, t5_tRFC); } +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_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_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); } + +static inline u32 cust_get_erista_cpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuUV); } +static inline u32 cust_get_eristaCpuVmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuVmin); } +static inline u32 cust_get_erista_cpu_max_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuMaxVolt); } +static inline u32 cust_get_eristaCpuUnlock(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuUnlock); } + +static inline u32 cust_get_mariko_cpu_uv_low(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuUVLow); } +static inline u32 cust_get_mariko_cpu_uv_high(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuUVHigh); } +static inline u32 cust_get_mariko_cpu_low_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuLowVmin); } +static inline u32 cust_get_mariko_cpu_high_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuHighVmin); } +static inline u32 cust_get_mariko_cpu_max_volt(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuMaxVolt); } +static inline u32 cust_get_erista_cpu_boost(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaCpuBoostClock); } +static inline u32 cust_get_mariko_cpu_boost(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuBoostClock); } +static inline u32 cust_get_table_conf(const CustomizeTable* t) { return CUST_GET_FIELD(t, tableConf); } + +static inline u32 cust_get_erista_gpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaGpuUV); } +static inline u32 cust_get_erista_gpu_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, eristaGpuVmin); } +static inline u32 cust_get_mariko_gpu_uv(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuUV); } +static inline u32 cust_get_mariko_gpu_vmin(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuVmin); } +static inline u32 cust_get_mariko_gpu_vmax(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoGpuVmax); } +static inline u32 cust_get_common_gpu_offset(const CustomizeTable* t) { return CUST_GET_FIELD(t, commonGpuVoltOffset); } +static inline u32 cust_get_gpu_speedo(const CustomizeTable* t) { return CUST_GET_FIELD(t, gpuSpeedo); } +static inline u32 cust_get_marikoCpuMaxClock(const CustomizeTable* t) { return CUST_GET_FIELD(t, marikoCpuMaxClock); } + +static inline u32 cust_get_erista_gpu_volt(const CustomizeTable* t, int idx) { + if (!t || idx < 0 || idx >= 27) return 0; + return t->eristaGpuVoltArray[idx]; +} + +static inline u32 cust_get_mariko_gpu_volt(const CustomizeTable* t, int idx) { + if (!t || idx < 0 || idx >= 24) return 0; + return t->marikoGpuVoltArray[idx]; +} + +#define DECL_ERISTA_GPU_VOLT_HELPER(freq, idx) \ +static inline bool cust_set_erista_gpu_volt_##freq( \ + const char* p, u32 v) { \ + return cust_set_erista_gpu_volt(p, idx, v); \ +} + +#define DECL_MARIKO_GPU_VOLT_HELPER(freq, idx) \ +static inline bool cust_set_mariko_gpu_volt_##freq( \ + const char* p, u32 v) { \ + return cust_set_mariko_gpu_volt(p, idx, v); \ +} + +DECL_ERISTA_GPU_VOLT_HELPER(76800, 0) +DECL_ERISTA_GPU_VOLT_HELPER(115200, 1) +DECL_ERISTA_GPU_VOLT_HELPER(153600, 2) +DECL_ERISTA_GPU_VOLT_HELPER(192000, 3) +DECL_ERISTA_GPU_VOLT_HELPER(230400, 4) +DECL_ERISTA_GPU_VOLT_HELPER(268800, 5) +DECL_ERISTA_GPU_VOLT_HELPER(307200, 6) +DECL_ERISTA_GPU_VOLT_HELPER(345600, 7) +DECL_ERISTA_GPU_VOLT_HELPER(384000, 8) +DECL_ERISTA_GPU_VOLT_HELPER(422400, 9) +DECL_ERISTA_GPU_VOLT_HELPER(460800, 10) +DECL_ERISTA_GPU_VOLT_HELPER(499200, 11) +DECL_ERISTA_GPU_VOLT_HELPER(537600, 12) +DECL_ERISTA_GPU_VOLT_HELPER(576000, 13) +DECL_ERISTA_GPU_VOLT_HELPER(614400, 14) +DECL_ERISTA_GPU_VOLT_HELPER(652800, 15) +DECL_ERISTA_GPU_VOLT_HELPER(691200, 16) +DECL_ERISTA_GPU_VOLT_HELPER(729600, 17) +DECL_ERISTA_GPU_VOLT_HELPER(768000, 18) +DECL_ERISTA_GPU_VOLT_HELPER(806400, 19) +DECL_ERISTA_GPU_VOLT_HELPER(844800, 20) +DECL_ERISTA_GPU_VOLT_HELPER(883200, 21) +DECL_ERISTA_GPU_VOLT_HELPER(921600, 22) +DECL_ERISTA_GPU_VOLT_HELPER(960000, 23) +DECL_ERISTA_GPU_VOLT_HELPER(998400, 24) +DECL_ERISTA_GPU_VOLT_HELPER(1036800, 25) +DECL_ERISTA_GPU_VOLT_HELPER(1075200, 26) + +DECL_MARIKO_GPU_VOLT_HELPER(76800, 0) +DECL_MARIKO_GPU_VOLT_HELPER(153600, 1) +DECL_MARIKO_GPU_VOLT_HELPER(230400, 2) +DECL_MARIKO_GPU_VOLT_HELPER(307200, 3) +DECL_MARIKO_GPU_VOLT_HELPER(384000, 4) +DECL_MARIKO_GPU_VOLT_HELPER(460800, 5) +DECL_MARIKO_GPU_VOLT_HELPER(537600, 6) +DECL_MARIKO_GPU_VOLT_HELPER(614400, 7) +DECL_MARIKO_GPU_VOLT_HELPER(691200, 8) +DECL_MARIKO_GPU_VOLT_HELPER(768000, 9) +DECL_MARIKO_GPU_VOLT_HELPER(844800, 10) +DECL_MARIKO_GPU_VOLT_HELPER(921600, 11) +DECL_MARIKO_GPU_VOLT_HELPER(998400, 12) +DECL_MARIKO_GPU_VOLT_HELPER(1075200, 13) +DECL_MARIKO_GPU_VOLT_HELPER(1152000, 14) +DECL_MARIKO_GPU_VOLT_HELPER(1228800, 15) +DECL_MARIKO_GPU_VOLT_HELPER(1267200, 16) +DECL_MARIKO_GPU_VOLT_HELPER(1305600, 17) +DECL_MARIKO_GPU_VOLT_HELPER(1344000, 18) +DECL_MARIKO_GPU_VOLT_HELPER(1382400, 19) +DECL_MARIKO_GPU_VOLT_HELPER(1420800, 20) +DECL_MARIKO_GPU_VOLT_HELPER(1459200, 21) +DECL_MARIKO_GPU_VOLT_HELPER(1497600, 22) +DECL_MARIKO_GPU_VOLT_HELPER(1536000, 23) + + +#define DECL_ERISTA_GPU_VOLT_GET(freq, idx) \ +static inline u32 cust_get_erista_gpu_volt_##freq##_val(const char* p) { \ + CustomizeTable t; \ + if (!cust_read_table(p, &t)) return 0; \ + return cust_get_erista_gpu_volt(&t, idx); \ +} +#define DECL_MARIKO_GPU_VOLT_GET(freq, idx) \ +static inline u32 cust_get_mariko_gpu_volt_##freq##_val(const char* p) { \ + CustomizeTable t; \ + if (!cust_read_table(p, &t)) return 0; \ + return cust_get_mariko_gpu_volt(&t, idx); \ +} + +DECL_ERISTA_GPU_VOLT_GET(76800, 0) +DECL_ERISTA_GPU_VOLT_GET(115200, 1) +DECL_ERISTA_GPU_VOLT_GET(153600, 2) +DECL_ERISTA_GPU_VOLT_GET(192000, 3) +DECL_ERISTA_GPU_VOLT_GET(230400, 4) +DECL_ERISTA_GPU_VOLT_GET(268800, 5) +DECL_ERISTA_GPU_VOLT_GET(307200, 6) +DECL_ERISTA_GPU_VOLT_GET(345600, 7) +DECL_ERISTA_GPU_VOLT_GET(384000, 8) +DECL_ERISTA_GPU_VOLT_GET(422400, 9) +DECL_ERISTA_GPU_VOLT_GET(460800, 10) +DECL_ERISTA_GPU_VOLT_GET(499200, 11) +DECL_ERISTA_GPU_VOLT_GET(537600, 12) +DECL_ERISTA_GPU_VOLT_GET(576000, 13) +DECL_ERISTA_GPU_VOLT_GET(614400, 14) +DECL_ERISTA_GPU_VOLT_GET(652800, 15) +DECL_ERISTA_GPU_VOLT_GET(691200, 16) +DECL_ERISTA_GPU_VOLT_GET(729600, 17) +DECL_ERISTA_GPU_VOLT_GET(768000, 18) +DECL_ERISTA_GPU_VOLT_GET(806400, 19) +DECL_ERISTA_GPU_VOLT_GET(844800, 20) +DECL_ERISTA_GPU_VOLT_GET(883200, 21) +DECL_ERISTA_GPU_VOLT_GET(921600, 22) +DECL_ERISTA_GPU_VOLT_GET(960000, 23) +DECL_ERISTA_GPU_VOLT_GET(998400, 24) +DECL_ERISTA_GPU_VOLT_GET(1036800, 25) +DECL_ERISTA_GPU_VOLT_GET(1075200, 26) + +DECL_MARIKO_GPU_VOLT_GET(76800, 0) +DECL_MARIKO_GPU_VOLT_GET(153600, 1) +DECL_MARIKO_GPU_VOLT_GET(230400, 2) +DECL_MARIKO_GPU_VOLT_GET(307200, 3) +DECL_MARIKO_GPU_VOLT_GET(384000, 4) +DECL_MARIKO_GPU_VOLT_GET(460800, 5) +DECL_MARIKO_GPU_VOLT_GET(537600, 6) +DECL_MARIKO_GPU_VOLT_GET(614400, 7) +DECL_MARIKO_GPU_VOLT_GET(691200, 8) +DECL_MARIKO_GPU_VOLT_GET(768000, 9) +DECL_MARIKO_GPU_VOLT_GET(844800, 10) +DECL_MARIKO_GPU_VOLT_GET(921600, 11) +DECL_MARIKO_GPU_VOLT_GET(998400, 12) +DECL_MARIKO_GPU_VOLT_GET(1075200, 13) +DECL_MARIKO_GPU_VOLT_GET(1152000, 14) +DECL_MARIKO_GPU_VOLT_GET(1228800, 15) +DECL_MARIKO_GPU_VOLT_GET(1267200, 16) +DECL_MARIKO_GPU_VOLT_GET(1305600, 17) +DECL_MARIKO_GPU_VOLT_GET(1344000, 18) +DECL_MARIKO_GPU_VOLT_GET(1382400, 19) +DECL_MARIKO_GPU_VOLT_GET(1420800, 20) +DECL_MARIKO_GPU_VOLT_GET(1459200, 21) +DECL_MARIKO_GPU_VOLT_GET(1497600, 22) +DECL_MARIKO_GPU_VOLT_GET(1536000, 23) \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/src/main.cpp b/Source/rewrite-hoc-clk/sysmodule/src/main.cpp new file mode 100644 index 00000000..6249172c --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/main.cpp @@ -0,0 +1,168 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +#include +#include +#include + +#include + +#include "errors.h" +#include "file_utils.h" +#include "board.h" +#include "process_management.h" +#include "clock_manager.h" +#include "ipc_service.h" +#define INNER_HEAP_SIZE 0x40000 + + +extern "C" +{ + void virtmemSetup(void); + + extern std::uint32_t __start__; + + std::uint32_t __nx_applet_type = AppletType_None; + TimeServiceType __nx_time_service_type = TimeServiceType_System; + std::uint32_t __nx_fs_num_sessions = 1; + u32 __nx_nv_transfermem_size = 0x8000; + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + NvServiceType __nx_nv_service_type = NvServiceType_Factory; + + + void __libnx_initheap(void) + { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib Heap Management */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; + + virtmemSetup(); + } + + void __appInit(void) + { + if (R_FAILED(smInitialize())) + { + fatalThrow(MAKERESULT(Module_Libnx, LibnxError_InitFail_SM)); + } + + Result rc = setsysInitialize(); + if (R_SUCCEEDED(rc)) + { + SetSysFirmwareVersion fw; + rc = setsysGetFirmwareVersion(&fw); + if (R_SUCCEEDED(rc)) + hosversionSet(MAKEHOSVERSION(fw.major, fw.minor, fw.micro)); + setsysExit(); + } + + // rc = fanInitialize(); + // if (R_FAILED(rc)) + // diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + + rc = i2cInitialize(); + if (R_FAILED(rc)) + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + rc = appletInitialize(); + if (R_FAILED(rc)) + diagAbortWithResult(MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen)); + } + + void __appExit(void) + { + // CloseFanControllerThread(); + // fanExit(); + i2cExit(); + fsExit(); + fsdevUnmountAll(); + appletExit(); + } +} + +int main(int argc, char** argv) +{ + Result rc = FileUtils::Initialize(); + if (R_FAILED(rc)) + { + fatalThrow(rc); + return 1; + } + + try + { + Board::Initialize(); + ProcessManagement::Initialize(); + + ProcessManagement::WaitForQLaunch(); + + ClockManager* clockMgr = new ClockManager(); + IpcService* ipcSrv = new IpcService(clockMgr); + + FileUtils::LogLine("Ready"); + + clockMgr->SetRunning(true); + clockMgr->GetConfig()->SetEnabled(true); + ipcSrv->SetRunning(true); + // TemperaturePoint *table; + // ReadConfigFile(&table); + // InitFanController(table); + // StartFanControllerThread(); + + while (clockMgr->Running()) + { + clockMgr->Tick(); + clockMgr->WaitForNextTick(); + } + + ipcSrv->SetRunning(false); + delete ipcSrv; + delete clockMgr; + ProcessManagement::Exit(); + Board::Exit(); + } + catch (const std::exception &ex) + { + FileUtils::LogLine("[!] %s", ex.what()); + } + catch (...) + { + std::exception_ptr p = std::current_exception(); + FileUtils::LogLine("[!?] %s", p ? p.__cxa_exception_type()->name() : "..."); + } + + FileUtils::LogLine("Exit"); + svcSleepThread(1000000ULL); + FileUtils::Exit(); + + return 0; +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/old_board.cpp b/Source/rewrite-hoc-clk/sysmodule/src/old_board.cpp new file mode 100644 index 00000000..7c59fdb3 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/old_board.cpp @@ -0,0 +1,1328 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + +// Note: Hoag crashes on display refresh rate init while in sleep mode + +#include +#include "board.h" +#include "errors.h" +#include "rgltr.h" +#include "file_utils.h" +#include // for std::clamp +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX(A, B) std::max(A, B) +#define MIN(A, B) std::min(A, B) +#define CEIL(A) std::ceil(A) +#define FLOOR(A) std::floor(A) +#define ROUND(A) std::lround(A) + + +#define FUSE_CPU_SPEEDO_0_CALIB 0x114 +//#define FUSE_CPU_SPEEDO_1_CALIB 0x12C +#define FUSE_CPU_SPEEDO_2_CALIB 0x130 + +#define FUSE_SOC_SPEEDO_0_CALIB 0x134 +//#define FUSE_SOC_SPEEDO_1_CALIB 0x138 +//#define FUSE_SOC_SPEEDO_2_CALIB 0x13C + +#define FUSE_CPU_IDDQ_CALIB 0x118 +#define FUSE_SOC_IDDQ_CALIB 0x140 +#define FUSE_GPU_IDDQ_CALIB 0x228 + +#define HOSSVC_HAS_CLKRST (hosversionAtLeast(8,0,0)) +#define HOSSVC_HAS_TC (hosversionAtLeast(5,0,0)) +#define NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD 0x80044715 +#define NVSCHED_CTRL_ENABLE 0x00000601 +#define NVSCHED_CTRL_DISABLE 0x00000602 + +constexpr u64 CpuTimeOutNs = 500'000'000; +constexpr double Systemtickfrequency = 19200000.0 * (static_cast(CpuTimeOutNs) / 1'000'000'000.0); +Result nvInitialize_rc; +Result nvCheck = 1; +Result nvCheck_sched = 1; + +LEvent threadexit; +Thread gpuLThread; +Thread cpuCore0Thread; +Thread cpuCore1Thread; +Thread cpuCore2Thread; +Thread cpuCore3Thread; +Thread miscThread; +double temp = 0; + +PwmChannelSession g_ICon; +Result pwmCheck = 1; +Result pwmDutyCycleCheck = 1; +double Rotation_Duty = 0; +u8 fanLevel; + +uint32_t GPU_Load_u = 0, fd = 0, fd2 = 0; +BatteryChargeInfo info; + +static SysClkSocType g_socType = SysClkSocType_Erista; +static HorizonOCConsoleType g_consoleType = HorizonOCConsoleType_Iowa; + +u64 idletick0 = 0; +u64 idletick1 = 0; +u64 idletick2 = 0; +// u64 idletick3 = 0; + +u32 cpu0, cpu1, cpu2, cpu3, cpuAvg; +u16 cpuSpeedo0, cpuSpeedo2, socSpeedo0; // CPU, GPU, SOC +u32 speedoBracket; +u16 cpuIDDQ, gpuIDDQ, socIDDQ; +u8 g_dramID = 0; +u64 cldvfs, cldvfs_temp; +u32 cachedEristaUvLowTune0 = 0, cachedEristaUvLowTune1 = 0, cachedMarikoUvHighTune0 = 0; + +static const u32 ramBrackets[][22] = { + { 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, }, + { 2300, 2366, 2433, 2466, 2533, 2566, 2633, 2700, 2733, 2800, 2833, 2900, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3266, }, + { 2433, 2466, 2533, 2600, 2666, 2733, 2766, 2800, 2833, 2866, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3300, 3333, 3366, }, + { 2500, 2533, 2600, 2633, 2666, 2733, 2800, 2866, 2900, 2966, 3033, 3100, 3166, 3200, 3233, 3266, 3300, 3333, 3366, 3400, 3400, 3400, }, +}; + +static const u32 gpuDvfsArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800}; + +u32 dvfsTable[6][32] = {}; +u64 dvfsAddress; +u32 ramVmin; + +const char* Board::GetModuleName(SysClkModule module, bool pretty) +{ + ASSERT_ENUM_VALID(SysClkModule, module); + return sysclkFormatModule(module, pretty); +} + +const char* Board::GetProfileName(SysClkProfile profile, bool pretty) +{ + ASSERT_ENUM_VALID(SysClkProfile, profile); + return sysclkFormatProfile(profile, pretty); +} + +const char* Board::GetThermalSensorName(SysClkThermalSensor sensor, bool pretty) +{ + ASSERT_ENUM_VALID(SysClkThermalSensor, sensor); + return sysclkFormatThermalSensor(sensor, pretty); +} + +const char* Board::GetPowerSensorName(SysClkPowerSensor sensor, bool pretty) +{ + ASSERT_ENUM_VALID(SysClkPowerSensor, sensor); + return sysclkFormatPowerSensor(sensor, pretty); +} + +PcvModule Board::GetPcvModule(SysClkModule sysclkModule) +{ + switch(sysclkModule) + { + case SysClkModule_CPU: + return PcvModule_CpuBus; + case SysClkModule_GPU: + return PcvModule_GPU; + case SysClkModule_MEM: + return PcvModule_EMC; + default: + ASSERT_ENUM_VALID(SysClkModule, sysclkModule); + } + + return (PcvModule)0; +} + +PcvModuleId Board::GetPcvModuleId(SysClkModule sysclkModule) +{ + PcvModuleId pcvModuleId; + Result rc = pcvGetModuleId(&pcvModuleId, GetPcvModule(sysclkModule)); + ASSERT_RESULT_OK(rc, "pcvGetModuleId"); + + return pcvModuleId; +} + +void CheckCore(void *idletickPtr) { + u64* idletick = static_cast(idletickPtr); + while(true) { + u64 idletickA; + u64 idletickB; + svcGetInfo(&idletickB, InfoType_IdleTickCount, INVALID_HANDLE, -1); + svcWaitForAddress(&threadexit, ArbitrationType_WaitIfEqual, 0, CpuTimeOutNs); + svcGetInfo(&idletickA, InfoType_IdleTickCount, INVALID_HANDLE, -1); + *idletick = idletickA - idletickB; + } +} + +void gpuLoadThread(void*) { + #define gpu_samples_average 8 + uint32_t gpu_load_array[gpu_samples_average] = {0}; + size_t i = 0; + if (R_SUCCEEDED(nvCheck)) do { + u32 temp; + if (R_SUCCEEDED(nvIoctl(fd, NVGPU_GPU_IOCTL_PMU_GET_GPU_LOAD, &temp))) { + gpu_load_array[i++ % gpu_samples_average] = temp; + GPU_Load_u = std::accumulate(&gpu_load_array[0], &gpu_load_array[gpu_samples_average], 0) / gpu_samples_average; + } + svcSleepThread(16'666'000); // wait a bit (this is the perfect amount of time to keep the reading accurate) + } while(true); +} + +void miscThreadFunc(void*) { + for(;;) { + if (R_SUCCEEDED(pwmCheck)) { + if (R_SUCCEEDED(pwmChannelSessionGetDutyCycle(&g_ICon, &temp))) { + temp *= 10; + temp = trunc(temp); + temp /= 10; + Rotation_Duty = 100.0 - temp; + } + } + fanLevel = (u8)Rotation_Duty; + svcSleepThread(300'000'000); + } +} + +void Board::Initialize() +{ + Result rc = 0; + if(HOSSVC_HAS_CLKRST) + { + rc = clkrstInitialize(); + ASSERT_RESULT_OK(rc, "clkrstInitialize"); + } + else + { + rc = pcvInitialize(); + ASSERT_RESULT_OK(rc, "pcvInitialize"); + } + + rc = apmExtInitialize(); + ASSERT_RESULT_OK(rc, "apmExtInitialize"); + + rc = psmInitialize(); + ASSERT_RESULT_OK(rc, "psmInitialize"); + + if(HOSSVC_HAS_TC) + { + rc = tcInitialize(); + ASSERT_RESULT_OK(rc, "tcInitialize"); + } + + rc = max17050Initialize(); + ASSERT_RESULT_OK(rc, "max17050Initialize"); + + rc = tmp451Initialize(); + ASSERT_RESULT_OK(rc, "tmp451Initialize"); + nvInitialize_rc = nvInitialize(); + if (R_SUCCEEDED(nvInitialize_rc)) { + nvCheck = nvOpen(&fd, "/dev/nvhost-ctrl-gpu"); + nvCheck_sched = nvOpen(&fd2, "/dev/nvsched-ctrl"); + } + + rc = rgltrInitialize(); + ASSERT_RESULT_OK(rc, "rgltrInitialize"); + + // if (R_SUCCEEDED(fanInitialize())) { + // if (hosversionAtLeast(7,0,0)) fanCheck = fanOpenController(&fanController, 0x3D000001); + // else fanCheck = fanOpenController(&fanController, 1); + // } + + rc = pmdmntInitialize(); + ASSERT_RESULT_OK(rc, "pmdmntInitialize"); + + threadCreate(&gpuLThread, gpuLoadThread, NULL, NULL, 0x1000, 0x3F, -2); + threadStart(&gpuLThread); + leventClear(&threadexit); + threadCreate(&cpuCore0Thread, CheckCore, &idletick0, NULL, 0x1000, 0x10, 0); + threadCreate(&cpuCore1Thread, CheckCore, &idletick1, NULL, 0x1000, 0x10, 1); + threadCreate(&cpuCore2Thread, CheckCore, &idletick2, NULL, 0x1000, 0x10, 2); + // threadCreate(&cpuCore3Thread, CheckCore, &idletick3, NULL, 0x1000, 0x10, 3); + threadCreate(&miscThread, miscThreadFunc, NULL, NULL, 0x1000, 0x10, 3); + + threadStart(&cpuCore0Thread); + threadStart(&cpuCore1Thread); + threadStart(&cpuCore2Thread); + // threadStart(&cpuCore3Thread); + threadStart(&miscThread); + batteryInfoInitialize(); + FetchHardwareInfos(); + + if (hosversionAtLeast(6,0,0) && R_SUCCEEDED(pwmInitialize())) { + pwmCheck = pwmOpenSession2(&g_ICon, 0x3D000001); + } + + if(!IsHoag()) { + u64 clkVirtAddr, dsiVirtAddr, outsize; + rc = svcQueryMemoryMapping(&clkVirtAddr, &outsize, 0x60006000, 0x1000); + ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (clk)"); + rc = svcQueryMemoryMapping(&dsiVirtAddr, &outsize, 0x54300000, 0x40000); + ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (dsi)"); + + DisplayRefreshConfig cfg = {.clkVirtAddr = clkVirtAddr, .dsiVirtAddr = dsiVirtAddr}; + + DisplayRefresh_Initialize(&cfg); + } + + rc = svcQueryMemoryMapping(&cldvfs, &cldvfs_temp, CLDVFS_REGION_BASE, CLDVFS_REGION_SIZE); + ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping (cldvfs)"); + + if(Board::GetSocType() == SysClkSocType_Erista) { + cachedEristaUvLowTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0); + cachedEristaUvLowTune1 = *(u32*)(cldvfs + CL_DVFS_TUNE1_0); + } else { + Board::SetHz(SysClkModule_CPU, 1785000000); + cachedMarikoUvHighTune0 = *(u32*)(cldvfs + CL_DVFS_TUNE0_0); + Board::ResetToStockCpu(); + } + + +} + +void Board::fuseReadSpeedos() { + + u64 pid = 0; + if (R_FAILED(pmdmntGetProcessId(&pid, 0x0100000000000006))) { + return; + } + + Handle debug; + if (R_FAILED(svcDebugActiveProcess(&debug, pid))) { + return; + } + + MemoryInfo mem_info = {0}; + u32 pageinfo = 0; + u64 addr = 0; + + char stack[0x10] = {0}; + const char compare[0x10] = {0}; + char dump[0x400] = {0}; + + while (true) { + if (R_FAILED(svcQueryDebugProcessMemory(&mem_info, &pageinfo, debug, addr)) || mem_info.addr < addr) { + break; + } + + if (mem_info.type == MemType_Io && mem_info.size == 0x1000) { + if (R_FAILED(svcReadDebugProcessMemory(stack, debug, mem_info.addr, sizeof(stack)))) { + break; + } + + if (memcmp(stack, compare, sizeof(stack)) == 0) { + if (R_FAILED(svcReadDebugProcessMemory(dump, debug, mem_info.addr + 0x800, sizeof(dump)))) { + break; + } + + cpuSpeedo0 = *reinterpret_cast(dump + FUSE_CPU_SPEEDO_0_CALIB); + cpuSpeedo2 = *reinterpret_cast(dump + FUSE_CPU_SPEEDO_2_CALIB); + socSpeedo0 = *reinterpret_cast(dump + FUSE_SOC_SPEEDO_0_CALIB); + cpuIDDQ = *reinterpret_cast(dump + FUSE_CPU_IDDQ_CALIB); + gpuIDDQ = *reinterpret_cast(dump + FUSE_SOC_IDDQ_CALIB); + socIDDQ = *reinterpret_cast(dump + FUSE_GPU_IDDQ_CALIB); + + svcCloseHandle(debug); + return; + } + } + + addr = mem_info.addr + mem_info.size; + } + + svcCloseHandle(debug); +} + +u16 Board::getSpeedo(HorizonOCSpeedo speedoType) { + switch(speedoType) { + case HorizonOCSpeedo_CPU: + return cpuSpeedo0; + case HorizonOCSpeedo_GPU: + return cpuSpeedo2; + case HorizonOCSpeedo_SOC: + return socSpeedo0; + default: + ASSERT_ENUM_VALID(HorizonOCSpeedo, speedoType); + return 0; + } +} + +u16 Board::getIDDQ(HorizonOCSpeedo speedoType) { + switch(speedoType) { + case HorizonOCSpeedo_CPU: + return cpuIDDQ; + case HorizonOCSpeedo_GPU: + return gpuIDDQ; + case HorizonOCSpeedo_SOC: + return socIDDQ; + default: + ASSERT_ENUM_VALID(HorizonOCSpeedo, speedoType); + return 0; + } +} + + +void Board::Exit() +{ + if(HOSSVC_HAS_CLKRST) + { + clkrstExit(); + } + else + { + pcvExit(); + } + + apmExtExit(); + psmExit(); + + if(HOSSVC_HAS_TC) + { + tcExit(); + } + + max17050Exit(); + tmp451Exit(); + + threadClose(&gpuLThread); + threadClose(&cpuCore0Thread); + threadClose(&cpuCore1Thread); + threadClose(&cpuCore2Thread); + // threadClose(&cpuCore3Thread); + threadClose(&miscThread); + + pwmChannelSessionClose(&g_ICon); + pwmExit(); + rgltrExit(); + batteryInfoExit(); + pmdmntExit(); + nvExit(); + if(!IsHoag()) + DisplayRefresh_Shutdown(); +} + +SysClkProfile Board::GetProfile() +{ + std::uint32_t mode = 0; + Result rc = apmExtGetPerformanceMode(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); + + if(mode) + { + return SysClkProfile_Docked; + } + + PsmChargerType chargerType; + + rc = psmGetChargerType(&chargerType); + ASSERT_RESULT_OK(rc, "psmGetChargerType"); + + if(chargerType == PsmChargerType_EnoughPower) + { + return SysClkProfile_HandheldChargingOfficial; + } + else if(chargerType == PsmChargerType_LowPower) + { + return SysClkProfile_HandheldChargingUSB; + } + + return SysClkProfile_Handheld; +} + +void Board::SetHz(SysClkModule module, std::uint32_t hz) +{ + Result rc = 0; + if(module == HorizonOCModule_Display) { + if(!IsHoag()) + DisplayRefresh_SetRate(hz); + return; + } + if(module > SysClkModule_MEM) + return; + if(HOSSVC_HAS_CLKRST) + { + ClkrstSession session = {0}; + + rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3); + ASSERT_RESULT_OK(rc, "clkrstOpenSession"); + rc = clkrstSetClockRate(&session, hz); + ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); + if (module == SysClkModule_CPU) { + svcSleepThread(200'000); + rc = clkrstSetClockRate(&session, hz); + ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); + } + clkrstCloseSession(&session); + } + else + { + rc = pcvSetClockRate(Board::GetPcvModule(module), hz); + ASSERT_RESULT_OK(rc, "pcvSetClockRate"); + if (module == SysClkModule_CPU) { + svcSleepThread(200'000); + rc = pcvSetClockRate(Board::GetPcvModule(module), hz); + ASSERT_RESULT_OK(rc, "pcvSetClockRate"); + } + } +} + +std::uint32_t Board::GetHz(SysClkModule module) +{ + Result rc = 0; + std::uint32_t hz = 0; + + if(module == HorizonOCModule_Display) { + if(!IsHoag()) + DisplayRefresh_GetRate(&hz, false); + else + hz = 60; + return hz; + } + + if(HOSSVC_HAS_CLKRST) + { + ClkrstSession session = {0}; + + rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3); + ASSERT_RESULT_OK(rc, "clkrstOpenSession"); + + rc = clkrstGetClockRate(&session, &hz); + ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); + + clkrstCloseSession(&session); + } + else + { + rc = pcvGetClockRate(Board::GetPcvModule(module), &hz); + ASSERT_RESULT_OK(rc, "pcvGetClockRate"); + } + + return hz; +} + +std::uint32_t Board::GetRealHz(SysClkModule module) +{ + u32 hz = 0; + switch(module) + { + case SysClkModule_CPU: + return t210ClkCpuFreq(); + case SysClkModule_GPU: + return t210ClkGpuFreq(); + case SysClkModule_MEM: + return t210ClkMemFreq(); + case HorizonOCModule_Display: + if(!IsHoag()) + DisplayRefresh_GetRate(&hz, false); + else + hz = 60; + return hz; + default: + ASSERT_ENUM_VALID(SysClkModule, module); + } + + return 0; +} + +void Board::GetFreqList(SysClkModule module, std::uint32_t* outList, std::uint32_t maxCount, std::uint32_t* outCount) +{ + Result rc = 0; + PcvClockRatesListType type; + s32 tmpInMaxCount = maxCount; + s32 tmpOutCount = 0; + + + + if(HOSSVC_HAS_CLKRST) + { + ClkrstSession session = {0}; + + rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3); + ASSERT_RESULT_OK(rc, "clkrstOpenSession"); + + rc = clkrstGetPossibleClockRates(&session, outList, tmpInMaxCount, &type, &tmpOutCount); + ASSERT_RESULT_OK(rc, "clkrstGetPossibleClockRates"); + + clkrstCloseSession(&session); + } + else + { + rc = pcvGetPossibleClockRates(Board::GetPcvModule(module), outList, tmpInMaxCount, &type, &tmpOutCount); + ASSERT_RESULT_OK(rc, "pcvGetPossibleClockRates"); + } + + if(type != PcvClockRatesListType_Discrete) + { + ERROR_THROW("Unexpected PcvClockRatesListType: %u (module = %s)", type, Board::GetModuleName(module, false)); + } + + *outCount = tmpOutCount; +} + +void Board::ResetToStock() +{ + Result rc = 0; + if(hosversionAtLeast(9,0,0)) + { + std::uint32_t confId = 0; + rc = apmExtGetCurrentPerformanceConfiguration(&confId); + ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); + + SysClkApmConfiguration* apmConfiguration = NULL; + for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) + { + if(sysclk_g_apm_configurations[i].id == confId) + { + apmConfiguration = &sysclk_g_apm_configurations[i]; + break; + } + } + + if(!apmConfiguration) + { + ERROR_THROW("Unknown apm configuration: %x", confId); + } + + Board::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz); + Board::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz); + Board::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz); + } + else + { + std::uint32_t mode = 0; + rc = apmExtGetPerformanceMode(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); + + rc = apmExtSysRequestPerformanceMode(mode); + ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); + } +} + +void Board::ResetToStockCpu() +{ + Result rc = 0; + if(hosversionAtLeast(9,0,0)) + { + std::uint32_t confId = 0; + rc = apmExtGetCurrentPerformanceConfiguration(&confId); + ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); + + SysClkApmConfiguration* apmConfiguration = NULL; + for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) + { + if(sysclk_g_apm_configurations[i].id == confId) + { + apmConfiguration = &sysclk_g_apm_configurations[i]; + break; + } + } + + if(!apmConfiguration) + { + ERROR_THROW("Unknown apm configuration: %x", confId); + } + + Board::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz); + } + else + { + std::uint32_t mode = 0; + rc = apmExtGetPerformanceMode(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); + + rc = apmExtSysRequestPerformanceMode(mode); + ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); + } +} + +void Board::ResetToStockMem() +{ + Result rc = 0; + if(hosversionAtLeast(9,0,0)) + { + std::uint32_t confId = 0; + rc = apmExtGetCurrentPerformanceConfiguration(&confId); + ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); + + SysClkApmConfiguration* apmConfiguration = NULL; + for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) + { + if(sysclk_g_apm_configurations[i].id == confId) + { + apmConfiguration = &sysclk_g_apm_configurations[i]; + break; + } + } + + if(!apmConfiguration) + { + ERROR_THROW("Unknown apm configuration: %x", confId); + } + + Board::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz); + } + else + { + std::uint32_t mode = 0; + rc = apmExtGetPerformanceMode(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); + + rc = apmExtSysRequestPerformanceMode(mode); + ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); + } +} + +void Board::ResetToStockGpu() +{ + Result rc = 0; + if(hosversionAtLeast(9,0,0)) + { + std::uint32_t confId = 0; + rc = apmExtGetCurrentPerformanceConfiguration(&confId); + ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); + + SysClkApmConfiguration* apmConfiguration = NULL; + for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) + { + if(sysclk_g_apm_configurations[i].id == confId) + { + apmConfiguration = &sysclk_g_apm_configurations[i]; + break; + } + } + + if(!apmConfiguration) + { + ERROR_THROW("Unknown apm configuration: %x", confId); + } + + Board::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz); + } + else + { + std::uint32_t mode = 0; + rc = apmExtGetPerformanceMode(&mode); + ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); + + rc = apmExtSysRequestPerformanceMode(mode); + ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); + } +} + +void Board::ResetToStockDisplay() { + if(!IsHoag()) + DisplayRefresh_SetRate(60); +} + +u8 Board::GetHighestDockedDisplayRate() { + if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag) { + return DisplayRefresh_GetDockedHighestAllowed(); + } else + return 60; +} + +std::uint32_t Board::GetTemperatureMilli(SysClkThermalSensor sensor) +{ + std::int32_t millis = 0; + + if(sensor == SysClkThermalSensor_SOC) + { + millis = tmp451TempSoc(); + } + else if(sensor == SysClkThermalSensor_PCB) + { + millis = tmp451TempPcb(); + } + else if(sensor == SysClkThermalSensor_Skin) + { + if(HOSSVC_HAS_TC) + { + Result rc; + rc = tcGetSkinTemperatureMilliC(&millis); + ASSERT_RESULT_OK(rc, "tcGetSkinTemperatureMilliC"); + } + } + else if (sensor == HorizonOCThermalSensor_Battery) { + batteryInfoGetChargeInfo(&info); + millis = batteryInfoGetTemperatureMiliCelsius(&info); + } + else if (sensor == HorizonOCThermalSensor_PMIC) { + millis = 50000; + } + else + { + ASSERT_ENUM_VALID(SysClkThermalSensor, sensor); + } + + return std::max(0, millis); +} + +std::int32_t Board::GetPowerMw(SysClkPowerSensor sensor) +{ + switch(sensor) + { + case SysClkPowerSensor_Now: + return max17050PowerNow(); + case SysClkPowerSensor_Avg: + return max17050PowerAvg(); + default: + ASSERT_ENUM_VALID(SysClkPowerSensor, sensor); + } + + return 0; +} + +u32 GetMaxCpuLoad() { + float cpuUsage0 = std::clamp(((Systemtickfrequency - idletick0) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); + float cpuUsage1 = std::clamp(((Systemtickfrequency - idletick1) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); + float cpuUsage2 = std::clamp(((Systemtickfrequency - idletick2) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); + // float cpuUsage3 = std::clamp(((Systemtickfrequency - idletick3) / static_cast(Systemtickfrequency)) * 1000.0, 0.0, 1000.0); + + return std::round(std::max({cpuUsage0, cpuUsage1, cpuUsage2})); +} + +std::uint32_t Board::GetPartLoad(SysClkPartLoad loadSource) +{ + switch(loadSource) + { + case SysClkPartLoad_EMC: + return t210EmcLoadAll(); + case SysClkPartLoad_EMCCpu: + return t210EmcLoadCpu(); + case HocClkPartLoad_GPU: + return GPU_Load_u; + case HocClkPartLoad_CPUMax: + return GetMaxCpuLoad(); + case HocClkPartLoad_BAT: + batteryInfoGetChargeInfo(&info); + return info.RawBatteryCharge; + case HocClkPartLoad_FAN: + return fanLevel; + default: + ASSERT_ENUM_VALID(SysClkPartLoad, loadSource); + } + + return 0; +} + + +SysClkSocType Board::GetSocType() { + return g_socType; +} + +HorizonOCConsoleType Board::GetConsoleType() { + return g_consoleType; +} + +u8 Board::GetDramID() { + return g_dramID; +} + +void Board::FetchHardwareInfos() +{ + fuseReadSpeedos(); + SetSpeedoBracket(); + u64 sku = 0, dramID = 0; + Result rc = splInitialize(); + ASSERT_RESULT_OK(rc, "splInitialize"); + + rc = splGetConfig(SplConfigItem_HardwareType, &sku); + ASSERT_RESULT_OK(rc, "splGetConfig"); + + rc = splGetConfig(SplConfigItem_DramId, &dramID); + ASSERT_RESULT_OK(rc, "splGetConfig"); + + splExit(); + + switch(sku) + { + case 2: + case 3: + case 4: + case 5: + g_socType = SysClkSocType_Mariko; + break; + default: + g_socType = SysClkSocType_Erista; + } + + if (g_socType == SysClkSocType_Mariko) { + CacheDvfsTable(); + } + + g_consoleType = (HorizonOCConsoleType)sku; + g_dramID = (u8)dramID; +} + +/* +* Switch Power domains (max77620): +* Name | Usage | uV step | uV min | uV default | uV max | Init +*-------+---------------+---------+--------+------------+---------+------------------ +* sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) +* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) +* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) +* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | +* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) +* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | +* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv) +* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | 0.85V (AO, pcv) +* ldo5 | GC Card | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv) +* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V (pcv) +* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) +* ldo8 | XUSB, DP, MCU | 50000 | 800000 | 1050000 | 2800000 | 1.05V/2.8V (pcv) + +typedef enum { + PcvPowerDomainId_Max77620_Sd0 = 0x3A000080, + PcvPowerDomainId_Max77620_Sd1 = 0x3A000081, // vdd2 + PcvPowerDomainId_Max77620_Sd2 = 0x3A000082, + PcvPowerDomainId_Max77620_Sd3 = 0x3A000083, + PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0, + PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1, + PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2, + PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3, + PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4, + PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5, + PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6, + PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7, + PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8, + PcvPowerDomainId_Max77621_Cpu = 0x3A000003, + PcvPowerDomainId_Max77621_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Cpu = 0x3A000003, + PcvPowerDomainId_Max77812_Gpu = 0x3A000004, + PcvPowerDomainId_Max77812_Dram = 0x3A000005, // vddq +} PowerDomainId; + +*/ + +std::uint32_t Board::GetVoltage(HocClkVoltage voltage) +{ + RgltrSession session; + Result rc = 0; + u32 out = 0; + switch(voltage) + { + case HocClkVoltage_SOC: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Sd0); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_EMCVDD2: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Sd1); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_CPU: + if(Board::GetSocType() == SysClkSocType_Mariko) + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77621_Cpu); + else + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Cpu); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_GPU: + if(Board::GetSocType() == SysClkSocType_Mariko) + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77621_Gpu); + else + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Gpu); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_EMCVDDQ_MarikoOnly: + if(Board::GetSocType() == SysClkSocType_Mariko) { + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77812_Dram); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + } else { + out = Board::GetVoltage(HocClkVoltage_EMCVDD2); + } + break; + case HocClkVoltage_Display: + rc = rgltrOpenSession(&session, PcvPowerDomainId_Max77620_Ldo0); + ASSERT_RESULT_OK(rc, "rgltrOpenSession") + rgltrGetVoltage(&session, &out); + rgltrCloseSession(&session); + break; + case HocClkVoltage_Battery: + batteryInfoGetChargeInfo(&info); + out = info.VoltageAvg; + break; + default: + ASSERT_ENUM_VALID(HocClkVoltage, voltage); + } + + return out > 0 ? out : 0; +} + +void Board::SetSpeedoBracket() { + if (cpuSpeedo2 >= 1754) { + speedoBracket = 3; + } else if (cpuSpeedo2 >= 1690) { + speedoBracket = 2; + } else if (cpuSpeedo2 > 1625) { + speedoBracket = 1; + } else { + speedoBracket = 0; + } +} + +u32 Board::GetMinimumGpuVoltage(u32 freqMhz) { + if (freqMhz <= 1600) + return 0; + + for (u32 voltageIndex = 0; voltageIndex < 22; ++voltageIndex) { + if (freqMhz <= ramBrackets[speedoBracket][voltageIndex]) { + return gpuDvfsArray[voltageIndex]; + } + } + + return 800; +} + +Handle Board::GetPcvHandle() { + constexpr u64 PcvID = 0x10000000000001a; + u64 processIDList[80]{}; + s32 processCount = 0; + Handle handle = INVALID_HANDLE; + + DebugEventInfo debugEvent{}; + + /* Get all running processes. */ + Result resultGetProcessList = svcGetProcessList(&processCount, processIDList, std::size(processIDList)); + if (R_FAILED(resultGetProcessList)) { + return INVALID_HANDLE; + } + + /* Try to find pcv. */ + for (int i = 0; i < processCount; ++i) { + if (handle != INVALID_HANDLE) { + svcCloseHandle(handle); + handle = INVALID_HANDLE; + } + + /* Try to debug process, if it fails, try next process. */ + Result resultSvcDebugProcess = svcDebugActiveProcess(&handle, processIDList[i]); + if (R_FAILED(resultSvcDebugProcess)) { + continue; + } + + /* Try to get a debug event. */ + Result resultDebugEvent = svcGetDebugEvent(&debugEvent, handle); + if (R_SUCCEEDED(resultDebugEvent)) { + if (debugEvent.info.create_process.program_id == PcvID) { + return handle; + } + } + } + + /* Failed to get handle. */ + return INVALID_HANDLE; +} + +void Board::CacheDvfsTable() { + const u32 voltagePattern[] = { 600000, 12500, 1400000, }; + + Handle handle = GetPcvHandle(); + if (handle == INVALID_HANDLE) { + FileUtils::LogLine("[Board] Invalid handle!"); + return; + } + + MemoryInfo memoryInfo = {}; + u64 address = 0; + u32 pageInfo = 0; + constexpr u32 PageSize = 0x1000; + u8 buffer[PageSize]; + + /* Loop until failure. */ + while (true) { + /* Find pcv heap. */ + while (true) { + Result resultProcessMemory = svcQueryDebugProcessMemory(&memoryInfo, &pageInfo, handle, address); + address = memoryInfo.addr + memoryInfo.size; + + if (R_FAILED(resultProcessMemory) || !address) { + svcCloseHandle(handle); + FileUtils::LogLine("[Board] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory)); + handle = INVALID_HANDLE; + return; + } + + if (memoryInfo.size && (memoryInfo.perm & 3) == 3 && static_cast(memoryInfo.type) == 0x04) { + /* Found valid memory. */ + break; + } + } + + for (u64 base = 0; base < memoryInfo.size; base += PageSize) { + u32 memorySize = std::min(memoryInfo.size, static_cast(PageSize)); + if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, base + memoryInfo.addr, memorySize))) { + break; + } + + u8 *resultPattern = static_cast(memmem_impl(buffer, sizeof(buffer), voltagePattern, sizeof(voltagePattern))); + u32 index = resultPattern - buffer; + + if (!resultPattern) { + continue; + } + + /* Assuming mariko. */ + const u32 vmax = 800; + constexpr u32 DvfsTableOffset = 312; + if (!std::memcmp(&buffer[index + DvfsTableOffset], &vmax, sizeof(vmax))) { + std::memcpy(dvfsTable, &buffer[index + DvfsTableOffset], sizeof(dvfsTable)); + dvfsAddress = base + memoryInfo.addr + DvfsTableOffset + index; + } + + svcCloseHandle(handle); + handle = INVALID_HANDLE; + return; + } + } + + svcCloseHandle(handle); + handle = INVALID_HANDLE; + return; +} + +void Board::PcvHijackDvfs(u32 vmin) { + u32 table[192]; + static_assert(sizeof(table) == sizeof(dvfsTable)); + std::memcpy(table, dvfsTable, sizeof(dvfsTable)); + + if (ramVmin == vmin) { + return; + } + + for (u32 i = 0; i < std::size(table); ++i) { + if (table[i] && table[i] <= vmin) { + table[i] = vmin; + } + } + + Handle handle = GetPcvHandle(); + if (handle == INVALID_HANDLE) { + FileUtils::LogLine("Invalid handle!"); + return; + } + + Result rc = svcWriteDebugProcessMemory(handle, table, dvfsAddress, sizeof(table)); + + if (R_SUCCEEDED(rc)) { + ramVmin = vmin; + } + + svcCloseHandle(handle); + FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin); +} + +bool Board::IsDram8GB() { + SecmonArgs args = {}; + args.X[0] = 0xF0000002; + args.X[1] = MC_REGISTER_BASE + MC_EMEM_CFG_0; + svcCallSecureMonitor(&args); + + if(args.X[1] == (MC_REGISTER_BASE + MC_EMEM_CFG_0)) { // if param 1 is identical read failed + writeNotification("Horizon OC\nSecmon read failed!\n This may be a hardware issue!"); + return false; + } else + return args.X[1] == 0x00002000 ? true : false; +} + +void Board::SetGpuSchedulingMode(GpuSchedulingMode mode, GpuSchedulingOverrideMethod method) { + if (nvCheck_sched == 1 && method == GpuSchedulingOverrideMethod_NvService) { + return; + } + u32 temp; + bool enabled = false; + switch(mode) { + case GpuSchedulingMode_DoNotOverride: break; + case GpuSchedulingMode_Disabled: + if(method == GpuSchedulingOverrideMethod_NvService) + nvIoctl(fd2, NVSCHED_CTRL_DISABLE, &temp); + else + enabled = false; + break; + case GpuSchedulingMode_Enabled: + if(method == GpuSchedulingOverrideMethod_NvService) + nvIoctl(fd2, NVSCHED_CTRL_ENABLE, &temp); + else + enabled = true; + break; + default: + ASSERT_ENUM_VALID(GpuSchedulingMode, mode); + } + if(method == GpuSchedulingOverrideMethod_Ini) { + const char* ini_path = "sdmc:/atmosphere/config/system_settings.ini"; + const char* section = "am.gpu"; + const char* key = "gpu_scheduling_enabled"; + + const char* value = enabled ? "u8!0x1" : "u8!0x0"; + + ini_puts(section, key, value, ini_path); + } +} +void Board::SetDisplayRefreshDockedState(bool docked) { + if(Board::GetConsoleType() != HorizonOCConsoleType_Hoag) { + DisplayRefresh_SetDockedState(docked); + } +} + + +typedef struct EristaCpuUvEntry { + u32 tune0; + u32 tune1; +} EristaCpuUvEntry; +typedef struct MarikoCpuUvEntry { + u32 tune0_low; + u32 tune0_high; + u32 tune1_low; + u32 tune1_high; +} MarikoCpuUvEntry; + +EristaCpuUvEntry eristaCpuUvTable[5] = { + {0xffff, 0x27007ff}, + {0xefff, 0x27407ff}, + {0xdfff, 0x27807ff}, + {0xdfdf, 0x27a07ff}, + {0xcfdf, 0x37007ff}, +}; + +MarikoCpuUvEntry marikoCpuUvLow[12] = { + {0xffa0, 0xffff, 0x21107ff, 0}, + {0x0, 0xffdf, 0x21107ff, 0x27207ff}, + {0xffdf, 0xffdf, 0x21107ff, 0x27307ff}, + {0xffff, 0xffdf, 0x21107ff, 0x27407ff}, + {0x0, 0xffdf, 0x21607ff, 0x27707ff}, + {0x0, 0xffdf, 0x21607ff, 0x27807ff}, + {0x0, 0xdfff, 0x21607ff, 0x27b07ff}, + {0xdfff, 0xdfff, 0x21707ff, 0x27b07ff}, + {0xdfff, 0xdfff, 0x21707ff, 0x27c07ff}, + {0xdfff, 0xdfff, 0x21707ff, 0x27d07ff}, + {0xdfff, 0xdfff, 0x21707ff, 0x27e07ff}, + {0xdfff, 0xdfff, 0x21707ff, 0x27f07ff}, +}; + +MarikoCpuUvEntry marikoCpuUvHigh[12] = { + {0x0, 0xffff, 0, 0}, + {0x0, 0xffdf, 0, 0x27207ff}, + {0x0, 0xffdf, 0, 0x27307ff}, + {0x0, 0xffdf, 0, 0x27407ff}, + {0x0, 0xffdf, 0, 0x27707ff}, + {0x0, 0xffdf, 0, 0x27807ff}, + {0x0, 0xdfff, 0, 0x27b07ff}, + {0x0, 0xdfff, 0, 0x27c07ff}, + {0x0, 0xdfff, 0, 0x27d07ff}, + {0x0, 0xdfff, 0, 0x27e07ff}, + {0x0, 0xdfff, 0, 0x27f07ff}, + {0x0, 0xdfff, 0, 0x27f07ff}, +}; +void Board::SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint) { + u32* tune0_ptr = (u32*)(cldvfs + CL_DVFS_TUNE0_0); + u32* tune1_ptr = (u32*)(cldvfs + CL_DVFS_TUNE1_0); + if(Board::GetSocType() == SysClkSocType_Mariko) { + if(Board::GetHz(SysClkModule_CPU) < tbreakPoint && (levelLow || levelHigh)) { + if(levelLow) { + *tune0_ptr = marikoCpuUvLow[levelLow-1].tune0_low; + *tune1_ptr = marikoCpuUvLow[levelLow-1].tune1_low; + } + return; + } else { + if(levelLow) { + *tune0_ptr = marikoCpuUvLow[levelLow-1].tune0_low; + *tune1_ptr = marikoCpuUvLow[levelLow-1].tune1_low; + } + if(levelHigh) { + *tune0_ptr = marikoCpuUvHigh[levelHigh-1].tune0_high; + *tune1_ptr = marikoCpuUvHigh[levelHigh-1].tune1_high; + } + return; + } + if(Board::GetHz(SysClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak + *tune0_ptr = 0xCFFF; + *tune1_ptr = 0xFF072201; + return; + } else if (Board::GetHz(SysClkModule_CPU) >= tbreakPoint || (!levelHigh)) { + *tune0_ptr = cachedMarikoUvHighTune0; // per console? + *tune1_ptr = 0xFFF7FF3F; + return; + } + } else { + if(Board::GetHz(SysClkModule_CPU) < tbreakPoint || (!levelLow)) { // account for tbreak + *tune0_ptr = cachedEristaUvLowTune0; // I think each erista has a different tune0/tune1? + *tune1_ptr = cachedEristaUvLowTune1; + return; + } else { + if(levelLow) { + *tune0_ptr = eristaCpuUvTable[levelLow-1].tune0; + *tune1_ptr = eristaCpuUvTable[levelLow-1].tune1; + } else { + *tune0_ptr = 0x0; + *tune1_ptr = 0x0; + } + } + } +} +/* +enum TableConfig: u32 { + DEFAULT_TABLE = 1, + TBREAK_1581 = 2, + TBREAK_1683 = 3, + EXTREME_TABLE = 4, +}; +*/ +u32 Board::CalculateTbreak(u32 table) { + if(Board::GetSocType() == SysClkSocType_Erista) + return 1581000000; + else { + switch(table) { + case 1 ... 2: + case 4: + return 1581000000; + case 3: + return 1683000000; + default: + return 1581000000; + } + } + +} + +bool Board::IsHoag() { + return Board::GetConsoleType() == HorizonOCConsoleType_Hoag; +} \ No newline at end of file diff --git a/Source/rewrite-hoc-clk/sysmodule/src/old_board.h b/Source/rewrite-hoc-clk/sysmodule/src/old_board.h new file mode 100644 index 00000000..2a32c87b --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/old_board.h @@ -0,0 +1,80 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once +#include +#include +#include + +class Board +{ + public: + static void PcvHijackDvfs(u32 vmin); + static u32 GetMinimumGpuVoltage(u32 freqMhz); + static void fuseReadSpeedos(); + static u16 getSpeedo(HorizonOCSpeedo speedoType); + static u16 getIDDQ(HorizonOCSpeedo speedoType); + static const char* GetProfileName(SysClkProfile profile, bool pretty); + static const char* GetModuleName(SysClkModule module, bool pretty); + static const char* GetThermalSensorName(SysClkThermalSensor sensor, bool pretty); + static const char* GetPowerSensorName(SysClkPowerSensor sensor, bool pretty); + static void Initialize(); + static void Exit(); + static void ResetToStock(); + static void ResetToStockCpu(); + static void ResetToStockMem(); + static void ResetToStockGpu(); + static void ResetToStockDisplay(); + static u8 GetHighestDockedDisplayRate(); + static SysClkProfile GetProfile(); + static void SetHz(SysClkModule module, std::uint32_t hz); + static std::uint32_t GetHz(SysClkModule module); + static std::uint32_t GetRealHz(SysClkModule module); + static void GetFreqList(SysClkModule module, std::uint32_t* outList, std::uint32_t maxCount, std::uint32_t* outCount); + static std::uint32_t GetTemperatureMilli(SysClkThermalSensor sensor); + static std::int32_t GetPowerMw(SysClkPowerSensor sensor); + static std::uint32_t GetPartLoad(SysClkPartLoad load); + static SysClkSocType GetSocType(); + static HorizonOCConsoleType GetConsoleType(); + static std::uint32_t GetVoltage(HocClkVoltage voltage); + static u8 GetFanRotationLevel(); + static u8 GetDramID(); + static bool IsDram8GB(); + static void SetGpuSchedulingMode(GpuSchedulingMode mode, GpuSchedulingOverrideMethod method); + static void SetDisplayRefreshDockedState(bool docked); + static void SetCpuUvLevel(u32 levelLow, u32 levelHigh, u32 tbreakPoint); + static u32 CalculateTbreak(u32 table); + static bool IsHoag(); + protected: + static void FetchHardwareInfos(); + static PcvModule GetPcvModule(SysClkModule sysclkModule); + static PcvModuleId GetPcvModuleId(SysClkModule sysclkModule); + private: + static void SetSpeedoBracket(); + static void CacheDvfsTable(); + static Handle GetPcvHandle(); +}; diff --git a/Source/rewrite-hoc-clk/sysmodule/src/process_management.cpp b/Source/rewrite-hoc-clk/sysmodule/src/process_management.cpp new file mode 100644 index 00000000..6c0f6953 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/process_management.cpp @@ -0,0 +1,85 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#include "process_management.h" +#include "file_utils.h" +#include "errors.h" +#define IS_QLAUNCH 0x20f + +void ProcessManagement::Initialize() +{ + Result rc = 0; + + rc = pmdmntInitialize(); + ASSERT_RESULT_OK(rc, "pmdmntInitialize"); + + rc = pminfoInitialize(); + ASSERT_RESULT_OK(rc, "pminfoInitialize"); +} + +void ProcessManagement::WaitForQLaunch() +{ + Result rc = 0; + std::uint64_t pid = 0; + do + { + rc = pmdmntGetProcessId(&pid, PROCESS_MANAGEMENT_QLAUNCH_TID); + svcSleepThread(50 * 1000000ULL); // 50ms + } while (R_FAILED(rc)); +} + +std::uint64_t ProcessManagement::GetCurrentApplicationId() +{ + Result rc = 0; + std::uint64_t pid = 0; + std::uint64_t tid = 0; + rc = pmdmntGetApplicationProcessId(&pid); + + if (rc == IS_QLAUNCH) + { + return PROCESS_MANAGEMENT_QLAUNCH_TID; + } + + ASSERT_RESULT_OK(rc, "pmdmntGetApplicationProcessId"); + + rc = pminfoGetProgramId(&tid, pid); + + if (rc == IS_QLAUNCH) + { + return PROCESS_MANAGEMENT_QLAUNCH_TID; + } + + ASSERT_RESULT_OK(rc, "pminfoGetProgramId"); + + return tid; +} + +void ProcessManagement::Exit() +{ + pmdmntExit(); + pminfoExit(); +} diff --git a/Source/rewrite-hoc-clk/sysmodule/src/process_management.h b/Source/rewrite-hoc-clk/sysmodule/src/process_management.h new file mode 100644 index 00000000..65abe792 --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/src/process_management.h @@ -0,0 +1,41 @@ +/* + * 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 . + * + */ + +/* -------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * , , + * wrote this file. As long as you retain this notice you can do whatever you + * want with this stuff. If you meet any of us some day, and you think this + * stuff is worth it, you can buy us a beer in return. - The sys-clk authors + * -------------------------------------------------------------------------- + */ + + +#pragma once +#include +#include + +#define PROCESS_MANAGEMENT_QLAUNCH_TID 0x0100000000001000ULL + +class ProcessManagement +{ + public: + static void Initialize(); + static void WaitForQLaunch(); + static std::uint64_t GetCurrentApplicationId(); + static void Exit(); +}; diff --git a/Source/rewrite-hoc-clk/sysmodule/toolbox.json b/Source/rewrite-hoc-clk/sysmodule/toolbox.json new file mode 100644 index 00000000..83ac964c --- /dev/null +++ b/Source/rewrite-hoc-clk/sysmodule/toolbox.json @@ -0,0 +1,5 @@ +{ + "name" : "hoc-clk", + "tid" : "00FF0000636C6BFF", + "requires_reboot": false +} \ No newline at end of file