hoc-clk: add live vdd2, live boost clock and basic pwm dimming

This commit is contained in:
souldbminersmwc
2025-11-07 21:04:34 -05:00
parent 31e1f1e1c6
commit 7711b41388
30 changed files with 1967 additions and 777 deletions

View File

@@ -4,8 +4,9 @@
- Tinymembench is under MIT license, which is compatible with GPL v2. - Tinymembench is under MIT license, which is compatible with GPL v2.
- Although "sys-clk" uses permissive license, all modifications towards it in this repo ("hoc-sys") are licensed under GPL v2. - Although "sys-clk" uses a permissive license, all modifications towards it in this repo ("hoc-sys") are licensed under GPL v2.
- Hekate Power BDK is made by CtCaer and licensed under GPLv2
GNU GENERAL PUBLIC LICENSE GNU GENERAL PUBLIC LICENSE
Version 2, June 1991 Version 2, June 1991

123
README.md
View File

@@ -5,7 +5,7 @@
--- ---
![License: GPL-2.0](https://img.shields.io/badge/GPL--2.0-red?style=for-the-badge) ![License: GPLv2](https://img.shields.io/badge/GPL--2.0-red?style=for-the-badge)
![Nintendo Switch](https://img.shields.io/badge/Nintendo_Switch-E60012?style=for-the-badge\&logo=nintendo-switch\&logoColor=white) ![Nintendo Switch](https://img.shields.io/badge/Nintendo_Switch-E60012?style=for-the-badge\&logo=nintendo-switch\&logoColor=white)
[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge\&logo=discord\&logoColor=white)](https://discord.com/invite/S3eX47dHsB) [![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge\&logo=discord\&logoColor=white)](https://discord.com/invite/S3eX47dHsB)
![VSCode](https://img.shields.io/badge/VSCode-0078D4?style=for-the-badge\&logo=visual%20studio%20code\&logoColor=white) ![VSCode](https://img.shields.io/badge/VSCode-0078D4?style=for-the-badge\&logo=visual%20studio%20code\&logoColor=white)
@@ -14,98 +14,51 @@
--- ---
</div> ### DISCLAIMER: THIS TOOL CAN BE DANGEROUS IF MISUSED. PROCEED WITH CAUTION
* Due to the design of Horizon OS, overclocking RAM can cause **NAND DAMAGE**. Ensure to have a NAND Backup<br>
## ⚠️ Disclaimer A open source overclocking tool for Nintendo Switch consoles running Atmosphere custom firmware<br>
> **THIS TOOL CAN BE DANGEROUS IF MISUSED. PROCEED WITH CAUTION.**
> Due to the design of Horizon OS, **overclocking RAM can cause NAND DAMAGE.**
> Ensure you have a **full NAND backup** before proceeding.
--- ## Features:
CPU overclock up to 2397MHz on Mariko units, 2091MHz on Erista units<br>
GPU up to 1305MHz on Mariko units, 998MHz on Erista units<br>
RAM up to 3200MHz on Mariko units, 2360MHz on Erista units<br>
Over/undervolting<br>
Configurator<br>
Works with most homebrew<br>
## 🌀 About *Higher (potentially dangerous) frequencies are unlockable*<br>
*The exact maximum overclock varies per console*<br>
## Installation
Ensure you have the latest version of [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere) and [Ultrahand](https://github.com/ppkantorski/Ultrahand-Overlay) installed before continuing<br>
Grab latest hoc.kip from releases tab<br>
If using hekate, edit hekate_ipl.ini to include "kip1=atmosphere/kips/*". No need for editing if using fusee<br>
Download latest Horizon OC sysmodule from releases tab<br>
Extract sysmodule into root of SD card<br>
**Horizon OC** is an open-source overclocking tool for Nintendo Switch consoles running **Atmosphere custom firmware**. Alternatively, you can download the configurator and click the two install buttons<br>
It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration tools.
---
## 🚀 Features ## Configuration
Download the latest configurator on your computer<br>
Run the file<br>
Select the drive your SD card or UMS device is mounted as<br>
Configure the kip to your liking, and in the end, save it<br>
* **CPU:** Up to `2397MHz` (Mariko) / `2091MHz` (Erista) ## Building
* **GPU:** Up to `1305MHz` (Mariko) / `998MHz` (Erista) Set up a development enviorment ready to compile Atmosphere<br>
* **RAM:** Up to `3200MHz` (Mariko) / `2360MHz` (Erista) Git clone Atmosphere, and move the cloned folder into build/<br>
* Over/undervolting support Insert Source/stratosphere folder into build/<br>
* Built-in configurator Run build.sh
* Compatible with most homebrew
> ⚡ *Higher (potentially dangerous) frequencies are unlockable.* To build the configurator, cd into Source/Configurator<br>
> ⚙️ *Exact maximum values vary per console.* Run build.bat or run "python -m PyInstaller --onefile --add-data "assets;assets" --icon=assets/icon.ico --noconsole src/main.py"<br>
---
## 🧩 Installation
1. Ensure you have the latest versions of
* [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere)
* [Ultrahand Overlay](https://github.com/ppkantorski/Ultrahand-Overlay)
2. Download the latest **hoc.kip** file from the [Releases](../../releases) tab.
3. If using **Hekate**, edit `hekate_ipl.ini` to include:
```
kip1=atmosphere/kips/*
```
*(No changes needed if using fusee.)*
4. Download and extract the **Horizon OC sysmodule** to the root of your SD card.
5. Alternatively, use the **Configurator** and click the **Install** buttons for automatic setup.
---
## ⚙️ Configuration
1. Download the latest **Configurator** on your computer.
2. Run the executable.
3. Select your SD card or UMS drive.
4. Adjust overclocking settings as desired.
5. Click **Save** to apply your configuration.
---
## 🧱 Building from Source
1. Set up a development environment for compiling **Atmosphere**.
2. Clone Atmosphere:
```bash
git clone https://github.com/Atmosphere-NX/Atmosphere.git
```
3. Move the cloned folder into `build/`.
4. Insert your `Source/stratosphere` folder into `build/`.
5. Run:
```bash
./build.sh
```
To build the Configurator:
```bash
cd Source/Configurator
build.bat
# or
python -m PyInstaller --onefile --add-data "assets;assets" --icon=assets/icon.ico --noconsole src/main.py
```
---
## 💎 Credits
* **Lightos** & **Dominatorul** RAM timings
* **KazushiMe** & **meha** Switch-OC-Suite
* **sys-clk team** sys-clk
* **b0rd2death** Ultrahand sys-clk fork
* **Lightos** & **Sammybigio2011** Early testing
## Credits
Lightos for RAM timings<br>
KazushiMe and meha for Switch-Oc-Suite<br>
sys-clk team for sys-clk<br>
b0rd2death for Ultrahand sys-clk fork<br>
Lightos and Sammybigio2011 for early testing<br>

View File

@@ -62,6 +62,9 @@ typedef enum
SysClkThermalSensor_SOC = 0, SysClkThermalSensor_SOC = 0,
SysClkThermalSensor_PCB, SysClkThermalSensor_PCB,
SysClkThermalSensor_Skin, SysClkThermalSensor_Skin,
// HocClkThermalSensor_BQ24193,
HocClkThermalSensor_Battery,
HocClkThermalSensor_PMIC,
SysClkThermalSensor_EnumMax SysClkThermalSensor_EnumMax
} SysClkThermalSensor; } SysClkThermalSensor;
@@ -87,6 +90,16 @@ typedef enum
ReverseNX_Docked, ReverseNX_Docked,
} ReverseNXMode; } ReverseNXMode;
typedef enum {
HocClkConsoleType_V1 = 0,
HocClkConsoleType_UnreleasedErista,
HocClkConsoleType_UnreleasedMariko,
HocClkConsoleType_V2,
HocClkConsoleType_Lite,
HocClkConsoleType_UnreleasedMariko2,
HocClkConsoleType_OLED,
HocClkConsoleType_EnumMax,
} HocClkConsoleType;
#define SYSCLK_ENUM_VALID(n, v) ((v) < n##_EnumMax) #define SYSCLK_ENUM_VALID(n, v) ((v) < n##_EnumMax)

View File

@@ -60,6 +60,15 @@ typedef enum {
HocClkConfigValue_EnforceBoardLimit, HocClkConfigValue_EnforceBoardLimit,
HocClkConfigValue_EristaBoostClock,
HocClkConfigValue_MarikoBoostClock,
HocClkConfigValue_EMCEnableUnsafeVoltages,
HocClkConfigValue_EMCVdd2VoltageuV,
HocClkConfigValue_EMCVddqVoltageuV,
HocClkConfigValue_PWMDimming,
SysClkConfigValue_EnumMax, SysClkConfigValue_EnumMax,
} SysClkConfigValue; } SysClkConfigValue;
@@ -122,7 +131,20 @@ static inline const char* sysclkFormatConfigValue(SysClkConfigValue val, bool pr
case HocClkConfigValue_TDPCycleLimit: case HocClkConfigValue_TDPCycleLimit:
return pretty ? "TDP Cycle Limit" : "tdp_limit_c"; return pretty ? "TDP Cycle Limit" : "tdp_limit_c";
case HocClkConfigValue_EnforceBoardLimit:
return pretty ? "Enforce Board Limit" : "enforce_board_limit";
case HocClkConfigValue_EristaBoostClock:
return pretty ? "Boost Clock" : "e_boost_clock";
case HocClkConfigValue_MarikoBoostClock:
return pretty ? "Boost Clock" : "m_boost_clock";
case HocClkConfigValue_EMCEnableUnsafeVoltages:
return pretty ? "EMC Unsafe Voltages" : "emc_unsafe_voltages";
case HocClkConfigValue_EMCVdd2VoltageuV:
return pretty ? "EMC VDD2 Voltage (mV)" : "emc_vdd2_voltage_uv";
case HocClkConfigValue_EMCVddqVoltageuV:
return pretty ? "EMC VDDQ Voltage (mV)" : "emc_vddq_voltage_uv";
case HocClkConfigValue_PWMDimming:
return pretty ? "PWM Dimming" : "pwm_dimming";
default: default:
return pretty ? "Null" : "null"; return pretty ? "Null" : "null";
} }
@@ -140,8 +162,10 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
case SysClkConfigValue_CsvWriteIntervalMs: case SysClkConfigValue_CsvWriteIntervalMs:
case HocClkConfigValue_UncappedClocks: case HocClkConfigValue_UncappedClocks:
case HocClkConfigValue_OverwriteBoostMode: case HocClkConfigValue_OverwriteBoostMode:
case HocClkConfigValue_EMCEnableUnsafeVoltages:
return 0ULL; return 0ULL;
case HocClkConfigValue_EristaMaxCpuClock: case HocClkConfigValue_EristaMaxCpuClock:
case HocClkConfigValue_EristaBoostClock:
return 1785ULL; return 1785ULL;
case HocClkConfigValue_EristaMaxGpuClock: case HocClkConfigValue_EristaMaxGpuClock:
return 921ULL; return 921ULL;
@@ -149,16 +173,18 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
return 1600ULL; return 1600ULL;
case HocClkConfigValue_MarikoMaxCpuClock: case HocClkConfigValue_MarikoMaxCpuClock:
case HocClkConfigValue_MarikoBoostClock:
return 1963ULL; return 1963ULL;
case HocClkConfigValue_MarikoMaxGpuClock: case HocClkConfigValue_MarikoMaxGpuClock:
return 1075ULL; return 1075ULL;
case HocClkConfigValue_MarikoMaxMemClock: case HocClkConfigValue_MarikoMaxMemClock:
return 1862ULL; return 1862ULL;
case HocClkConfigValue_ThermalThrottle: case HocClkConfigValue_ThermalThrottle:
case HocClkConfigValue_DockedGovernor: case HocClkConfigValue_DockedGovernor:
case HocClkConfigValue_HandheldGovernor: case HocClkConfigValue_HandheldGovernor:
case HocClkConfigValue_HandheldTDP: case HocClkConfigValue_HandheldTDP:
case HocClkConfigValue_EnforceBoardLimit:
case HocClkConfigValue_PWMDimming:
return 1ULL; return 1ULL;
case HocClkConfigValue_ThermalThrottleThreshold: case HocClkConfigValue_ThermalThrottleThreshold:
return 70ULL; return 70ULL;
@@ -168,6 +194,10 @@ static inline uint64_t sysclkDefaultConfigValue(SysClkConfigValue val)
return 6400ULL; return 6400ULL;
case HocClkConfigValue_TDPCycleLimit: case HocClkConfigValue_TDPCycleLimit:
return 10ULL; return 10ULL;
case HocClkConfigValue_EMCVdd2VoltageuV:
return 1175000ULL;
case HocClkConfigValue_EMCVddqVoltageuV:
return 600000ULL;
default: default:
return 0ULL; return 0ULL;
} }
@@ -193,6 +223,10 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case SysClkConfigValue_PowerLogIntervalMs: case SysClkConfigValue_PowerLogIntervalMs:
case SysClkConfigValue_CsvWriteIntervalMs: case SysClkConfigValue_CsvWriteIntervalMs:
case HocClkConfigValue_TDPCycleLimit: case HocClkConfigValue_TDPCycleLimit:
case HocClkConfigValue_EristaBoostClock:
case HocClkConfigValue_MarikoBoostClock:
case HocClkConfigValue_EMCVdd2VoltageuV:
case HocClkConfigValue_EMCVddqVoltageuV:
return input >= 0; return input >= 0;
case HocClkConfigValue_UncappedClocks: case HocClkConfigValue_UncappedClocks:
case HocClkConfigValue_OverwriteBoostMode: case HocClkConfigValue_OverwriteBoostMode:
@@ -200,7 +234,11 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case HocClkConfigValue_DockedGovernor: case HocClkConfigValue_DockedGovernor:
case HocClkConfigValue_HandheldGovernor: case HocClkConfigValue_HandheldGovernor:
case HocClkConfigValue_HandheldTDP: case HocClkConfigValue_HandheldTDP:
case HocClkConfigValue_EnforceBoardLimit:
case HocClkConfigValue_EMCEnableUnsafeVoltages:
case HocClkConfigValue_PWMDimming:
return (input & 0x1) == input; return (input & 0x1) == input;
default: default:
return false; return false;
} }

View File

@@ -49,7 +49,7 @@ enum SysClkIpcCmd
SysClkIpcCmd_SetConfigValues = 10, SysClkIpcCmd_SetConfigValues = 10,
SysClkIpcCmd_GetFreqList = 11, SysClkIpcCmd_GetFreqList = 11,
SysClkIpcCmd_SetReverseNXRTMode = 12, SysClkIpcCmd_SetReverseNXRTMode = 12,
HocClkIpcCmd_UpdateEMCRegs = 13, HocClkIpcCmd_UpdateEMC = 13,
}; };

View File

@@ -152,5 +152,5 @@ Result sysclkIpcSetReverseNXRTMode(ReverseNXMode mode)
Result hocClkIpcUpdateEmcRegs() Result hocClkIpcUpdateEmcRegs()
{ {
int nil = 0; int nil = 0;
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_SetReverseNXRTMode, nil); return serviceDispatchIn(&g_sysclkSrv, HocClkIpcCmd_UpdateEMC, nil);
} }

View File

@@ -23,20 +23,13 @@
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors * stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
#include "base_gui.h" #include "base_gui.h"
#include "../elements/base_frame.h" #include "../elements/base_frame.h"
#include "logo_rgba_bin.h" #include "logo_rgba_bin.h"
#define LOGO_X 20 #define LOGO_X 20
#define LOGO_Y 45 #define LOGO_Y 45
#define LOGO_LABEL_FONT_SIZE 35 #define LOGO_LABEL_FONT_SIZE 35
#define VERSION_X (LOGO_X + 250) #define VERSION_X (LOGO_X + 250)
#define VERSION_Y LOGO_Y-40 #define VERSION_Y LOGO_Y-40
#define VERSION_FONT_SIZE 15 #define VERSION_FONT_SIZE 15
@@ -52,9 +45,12 @@ std::string getVersionString() {
void BaseGui::preDraw(tsl::gfx::Renderer* renderer) void BaseGui::preDraw(tsl::gfx::Renderer* renderer)
{ {
// renderer->drawBitmap(LOGO_X, LOGO_Y, LOGO_WIDTH, LOGO_HEIGHT, logo_rgba_bin); // Draw "Horizon OC " in default color
renderer->drawString("Horizon OC overlay", false, LOGO_X, LOGO_Y, LOGO_LABEL_FONT_SIZE, renderer->a(TEXT_COLOR)); renderer->drawString("Horizon OC ", false, LOGO_X, LOGO_Y, LOGO_LABEL_FONT_SIZE, renderer->a(TEXT_COLOR));
// renderer->drawString(TARGET_VERSION, false, VERSION_X, VERSION_Y, VERSION_FONT_SIZE, tsl::bannerVersionTextColor);
// Draw "Gaea" in green
tsl::Color greenColor(40, 204, 40, 255); // Light green
renderer->drawString("Gaea", false, LOGO_X + 225, LOGO_Y, LOGO_LABEL_FONT_SIZE, greenColor);
} }
tsl::elm::Element* BaseGui::createUI() tsl::elm::Element* BaseGui::createUI()

View File

@@ -1,4 +1,15 @@
/* /*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
/*
*
* Copyright (c) Souldbminer and Horizon OC Contributors * Copyright (c) Souldbminer and Horizon OC Contributors
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -12,297 +23,322 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
/* -------------------------------------------------------------------------- #include "base_menu_gui.h"
* "THE BEER-WARE LICENSE" (Revision 42): #include "fatal_gui.h"
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you // Cache hardware model to avoid repeated syscalls
* 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 BaseMenuGui::BaseMenuGui()
* -------------------------------------------------------------------------- {
*/ tsl::initializeThemeVars();
this->context = nullptr;
this->lastContextUpdate = 0;
this->listElement = nullptr;
// Initialize all voltages to zero once
memset(&cpuVoltageUv, 0, sizeof(u32) * 5); // Zero all 5 voltage values at once
// Pre-cache hardware model during initialization
IsMariko();
// Initialize display strings
memset(displayStrings, 0, sizeof(displayStrings));
}
BaseMenuGui::~BaseMenuGui() {
delete this->context; // delete handles nullptr automatically
}
// Fast preDraw - just renders pre-computed strings
void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) {
BaseGui::preDraw(renderer);
if(!this->context) [[unlikely]] return;
// All constants pre-calculated and cached
static constexpr const char* const labels[] = {
"App ID", "Profile", "CPU", "GPU", "MEM", "SoC", "Board", "Skin", "Now", "Avg", "CPU", "GPU", "PLL", "PMIC", "BAT"
};
static constexpr u32 dataPositions[6] = {63-3+3, 200-1, 344-1-3, 200-1, 342-1, 321-1};
static u32 labelWidths[10];
static bool positionsInitialized = false;
if (!positionsInitialized) {
for (int i = 0; i < 10; i++) {
labelWidths[i] = renderer->getTextDimensions(labels[i], false, SMALL_TEXT_SIZE).first;
}
positionsInitialized = true;
}
static u32 positions[10] = {24-1, 310-labelWidths[1], 24-1, 192-labelWidths[3], 332-labelWidths[4], 24-1, 192 - labelWidths[6], 332-labelWidths[7], 192 - labelWidths[8], 332-labelWidths[9]};
static u32 maxProfileValueWidth = renderer->getTextDimensions("PD Charger", false, SMALL_TEXT_SIZE).first; // longest word
u32 y = 91;
// === TOP SECTION ===
// renderer->drawRoundedRect(14, 70-1, 420, 30+2, 10.0f, renderer->aWithOpacity(tsl::tableBGColor));
// App ID - use pre-formatted string
renderer->drawString(labels[0], false, positions[0], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(displayStrings[0], false, positions[0] + labelWidths[0] + 9, y, SMALL_TEXT_SIZE, tsl::infoTextColor);
// Profile - use pre-formatted string
renderer->drawString(labels[1], false, 423 - maxProfileValueWidth - labelWidths[1] - 9, y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(displayStrings[1], false, 423 - maxProfileValueWidth, y, SMALL_TEXT_SIZE, tsl::infoTextColor);
y = y + 38; // Direct assignment instead of += 38
// === MAIN DATA SECTION ===
// renderer->drawRoundedRect(14, 106, 420, 116, 10.0f, renderer->aWithOpacity(tsl::tableBGColor));
// === FREQUENCY SECTION ===
// Labels first (better cache locality)
renderer->drawString(labels[2], false, positions[2], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[3], false, positions[3], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[4], false, positions[4], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
// Current frequencies - use pre-formatted strings
renderer->drawString(displayStrings[2], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU
renderer->drawString(displayStrings[3], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU
renderer->drawString(displayStrings[4], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // MEM
y = y + 20; // Direct assignment (129 + 20)
// === REAL FREQUENCIES ===
renderer->drawString(displayStrings[5], false, positions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU real
renderer->drawString(displayStrings[6], false, positions[3], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU real
renderer->drawString(displayStrings[7], false, positions[4], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // MEM real
y = y + 20; // Direct assignment (149 + 20)
// === VOLTAGES ===
renderer->drawString(displayStrings[8], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU voltage
renderer->drawString(displayStrings[9], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU voltage
// Memory voltage - check if VDD is present
if (emcVoltageUv && vddVoltageUv) {
renderer->drawStringWithColoredSections(displayStrings[10], false, {""}, dataPositions[5]-16, y, SMALL_TEXT_SIZE, tsl::infoTextColor, tsl::separatorColor);
} else if (vddVoltageUv) {
renderer->drawString(displayStrings[10], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
} else if (emcVoltageUv) {
renderer->drawString(displayStrings[10], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
}
// y = y + 20; // Direct assignment (169 + 22)
#include "base_menu_gui.h" // renderer->drawString(displayStrings[17], false, dataPositions[0], y, SMALL_TEXT_SIZE, tempColors[3]); // CPU
#include "fatal_gui.h" // renderer->drawString(displayStrings[18], false, dataPositions[1], y, SMALL_TEXT_SIZE, tempColors[4]); // GPU
// renderer->drawString(displayStrings[19], false, dataPositions[2], y, SMALL_TEXT_SIZE, tempColors[5]); // PLL
// Cache hardware model to avoid repeated syscalls y = y + 22; // Direct assignment (149 + 20)
BaseMenuGui::BaseMenuGui() : tempColors{tsl::Color(0), tsl::Color(0), tsl::Color(0)} // === TEMPERATURE SECTION ===
{ // Labels
tsl::initializeThemeVars(); renderer->drawString(labels[5], false, positions[5], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
this->context = nullptr; renderer->drawString(labels[6], false, positions[6]-1, y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
this->lastContextUpdate = 0; renderer->drawString(labels[7], false, positions[7], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
this->listElement = nullptr;
// Initialize all voltages to zero once // Temperatures with color - use pre-computed colors
memset(&cpuVoltageUv, 0, sizeof(u32) * 5); // Zero all 5 voltage values at once renderer->drawString(displayStrings[11], false, dataPositions[0], y, SMALL_TEXT_SIZE, tempColors[0]); // SOC
renderer->drawString(displayStrings[12], false, dataPositions[1], y, SMALL_TEXT_SIZE, tempColors[1]); // PCB
renderer->drawString(displayStrings[13], false, dataPositions[2], y, SMALL_TEXT_SIZE, tempColors[2]); // Skin
// Pre-cache hardware model during initialization y = y + 20; // Direct assignment (191 + 20)
IsMariko();
// Initialize display strings // === SOC VOLTAGE & POWER ===
memset(displayStrings, 0, sizeof(displayStrings)); // SOC voltage (if available)
} if (socVoltageUv) [[likely]] {
renderer->drawString(displayStrings[14], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
}
BaseMenuGui::~BaseMenuGui() { // Power labels and values
delete this->context; // delete handles nullptr automatically renderer->drawString(labels[8], false, positions[8]-1, y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
} renderer->drawString(labels[9], false, positions[9], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
// Fast preDraw - just renders pre-computed strings renderer->drawString(displayStrings[15], false, dataPositions[3], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Power now
void BaseMenuGui::preDraw(tsl::gfx::Renderer* renderer) { renderer->drawString(displayStrings[16], false, dataPositions[4], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Power avg
BaseGui::preDraw(renderer);
if(!this->context) [[unlikely]] return;
// All constants pre-calculated and cached y = y + 20; // Direct assignment (191 + 20)
static constexpr const char* const labels[10] = {
"App ID", "Profile", "CPU", "GPU", "MEM", "SoC", "Board", "Skin", "Now", "Avg"
};
static constexpr u32 dataPositions[6] = {63-3+3, 200-1, 344-1-3, 200-1, 342-1, 321-1}; renderer->drawString(displayStrings[20], false, dataPositions[0], y, SMALL_TEXT_SIZE, tempColors[4]); // BAT
renderer->drawString(displayStrings[21], false, dataPositions[1], y, SMALL_TEXT_SIZE, tempColors[5]); // PMIC
static u32 labelWidths[10]; y = y + 20; // Direct assignment (191 + 20)
static bool positionsInitialized = false;
if (!positionsInitialized) {
for (int i = 0; i < 10; i++) {
labelWidths[i] = renderer->getTextDimensions(labels[i], false, SMALL_TEXT_SIZE).first;
}
positionsInitialized = true;
}
static u32 positions[10] = {24-1, 310-labelWidths[1], 24-1, 192-labelWidths[3], 332-labelWidths[4], 24-1, 192 - labelWidths[6], 332-labelWidths[7], 192 - labelWidths[8], 332-labelWidths[9]};
static u32 maxProfileValueWidth = renderer->getTextDimensions("PD Charger", false, SMALL_TEXT_SIZE).first; // longest word }
u32 y = 91; // Optimized refresh - now does all the string formatting once per second
void BaseMenuGui::refresh()
{
// === TOP SECTION === const u64 ticks = armGetSystemTick();
renderer->drawRoundedRect(14, 70-1, 420, 30+2, 10.0f, renderer->aWithOpacity(tsl::tableBGColor)); // Use cached comparison - 1 billion nanoseconds
if (armTicksToNs(ticks - this->lastContextUpdate) <= 1000000000UL) [[likely]] {
return; // Early exit for most calls
}
// App ID - use pre-formatted string this->lastContextUpdate = ticks;
renderer->drawString(labels[0], false, positions[0], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(displayStrings[0], false, positions[0] + labelWidths[0] + 9, y, SMALL_TEXT_SIZE, tsl::infoTextColor);
// Profile - use pre-formatted string // Lazy context allocation
renderer->drawString(labels[1], false, 423 - maxProfileValueWidth - labelWidths[1] - 9, y, SMALL_TEXT_SIZE, tsl::sectionTextColor); if (!this->context) [[unlikely]] {
renderer->drawString(displayStrings[1], false, 423 - maxProfileValueWidth, y, SMALL_TEXT_SIZE, tsl::infoTextColor); this->context = new SysClkContext;
}
y = 129; // Direct assignment instead of += 38 // === ULTRA-FAST VOLTAGE READING ===
// Pre-computed domain configuration based on hardware
static const PowerDomainId domains[] = {
PcvPowerDomainId_Max77621_Cpu, // [0] CPU
PcvPowerDomainId_Max77621_Gpu, // [1] GPU
PcvPowerDomainId_Max77812_Dram, // [2] EMC/DRAM - Mariko only
PcvPowerDomainId_Max77620_Sd0, // [3] SOC
PcvPowerDomainId_Max77620_Sd1 // [4] VDD2
};
// === MAIN DATA SECTION === // Voltage array for direct indexing
renderer->drawRoundedRect(14, 106, 420, 116, 10.0f, renderer->aWithOpacity(tsl::tableBGColor)); u32* voltages[] = {&cpuVoltageUv, &gpuVoltageUv, &emcVoltageUv, &socVoltageUv, &vddVoltageUv};
// === FREQUENCY SECTION === // Single regulator init/exit cycle
// Labels first (better cache locality) if (R_SUCCEEDED(rgltrInitialize())) [[likely]] {
renderer->drawString(labels[2], false, positions[2], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); if (IsMariko()) {
renderer->drawString(labels[3], false, positions[3], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); // Mariko with EOS: all 5 domains
renderer->drawString(labels[4], false, positions[4], y, SMALL_TEXT_SIZE, tsl::sectionTextColor); for (int i = 0; i < 5; ++i) {
RgltrSession session;
if (R_SUCCEEDED(rgltrOpenSession(&session, domains[i]))) [[likely]] {
if (R_FAILED(rgltrGetVoltage(&session, voltages[i]))) {
*voltages[i] = 0;
}
rgltrCloseSession(&session);
} else {
*voltages[i] = 0;
}
}
} else {
// Erista
// Erista with EOS: CPU, GPU, SOC, VDD (no DRAM)
for (int i = 0; i < 5; ++i) {
if (i == 2) continue; // Skip DRAM domain
// Current frequencies - use pre-formatted strings RgltrSession session;
renderer->drawString(displayStrings[2], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU if (R_SUCCEEDED(rgltrOpenSession(&session, domains[i]))) [[likely]] {
renderer->drawString(displayStrings[3], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU if (R_FAILED(rgltrGetVoltage(&session, voltages[i]))) {
renderer->drawString(displayStrings[4], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // MEM *voltages[i] = 0;
}
rgltrCloseSession(&session);
} else {
*voltages[i] = 0;
}
emcVoltageUv = 0; // Erista never supports DRAM
}
}
y = 149; // Direct assignment (129 + 20) rgltrExit();
} else {
// Zero all voltages on regulator failure
memset(&cpuVoltageUv, 0, sizeof(u32) * 5);
}
// === REAL FREQUENCIES === // === SYSCLK CONTEXT UPDATE ===
renderer->drawString(displayStrings[5], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU real const Result rc = sysclkIpcGetCurrentContext(this->context);
renderer->drawString(displayStrings[6], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU real if (R_FAILED(rc)) [[unlikely]] {
renderer->drawString(displayStrings[7], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // MEM real FatalGui::openWithResultCode("sysclkIpcGetCurrentContext", rc);
return;
}
y = 169; // Direct assignment (149 + 20) // === FORMAT ALL DISPLAY STRINGS (once per second) ===
// App ID (hex conversion)
sprintf(displayStrings[0], "%016lX", context->applicationId);
// === VOLTAGES === // Profile
renderer->drawString(displayStrings[8], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // CPU voltage strcpy(displayStrings[1], sysclkFormatProfile(context->profile, true));
renderer->drawString(displayStrings[9], false, dataPositions[1], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // GPU voltage
// Memory voltage - check if VDD is present // Current frequencies
if (emcVoltageUv && vddVoltageUv) { u32 hz = context->freqs[0]; // CPU
renderer->drawStringWithColoredSections(displayStrings[10], false, {""}, dataPositions[5]-16, y, SMALL_TEXT_SIZE, tsl::infoTextColor, tsl::separatorColor);
} else if (vddVoltageUv) {
renderer->drawString(displayStrings[10], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
} else if (emcVoltageUv) {
renderer->drawString(displayStrings[10], false, dataPositions[2], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
}
y = 191; // Direct assignment (169 + 22) sprintf(displayStrings[2], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
// === TEMPERATURE SECTION === hz = context->freqs[1]; // GPU
// Labels sprintf(displayStrings[3], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
renderer->drawString(labels[5], false, positions[5], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[6], false, positions[6]-1, y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(labels[7], false, positions[7], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
// Temperatures with color - use pre-computed colors hz = context->freqs[2]; // MEM
renderer->drawString(displayStrings[11], false, dataPositions[0], y, SMALL_TEXT_SIZE, tempColors[0]); // SOC sprintf(displayStrings[4], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
renderer->drawString(displayStrings[12], false, dataPositions[1], y, SMALL_TEXT_SIZE, tempColors[1]); // PCB
renderer->drawString(displayStrings[13], false, dataPositions[2], y, SMALL_TEXT_SIZE, tempColors[2]); // Skin
y = 211; // Direct assignment (191 + 20) // Real frequencies
hz = context->realFreqs[0]; // CPU
sprintf(displayStrings[5], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
// === SOC VOLTAGE & POWER === hz = context->realFreqs[1]; // GPU
// SOC voltage (if available) sprintf(displayStrings[6], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
if (socVoltageUv) [[likely]] {
renderer->drawString(displayStrings[14], false, dataPositions[0], y, SMALL_TEXT_SIZE, tsl::infoTextColor);
}
// Power labels and values hz = context->realFreqs[2]; // MEM
renderer->drawString(labels[8], false, positions[8]-1, y, SMALL_TEXT_SIZE, tsl::sectionTextColor); sprintf(displayStrings[7], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
renderer->drawString(labels[9], false, positions[9], y, SMALL_TEXT_SIZE, tsl::sectionTextColor);
renderer->drawString(displayStrings[15], false, dataPositions[3], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Power now // Voltages
renderer->drawString(displayStrings[16], false, dataPositions[4], y, SMALL_TEXT_SIZE, tsl::infoTextColor); // Power avg sprintf(displayStrings[8], "%.1f mV", cpuVoltageUv / 1000.0);
} sprintf(displayStrings[9], "%.1f mV", gpuVoltageUv / 1000.0);
// Optimized refresh - now does all the string formatting once per second // Memory voltage (handle VDD case)
void BaseMenuGui::refresh() if (emcVoltageUv && vddVoltageUv) {
{ //sprintf(displayStrings[10], "%u%u mV", vddVoltageUv / 1000U, emcVoltageUv / 1000U);
const u64 ticks = armGetSystemTick(); //sprintf(displayStrings[10], "%u%.1f mV", vddVoltageUv / 1000U, emcVoltageUv / 1000.0f);
// Use cached comparison - 1 billion nanoseconds sprintf(displayStrings[10], "%u.%u%u mV", vddVoltageUv / 1000U, (vddVoltageUv % 1000U) / 100U, emcVoltageUv / 1000U);
if (armTicksToNs(ticks - this->lastContextUpdate) <= 1000000000UL) [[likely]] { } else if (vddVoltageUv) {
return; // Early exit for most calls //sprintf(displayStrings[10], "%u mV", vddVoltageUv / 1000U);
} sprintf(displayStrings[10], "%u.%u mV", vddVoltageUv / 1000U, (vddVoltageUv % 1000U) / 100U);
} else if (emcVoltageUv) {
sprintf(displayStrings[10], "%u mV", emcVoltageUv / 1000U);
}
this->lastContextUpdate = ticks;
// Lazy context allocation u32 millis = context->temps[SysClkThermalSensor_SOC]; // SOC
if (!this->context) [[unlikely]] { sprintf(displayStrings[11], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
this->context = new SysClkContext; tempColors[0] = tsl::GradientColor(millis * 0.001f);
}
// === ULTRA-FAST VOLTAGE READING === millis = context->temps[SysClkThermalSensor_PCB]; // PCB
// Pre-computed domain configuration based on hardware sprintf(displayStrings[12], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
static const PowerDomainId domains[] = { tempColors[1] = tsl::GradientColor(millis * 0.001f);
PcvPowerDomainId_Max77621_Cpu, // [0] CPU
PcvPowerDomainId_Max77621_Gpu, // [1] GPU
PcvPowerDomainId_Max77812_Dram, // [2] EMC/DRAM - Mariko only
PcvPowerDomainId_Max77620_Sd0, // [3] SOC - EOS only
PcvPowerDomainId_Max77620_Sd1 // [4] VDD2 - EOS only
};
// Voltage array for direct indexing millis = context->temps[SysClkThermalSensor_Skin]; // Skin
u32* voltages[] = {&cpuVoltageUv, &gpuVoltageUv, &emcVoltageUv, &socVoltageUv, &vddVoltageUv}; sprintf(displayStrings[13], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
tempColors[2] = tsl::GradientColor(millis * 0.001f);
// Single regulator init/exit cycle millis = context->temps[HocClkThermalSensor_Battery]; //
if (R_SUCCEEDED(rgltrInitialize())) [[likely]] { sprintf(displayStrings[20], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
if (IsMariko()) { tempColors[3] = tsl::GradientColor(millis * 0.001f);
// Mariko with EOS: all 5 domains
for (int i = 0; i < 5; ++i) {
RgltrSession session;
if (R_SUCCEEDED(rgltrOpenSession(&session, domains[i]))) [[likely]] {
if (R_FAILED(rgltrGetVoltage(&session, voltages[i]))) {
*voltages[i] = 0;
}
rgltrCloseSession(&session);
} else {
*voltages[i] = 0;
}
}
} else {
// Erista
// Erista with EOS: CPU, GPU, SOC, VDD (no DRAM)
for (int i = 0; i < 5; ++i) {
if (i == 2) continue; // Skip DRAM domain
RgltrSession session; millis = context->temps[HocClkThermalSensor_PMIC]; //
if (R_SUCCEEDED(rgltrOpenSession(&session, domains[i]))) [[likely]] { sprintf(displayStrings[21], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
if (R_FAILED(rgltrGetVoltage(&session, voltages[i]))) { tempColors[4] = tsl::GradientColor(millis * 0.001f);
*voltages[i] = 0;
}
rgltrCloseSession(&session);
} else {
*voltages[i] = 0;
}
emcVoltageUv = 0; // Erista never supports DRAM
}
}
rgltrExit(); // millis = context->temps[HocClkThermalSensor_GPU]; // Skin
} else { // sprintf(displayStrings[18], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
// Zero all voltages on regulator failure // tempColors[4] = tsl::GradientColor(millis * 0.001f);
memset(&cpuVoltageUv, 0, sizeof(u32) * 5);
}
// === SYSCLK CONTEXT UPDATE === // millis = context->temps[HocClkThermalSensor_PLL]; // Skin
const Result rc = sysclkIpcGetCurrentContext(this->context); // sprintf(displayStrings[19], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
if (R_FAILED(rc)) [[unlikely]] { // tempColors[5] = tsl::GradientColor(millis * 0.001f);
FatalGui::openWithResultCode("sysclkIpcGetCurrentContext", rc);
return;
}
// === FORMAT ALL DISPLAY STRINGS (once per second) === // SOC voltage (if available)
// App ID (hex conversion) if (socVoltageUv) {
sprintf(displayStrings[0], "%016lX", context->applicationId); sprintf(displayStrings[14], "%u mV", socVoltageUv / 1000U);
}
// Profile // Power
strcpy(displayStrings[1], sysclkFormatProfile(context->profile, true)); sprintf(displayStrings[15], "%d mW", context->power[0]); // Now
sprintf(displayStrings[16], "%d mW", context->power[1]); // Avg
// Current frequencies
u32 hz = context->freqs[0]; // CPU
sprintf(displayStrings[2], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->freqs[1]; // GPU }
sprintf(displayStrings[3], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->freqs[2]; // MEM tsl::elm::Element* BaseMenuGui::baseUI()
sprintf(displayStrings[4], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U); {
auto* list = new tsl::elm::List();
this->listElement = list;
this->listUI();
// Real frequencies return list;
hz = context->realFreqs[0]; // CPU }
sprintf(displayStrings[5], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->realFreqs[1]; // GPU
sprintf(displayStrings[6], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
hz = context->realFreqs[2]; // MEM
sprintf(displayStrings[7], "%u.%u MHz", hz / 1000000U, (hz / 100000U) % 10U);
// Voltages
sprintf(displayStrings[8], "%.1f mV", cpuVoltageUv / 1000.0);
sprintf(displayStrings[9], "%.1f mV", gpuVoltageUv / 1000.0);
// Memory voltage (handle VDD case)
if (emcVoltageUv && vddVoltageUv) {
//sprintf(displayStrings[10], "%u%u mV", vddVoltageUv / 1000U, emcVoltageUv / 1000U);
//sprintf(displayStrings[10], "%u%.1f mV", vddVoltageUv / 1000U, emcVoltageUv / 1000.0f);
sprintf(displayStrings[10], "%u.%u%u mV", vddVoltageUv / 1000U, (vddVoltageUv % 1000U) / 100U, emcVoltageUv / 1000U);
} else if (vddVoltageUv) {
//sprintf(displayStrings[10], "%u mV", vddVoltageUv / 1000U);
sprintf(displayStrings[10], "%u.%u mV", vddVoltageUv / 1000U, (vddVoltageUv % 1000U) / 100U);
} else if (emcVoltageUv) {
sprintf(displayStrings[10], "%u mV", emcVoltageUv / 1000U);
}
// Temperatures and pre-compute colors
u32 millis = context->temps[0]; // SOC
sprintf(displayStrings[11], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
tempColors[0] = tsl::GradientColor(millis * 0.001f);
millis = context->temps[1]; // PCB
sprintf(displayStrings[12], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
tempColors[1] = tsl::GradientColor(millis * 0.001f);
millis = context->temps[2]; // Skin
sprintf(displayStrings[13], "%u.%u °C", millis / 1000U, (millis % 1000U) / 100U);
tempColors[2] = tsl::GradientColor(millis * 0.001f);
// SOC voltage (if available)
if (socVoltageUv) {
sprintf(displayStrings[14], "%u mV", socVoltageUv / 1000U);
}
// Power
sprintf(displayStrings[15], "%d mW", context->power[0]); // Now
sprintf(displayStrings[16], "%d mW", context->power[1]); // Avg
}
tsl::elm::Element* BaseMenuGui::baseUI()
{
auto* list = new tsl::elm::List();
this->listElement = list;
this->listUI();
return list;
}

View File

@@ -1,4 +1,15 @@
/* /*
* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
* wrote this file. As long as you retain this notice you can do whatever you
* want with this stuff. If you meet any of us some day, and you think this
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
/*
*
* Copyright (c) Souldbminer and Horizon OC Contributors * Copyright (c) Souldbminer and Horizon OC Contributors
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@@ -12,65 +23,69 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/ */
/* -------------------------------------------------------------------------- #pragma once
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net> #include "../../rgltr_services.h"
* wrote this file. As long as you retain this notice you can do whatever you #include "../../ipc.h"
* want with this stuff. If you meet any of us some day, and you think this #include "base_gui.h"
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
* --------------------------------------------------------------------------
*/
#pragma once class BaseMenuGui : public BaseGui
{
protected:
SysClkContext* context;
std::uint64_t lastContextUpdate;
std::uint32_t cpuVoltageUv;
std::uint32_t gpuVoltageUv;
std::uint32_t emcVoltageUv;
std::uint32_t socVoltageUv; //add soc voltage
std::uint32_t vddVoltageUv;//add vdd2 voltage
#include "../../rgltr_services.h" public:
#include "../../ipc.h" uint64_t systemtickfrequency = 19200000;
#include "base_gui.h" bool g_hardwareModelCached = false;
bool g_isMariko = false;
bool g_isAula = false;
class BaseMenuGui : public BaseGui bool IsMariko() {
{ if (!g_hardwareModelCached) {
protected: SetSysProductModel model = SetSysProductModel_Invalid;
SysClkContext* context; setsysGetProductModel(&model);
std::uint64_t lastContextUpdate; g_isMariko = (model == SetSysProductModel_Iowa ||
std::uint32_t cpuVoltageUv; model == SetSysProductModel_Hoag ||
std::uint32_t gpuVoltageUv; model == SetSysProductModel_Calcio ||
std::uint32_t emcVoltageUv; model == SetSysProductModel_Aula);
std::uint32_t socVoltageUv; //add soc voltage g_hardwareModelCached = true;
std::uint32_t vddVoltageUv;//add vdd2 voltage }
return g_isMariko;
}
public: bool IsAula() {
bool g_hardwareModelCached = false;
bool g_isMariko = false;
bool IsMariko() {
if (!g_hardwareModelCached) { if (!g_hardwareModelCached) {
SetSysProductModel model = SetSysProductModel_Invalid; SetSysProductModel model = SetSysProductModel_Invalid;
setsysGetProductModel(&model); setsysGetProductModel(&model);
g_isMariko = (model == SetSysProductModel_Iowa || g_isMariko = (model == SetSysProductModel_Aula);
model == SetSysProductModel_Hoag ||
model == SetSysProductModel_Calcio ||
model == SetSysProductModel_Aula);
g_hardwareModelCached = true;
} }
return g_isMariko; return g_isAula;
} }
bool IsErista() { bool IsErista() {
return !IsMariko(); return !IsMariko();
} }
BaseMenuGui(); BaseMenuGui();
~BaseMenuGui(); ~BaseMenuGui();
void preDraw(tsl::gfx::Renderer* renderer) override; void preDraw(tsl::gfx::Renderer* renderer) override;
tsl::elm::List* listElement; tsl::elm::List* listElement;
tsl::elm::Element* baseUI() override; tsl::elm::Element* baseUI() override;
void refresh() override; void refresh() override;
virtual void listUI() = 0; virtual void listUI() = 0;
private: private:
char displayStrings[17][32]; // Pre-formatted display strings char displayStrings[32][32]; // Pre-formatted display strings
tsl::Color tempColors[3]; // Pre-computed temperature colors tsl::Color tempColors[6] = {
}; tsl::Color(0), tsl::Color(0), tsl::Color(0),
tsl::Color(0), tsl::Color(0), tsl::Color(0)
};
};

View File

@@ -205,14 +205,18 @@ void MiscGui::listUI()
addConfigToggle(HocClkConfigValue_OverwriteBoostMode, nullptr); addConfigToggle(HocClkConfigValue_OverwriteBoostMode, nullptr);
this->listElement->addItem(new tsl::elm::CategoryHeader("Experimental")); this->listElement->addItem(new tsl::elm::CategoryHeader("Experimental"));
addConfigToggle(HocClkConfigValue_EnforceBoardLimit, nullptr);
addConfigToggle(HocClkConfigValue_ThermalThrottle, nullptr); addConfigToggle(HocClkConfigValue_ThermalThrottle, nullptr);
addConfigToggle(HocClkConfigValue_HandheldTDP, nullptr); addConfigToggle(HocClkConfigValue_HandheldTDP, nullptr);
addConfigToggle(HocClkConfigValue_EnforceBoardLimit, nullptr);
if(IsAula()) {
addConfigToggle(HocClkConfigValue_PWMDimming, "PWM Dimming");
}
ValueThresholds tdpThresholds(8600, 9500); ValueThresholds tdpThresholds(8600, 9500);
addConfigButton( addConfigButton(
HocClkConfigValue_HandheldTDPLimit, HocClkConfigValue_HandheldTDPLimit,
"TDP Threshold", "TDP Threshold",
ValueRange(5000, 10000, 100, "mW", 1), ValueRange(5000, 10000, 200, "mW", 1, 0),
"Power", "Power",
&tdpThresholds &tdpThresholds
); );
@@ -221,7 +225,7 @@ void MiscGui::listUI()
addConfigButton( addConfigButton(
HocClkConfigValue_LiteTDPLimit, HocClkConfigValue_LiteTDPLimit,
"Lite TDP Threshold", "Lite TDP Threshold",
ValueRange(4000, 8000, 100, "mW", 1), ValueRange(4000, 8000, 200, "mW", 1, 0),
"Power", "Power",
&tdpThresholdsLite &tdpThresholdsLite
); );
@@ -230,11 +234,11 @@ void MiscGui::listUI()
addConfigButton( addConfigButton(
HocClkConfigValue_ThermalThrottleThreshold, HocClkConfigValue_ThermalThrottleThreshold,
"Thermal Throttle Limit", "Thermal Throttle Limit",
ValueRange(50, 85, 1, "°C", 1), ValueRange(50, 85, 5, "°C", 1, 0),
"Temp", "Temp",
&throttleThresholds &throttleThresholds
); );
this->listElement->addItem(new tsl::elm::CategoryHeader("Max Clocks")); this->listElement->addItem(new tsl::elm::CategoryHeader("Clocks"));
if(IsMariko()) { if(IsMariko()) {
addFreqButton(HocClkConfigValue_MarikoMaxCpuClock, nullptr, SysClkModule_CPU); addFreqButton(HocClkConfigValue_MarikoMaxCpuClock, nullptr, SysClkModule_CPU);
addFreqButton(HocClkConfigValue_MarikoMaxGpuClock, nullptr, SysClkModule_GPU); addFreqButton(HocClkConfigValue_MarikoMaxGpuClock, nullptr, SysClkModule_GPU);
@@ -244,6 +248,62 @@ void MiscGui::listUI()
addFreqButton(HocClkConfigValue_EristaMaxGpuClock, nullptr, SysClkModule_GPU); addFreqButton(HocClkConfigValue_EristaMaxGpuClock, nullptr, SysClkModule_GPU);
addFreqButton(HocClkConfigValue_EristaMaxMemClock, nullptr, SysClkModule_MEM); addFreqButton(HocClkConfigValue_EristaMaxMemClock, nullptr, SysClkModule_MEM);
} }
// addFreqButton(HocClkConfigValue_CustomBoostClock, nullptr, S);
if(IsMariko()) {
ValueThresholds BoostThresholds(1963, 2397);
addConfigButton(
HocClkConfigValue_MarikoBoostClock,
"CPU Boost Clock",
ValueRange(1785, 2805, 102, "MHz", 1, 0),
"CPU Boost Clock",
&BoostThresholds
);
} else {
ValueThresholds BoostThresholds(1785, 2091);
addConfigButton(
HocClkConfigValue_EristaBoostClock,
"CPU Boost Clock",
ValueRange(1785, 2295, 102, "MHz", 1, 0),
"CPU Boost Clock",
&BoostThresholds
);
}
this->listElement->addItem(new tsl::elm::CategoryHeader("EMC and MTC"));
addConfigToggle(HocClkConfigValue_EMCEnableUnsafeVoltages, nullptr);
if(this->configList->values[HocClkConfigValue_EMCEnableUnsafeVoltages]) {
ValueThresholds emcUvThresholds(1212500, 1250000);
addConfigButton(
HocClkConfigValue_EMCVdd2VoltageuV,
"EMC VDD2 Voltage",
ValueRange(918500, 1350000, 12500, "µV", 1, 1),
"EMC VDD2 Voltage",
&emcUvThresholds
);
} else {
if(IsMariko()) {
ValueThresholds emcUvThresholds(1212500, 1250000);
addConfigButton(
HocClkConfigValue_EMCVdd2VoltageuV,
"EMC VDD2 Voltage",
ValueRange(1100000, 1212500, 12500, "µV", 1, 1),
"EMC VDD2 Voltage",
&emcUvThresholds
);
} else {
ValueThresholds emcUvThresholds(1237500, 1300000);
addConfigButton(
HocClkConfigValue_EMCVdd2VoltageuV,
"EMC VDD2 Voltage",
ValueRange(1125000, 1237500, 12500, "mV", 1000, 1),
"EMC VDD2 Voltage",
&emcUvThresholds
);
}
}
tsl::elm::ListItem* applyBtn = new tsl::elm::ListItem("Apply EMC Regs"); tsl::elm::ListItem* applyBtn = new tsl::elm::ListItem("Apply EMC Regs");
applyBtn->setClickListener([](u64 keys) { applyBtn->setClickListener([](u64 keys) {
if (keys & HidNpadButton_A) { if (keys & HidNpadButton_A) {

View File

@@ -1,145 +1,118 @@
/* #include "value_choice_gui.h"
* Copyright (c) Souldbminer and Horizon OC Contributors #include "../format.h"
* #include "fatal_gui.h"
* This program is free software; you can redistribute it and/or modify it #include <sstream>
* under the terms and conditions of the GNU General Public License, #include <iomanip>
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* -------------------------------------------------------------------------- ValueChoiceGui::ValueChoiceGui(std::uint32_t selectedValue,
* "THE BEER-WARE LICENSE" (Revision 42): const ValueRange& range,
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net> const std::string& categoryName,
* wrote this file. As long as you retain this notice you can do whatever you ValueChoiceListener listener,
* want with this stuff. If you meet any of us some day, and you think this const ValueThresholds& thresholds,
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors bool enableThresholds)
* -------------------------------------------------------------------------- : selectedValue(selectedValue),
*/ range(range),
categoryName(categoryName),
listener(listener),
thresholds(thresholds),
enableThresholds(enableThresholds)
{
}
ValueChoiceGui::~ValueChoiceGui()
{
}
#include "value_choice_gui.h" std::string ValueChoiceGui::formatValue(std::uint32_t value)
#include "../format.h" {
#include "fatal_gui.h" std::ostringstream oss;
#include <sstream>
#include <iomanip>
ValueChoiceGui::ValueChoiceGui(std::uint32_t selectedValue, if (value == 0) {
const ValueRange& range, return VALUE_DEFAULT_TEXT;
const std::string& categoryName, }
ValueChoiceListener listener,
const ValueThresholds& thresholds,
bool enableThresholds)
: selectedValue(selectedValue),
range(range),
categoryName(categoryName),
listener(listener),
thresholds(thresholds),
enableThresholds(enableThresholds)
{
}
ValueChoiceGui::~ValueChoiceGui() // Convert to floating point for division
{ double displayValue = static_cast<double>(value) / static_cast<double>(range.divisor);
}
std::string ValueChoiceGui::formatValue(std::uint32_t value) // Set precision and formatting
{ oss << std::fixed << std::setprecision(range.decimalPlaces) << displayValue;
std::ostringstream oss;
if (value == 0) { if (!range.suffix.empty()) {
oss << " " << range.suffix;
}
return oss.str();
}
return VALUE_DEFAULT_TEXT; int ValueChoiceGui::getSafetyLevel(std::uint32_t value)
} {
if (!enableThresholds) {
return 0;
}
std::uint32_t displayValue = value / range.divisor; std::uint32_t scaledValue = value / range.divisor;
oss << displayValue; if (scaledValue > thresholds.danger) {
if (!range.suffix.empty()) { return 2;
oss << " " << range.suffix; }
} if (scaledValue > thresholds.warning) {
return oss.str(); return 1;
} }
return 0;
}
int ValueChoiceGui::getSafetyLevel(std::uint32_t value) tsl::elm::ListItem* ValueChoiceGui::createValueListItem(std::uint32_t value, bool selected, int safety)
{ {
if (!enableThresholds) { std::string text = formatValue(value);
return 0; if (selected) {
} text += " \uE14B";
}
std::uint32_t scaledValue = value / range.divisor; tsl::elm::ListItem* listItem = new tsl::elm::ListItem(text, "", false);
if (scaledValue > thresholds.danger) { switch (safety)
return 2; {
} case 0:
if (scaledValue > thresholds.warning) { listItem->setTextColor(tsl::Color(255, 255, 255, 255));
return 1; listItem->setValueColor(tsl::Color(255, 255, 255, 255));
} break;
return 0; case 1:
} listItem->setTextColor(tsl::Color(255, 165, 0, 255));
listItem->setValueColor(tsl::Color(255, 165, 0, 255));
break;
case 2:
listItem->setTextColor(tsl::Color(255, 0, 0, 255));
listItem->setValueColor(tsl::Color(255, 0, 0, 255));
break;
}
tsl::elm::ListItem* ValueChoiceGui::createValueListItem(std::uint32_t value, bool selected, int safety) listItem->setClickListener([this, value](u64 keys)
{ {
std::string text = formatValue(value); if ((keys & HidNpadButton_A) == HidNpadButton_A && this->listener) {
if (selected) { if (this->listener(value)) {
text += " \uE14B"; tsl::goBack();
} }
return true;
}
return false;
});
tsl::elm::ListItem* listItem = new tsl::elm::ListItem(text, "", false); return listItem;
}
switch (safety) void ValueChoiceGui::listUI()
{ {
case 0: if (!categoryName.empty()) {
listItem->setTextColor(tsl::Color(255, 255, 255, 255)); this->listElement->addItem(new tsl::elm::CategoryHeader(categoryName));
listItem->setValueColor(tsl::Color(255, 255, 255, 255)); }
break;
case 1:
listItem->setTextColor(tsl::Color(255, 165, 0, 255));
listItem->setValueColor(tsl::Color(255, 165, 0, 255));
break;
case 2:
listItem->setTextColor(tsl::Color(255, 0, 0, 255));
listItem->setValueColor(tsl::Color(255, 0, 0, 255));
break;
}
listItem->setClickListener([this, value](u64 keys) this->listElement->addItem(this->createValueListItem(0, this->selectedValue == 0, 0));
{
if ((keys & HidNpadButton_A) == HidNpadButton_A && this->listener) {
if (this->listener(value)) { for (std::uint32_t value = range.min; value <= range.max; value += range.step)
tsl::goBack(); {
} int safety = getSafetyLevel(value);
return true; bool selected = (value == this->selectedValue);
} this->listElement->addItem(this->createValueListItem(value, selected, safety));
return false; }
});
return listItem; this->listElement->jumpToItem("", "\uE14B");
} }
void ValueChoiceGui::listUI()
{
if (!categoryName.empty()) {
this->listElement->addItem(new tsl::elm::CategoryHeader(categoryName));
}
this->listElement->addItem(this->createValueListItem(0, this->selectedValue == 0, 0));
for (std::uint32_t value = range.min; value <= range.max; value += range.step)
{
int safety = getSafetyLevel(value);
bool selected = (value == this->selectedValue);
this->listElement->addItem(this->createValueListItem(value, selected, safety));
}
this->listElement->jumpToItem("", "\uE14B");
}

View File

@@ -37,19 +37,19 @@
#define VALUE_DEFAULT_TEXT "Default" #define VALUE_DEFAULT_TEXT "Default"
struct ValueRange { struct ValueRange {
std::uint32_t min; std::uint32_t min;
std::uint32_t max; std::uint32_t max;
std::uint32_t step; std::uint32_t step;
std::string suffix; std::string suffix;
std::uint32_t divisor; // Divide input values by this for display std::uint32_t divisor; // Divide input values by this for display
int decimalPlaces; // Number of decimal places to display (0-6)
// Default constructor ValueRange() : min(0), max(0), step(1), suffix(""), divisor(1), decimalPlaces(0) {}
ValueRange() : min(0), max(0), step(1), suffix(""), divisor(1) {}
ValueRange(std::uint32_t min, std::uint32_t max, std::uint32_t step, ValueRange(std::uint32_t min, std::uint32_t max, std::uint32_t step,
const std::string& suffix = "", std::uint32_t divisor = 1) const std::string& suffix = "", std::uint32_t divisor = 1, int decimalPlaces = 0)
: min(min), max(max), step(step), suffix(suffix), divisor(divisor) {} : min(min), max(max), step(step), suffix(suffix), divisor(divisor), decimalPlaces(decimalPlaces) {}
}; };
struct ValueThresholds { struct ValueThresholds {
std::uint32_t warning; // Values >= this show orange std::uint32_t warning; // Values >= this show orange

View File

@@ -24,35 +24,39 @@
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
#include <nxExt.h> #include <nxExt.h>
#include <switch.h>
#include "board.h" #include "board.h"
#include "errors.h" #include "errors.h"
#include "maxXXXXX.h"
#define HOSSVC_HAS_CLKRST (hosversionAtLeast(8,0,0)) #define HOSSVC_HAS_CLKRST (hosversionAtLeast(8, 0, 0))
#define HOSSVC_HAS_TC (hosversionAtLeast(5,0,0)) #define HOSSVC_HAS_TC (hosversionAtLeast(5, 0, 0))
#define BQ24193_I2C_ADDR 0x6B
#define BQ24193_FaultReg 0x09
static SysClkSocType g_socType = SysClkSocType_Erista; static SysClkSocType g_socType = SysClkSocType_Erista;
const char* Board::GetModuleName(SysClkModule module, bool pretty) const char *Board::GetModuleName(SysClkModule module, bool pretty)
{ {
ASSERT_ENUM_VALID(SysClkModule, module); ASSERT_ENUM_VALID(SysClkModule, module);
return sysclkFormatModule(module, pretty); return sysclkFormatModule(module, pretty);
} }
const char* Board::GetProfileName(SysClkProfile profile, bool pretty) const char *Board::GetProfileName(SysClkProfile profile, bool pretty)
{ {
ASSERT_ENUM_VALID(SysClkProfile, profile); ASSERT_ENUM_VALID(SysClkProfile, profile);
return sysclkFormatProfile(profile, pretty); return sysclkFormatProfile(profile, pretty);
} }
const char* Board::GetThermalSensorName(SysClkThermalSensor sensor, bool pretty) const char *Board::GetThermalSensorName(SysClkThermalSensor sensor, bool pretty)
{ {
ASSERT_ENUM_VALID(SysClkThermalSensor, sensor); ASSERT_ENUM_VALID(SysClkThermalSensor, sensor);
return sysclkFormatThermalSensor(sensor, pretty); return sysclkFormatThermalSensor(sensor, pretty);
} }
const char* Board::GetPowerSensorName(SysClkPowerSensor sensor, bool pretty) const char *Board::GetPowerSensorName(SysClkPowerSensor sensor, bool pretty)
{ {
ASSERT_ENUM_VALID(SysClkPowerSensor, sensor); ASSERT_ENUM_VALID(SysClkPowerSensor, sensor);
return sysclkFormatPowerSensor(sensor, pretty); return sysclkFormatPowerSensor(sensor, pretty);
@@ -60,19 +64,18 @@ const char* Board::GetPowerSensorName(SysClkPowerSensor sensor, bool pretty)
PcvModule Board::GetPcvModule(SysClkModule sysclkModule) PcvModule Board::GetPcvModule(SysClkModule sysclkModule)
{ {
switch(sysclkModule) switch (sysclkModule) {
{ case SysClkModule_CPU:
case SysClkModule_CPU: return PcvModule_CpuBus;
return PcvModule_CpuBus; case SysClkModule_GPU:
case SysClkModule_GPU: return PcvModule_GPU;
return PcvModule_GPU; case SysClkModule_MEM:
case SysClkModule_MEM: return PcvModule_EMC;
return PcvModule_EMC; default:
default: ASSERT_ENUM_VALID(SysClkModule, sysclkModule);
ASSERT_ENUM_VALID(SysClkModule, sysclkModule);
} }
return (PcvModule)0; return (PcvModule) 0;
} }
PcvModuleId Board::GetPcvModuleId(SysClkModule sysclkModule) PcvModuleId Board::GetPcvModuleId(SysClkModule sysclkModule)
@@ -88,13 +91,10 @@ void Board::Initialize()
{ {
Result rc = 0; Result rc = 0;
if(HOSSVC_HAS_CLKRST) if (HOSSVC_HAS_CLKRST) {
{
rc = clkrstInitialize(); rc = clkrstInitialize();
ASSERT_RESULT_OK(rc, "clkrstInitialize"); ASSERT_RESULT_OK(rc, "clkrstInitialize");
} } else {
else
{
rc = pcvInitialize(); rc = pcvInitialize();
ASSERT_RESULT_OK(rc, "pcvInitialize"); ASSERT_RESULT_OK(rc, "pcvInitialize");
} }
@@ -105,8 +105,7 @@ void Board::Initialize()
rc = psmInitialize(); rc = psmInitialize();
ASSERT_RESULT_OK(rc, "psmInitialize"); ASSERT_RESULT_OK(rc, "psmInitialize");
if(HOSSVC_HAS_TC) if (HOSSVC_HAS_TC) {
{
rc = tcInitialize(); rc = tcInitialize();
ASSERT_RESULT_OK(rc, "tcInitialize"); ASSERT_RESULT_OK(rc, "tcInitialize");
} }
@@ -122,20 +121,16 @@ void Board::Initialize()
void Board::Exit() void Board::Exit()
{ {
if(HOSSVC_HAS_CLKRST) if (HOSSVC_HAS_CLKRST) {
{
clkrstExit(); clkrstExit();
} } else {
else
{
pcvExit(); pcvExit();
} }
apmExtExit(); apmExtExit();
psmExit(); psmExit();
if(HOSSVC_HAS_TC) if (HOSSVC_HAS_TC) {
{
tcExit(); tcExit();
} }
@@ -149,8 +144,7 @@ SysClkProfile Board::GetProfile()
Result rc = apmExtGetPerformanceMode(&mode); Result rc = apmExtGetPerformanceMode(&mode);
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
if(mode) if (mode) {
{
return SysClkProfile_Docked; return SysClkProfile_Docked;
} }
@@ -159,12 +153,9 @@ SysClkProfile Board::GetProfile()
rc = psmGetChargerType(&chargerType); rc = psmGetChargerType(&chargerType);
ASSERT_RESULT_OK(rc, "psmGetChargerType"); ASSERT_RESULT_OK(rc, "psmGetChargerType");
if(chargerType == PsmChargerType_EnoughPower) if (chargerType == PsmChargerType_EnoughPower) {
{
return SysClkProfile_HandheldChargingOfficial; return SysClkProfile_HandheldChargingOfficial;
} } else if (chargerType == PsmChargerType_LowPower) {
else if(chargerType == PsmChargerType_LowPower)
{
return SysClkProfile_HandheldChargingUSB; return SysClkProfile_HandheldChargingUSB;
} }
@@ -175,9 +166,8 @@ void Board::SetHz(SysClkModule module, std::uint32_t hz)
{ {
Result rc = 0; Result rc = 0;
if(HOSSVC_HAS_CLKRST) if (HOSSVC_HAS_CLKRST) {
{ ClkrstSession session = { 0 };
ClkrstSession session = {0};
rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3); rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3);
ASSERT_RESULT_OK(rc, "clkrstOpenSession"); ASSERT_RESULT_OK(rc, "clkrstOpenSession");
@@ -186,9 +176,7 @@ void Board::SetHz(SysClkModule module, std::uint32_t hz)
ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); ASSERT_RESULT_OK(rc, "clkrstSetClockRate");
clkrstCloseSession(&session); clkrstCloseSession(&session);
} } else {
else
{
rc = pcvSetClockRate(Board::GetPcvModule(module), hz); rc = pcvSetClockRate(Board::GetPcvModule(module), hz);
ASSERT_RESULT_OK(rc, "pcvSetClockRate"); ASSERT_RESULT_OK(rc, "pcvSetClockRate");
} }
@@ -199,9 +187,8 @@ std::uint32_t Board::GetHz(SysClkModule module)
Result rc = 0; Result rc = 0;
std::uint32_t hz = 0; std::uint32_t hz = 0;
if(HOSSVC_HAS_CLKRST) if (HOSSVC_HAS_CLKRST) {
{ ClkrstSession session = { 0 };
ClkrstSession session = {0};
rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3); rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3);
ASSERT_RESULT_OK(rc, "clkrstOpenSession"); ASSERT_RESULT_OK(rc, "clkrstOpenSession");
@@ -210,9 +197,7 @@ std::uint32_t Board::GetHz(SysClkModule module)
ASSERT_RESULT_OK(rc, "clkrstSetClockRate"); ASSERT_RESULT_OK(rc, "clkrstSetClockRate");
clkrstCloseSession(&session); clkrstCloseSession(&session);
} } else {
else
{
rc = pcvGetClockRate(Board::GetPcvModule(module), &hz); rc = pcvGetClockRate(Board::GetPcvModule(module), &hz);
ASSERT_RESULT_OK(rc, "pcvGetClockRate"); ASSERT_RESULT_OK(rc, "pcvGetClockRate");
} }
@@ -222,49 +207,48 @@ std::uint32_t Board::GetHz(SysClkModule module)
std::uint32_t Board::GetRealHz(SysClkModule module) std::uint32_t Board::GetRealHz(SysClkModule module)
{ {
switch(module) switch (module) {
{ case SysClkModule_CPU:
case SysClkModule_CPU: return t210ClkCpuFreq();
return t210ClkCpuFreq(); case SysClkModule_GPU:
case SysClkModule_GPU: return t210ClkGpuFreq();
return t210ClkGpuFreq(); case SysClkModule_MEM:
case SysClkModule_MEM: return t210ClkMemFreq();
return t210ClkMemFreq(); default:
default: ASSERT_ENUM_VALID(SysClkModule, module);
ASSERT_ENUM_VALID(SysClkModule, module);
} }
return 0; return 0;
} }
void Board::GetFreqList(SysClkModule module, std::uint32_t* outList, std::uint32_t maxCount, std::uint32_t* outCount) void Board::GetFreqList(SysClkModule module, std::uint32_t *outList,
std::uint32_t maxCount, std::uint32_t *outCount)
{ {
Result rc = 0; Result rc = 0;
PcvClockRatesListType type; PcvClockRatesListType type;
s32 tmpInMaxCount = maxCount; s32 tmpInMaxCount = maxCount;
s32 tmpOutCount = 0; s32 tmpOutCount = 0;
if(HOSSVC_HAS_CLKRST) if (HOSSVC_HAS_CLKRST) {
{ ClkrstSession session = { 0 };
ClkrstSession session = {0};
rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3); rc = clkrstOpenSession(&session, Board::GetPcvModuleId(module), 3);
ASSERT_RESULT_OK(rc, "clkrstOpenSession"); ASSERT_RESULT_OK(rc, "clkrstOpenSession");
rc = clkrstGetPossibleClockRates(&session, outList, tmpInMaxCount, &type, &tmpOutCount); rc = clkrstGetPossibleClockRates(
&session, outList, tmpInMaxCount, &type, &tmpOutCount);
ASSERT_RESULT_OK(rc, "clkrstGetPossibleClockRates"); ASSERT_RESULT_OK(rc, "clkrstGetPossibleClockRates");
clkrstCloseSession(&session); clkrstCloseSession(&session);
} } else {
else rc = pcvGetPossibleClockRates(Board::GetPcvModule(module), outList,
{ tmpInMaxCount, &type, &tmpOutCount);
rc = pcvGetPossibleClockRates(Board::GetPcvModule(module), outList, tmpInMaxCount, &type, &tmpOutCount);
ASSERT_RESULT_OK(rc, "pcvGetPossibleClockRates"); ASSERT_RESULT_OK(rc, "pcvGetPossibleClockRates");
} }
if(type != PcvClockRatesListType_Discrete) if (type != PcvClockRatesListType_Discrete) {
{ ERROR_THROW("Unexpected PcvClockRatesListType: %u (module = %s)", type,
ERROR_THROW("Unexpected PcvClockRatesListType: %u (module = %s)", type, Board::GetModuleName(module, false)); Board::GetModuleName(module, false));
} }
*outCount = tmpOutCount; *outCount = tmpOutCount;
@@ -273,33 +257,27 @@ void Board::GetFreqList(SysClkModule module, std::uint32_t* outList, std::uint32
void Board::ResetToStock() void Board::ResetToStock()
{ {
Result rc = 0; Result rc = 0;
if(hosversionAtLeast(9,0,0)) if (hosversionAtLeast(9, 0, 0)) {
{
std::uint32_t confId = 0; std::uint32_t confId = 0;
rc = apmExtGetCurrentPerformanceConfiguration(&confId); rc = apmExtGetCurrentPerformanceConfiguration(&confId);
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
SysClkApmConfiguration* apmConfiguration = NULL; SysClkApmConfiguration *apmConfiguration = NULL;
for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) for (size_t i = 0; sysclk_g_apm_configurations[i].id; i++) {
{ if (sysclk_g_apm_configurations[i].id == confId) {
if(sysclk_g_apm_configurations[i].id == confId)
{
apmConfiguration = &sysclk_g_apm_configurations[i]; apmConfiguration = &sysclk_g_apm_configurations[i];
break; break;
} }
} }
if(!apmConfiguration) if (!apmConfiguration) {
{
ERROR_THROW("Unknown apm configuration: %x", confId); ERROR_THROW("Unknown apm configuration: %x", confId);
} }
Board::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz); Board::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz);
Board::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz); Board::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz);
Board::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz); Board::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz);
} } else {
else
{
std::uint32_t mode = 0; std::uint32_t mode = 0;
rc = apmExtGetPerformanceMode(&mode); rc = apmExtGetPerformanceMode(&mode);
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
@@ -312,31 +290,25 @@ void Board::ResetToStock()
void Board::ResetToStockCpu() void Board::ResetToStockCpu()
{ {
Result rc = 0; Result rc = 0;
if(hosversionAtLeast(9,0,0)) if (hosversionAtLeast(9, 0, 0)) {
{
std::uint32_t confId = 0; std::uint32_t confId = 0;
rc = apmExtGetCurrentPerformanceConfiguration(&confId); rc = apmExtGetCurrentPerformanceConfiguration(&confId);
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
SysClkApmConfiguration* apmConfiguration = NULL; SysClkApmConfiguration *apmConfiguration = NULL;
for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) for (size_t i = 0; sysclk_g_apm_configurations[i].id; i++) {
{ if (sysclk_g_apm_configurations[i].id == confId) {
if(sysclk_g_apm_configurations[i].id == confId)
{
apmConfiguration = &sysclk_g_apm_configurations[i]; apmConfiguration = &sysclk_g_apm_configurations[i];
break; break;
} }
} }
if(!apmConfiguration) if (!apmConfiguration) {
{
ERROR_THROW("Unknown apm configuration: %x", confId); ERROR_THROW("Unknown apm configuration: %x", confId);
} }
Board::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz); Board::SetHz(SysClkModule_CPU, apmConfiguration->cpu_hz);
} } else {
else
{
std::uint32_t mode = 0; std::uint32_t mode = 0;
rc = apmExtGetPerformanceMode(&mode); rc = apmExtGetPerformanceMode(&mode);
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
@@ -349,31 +321,25 @@ void Board::ResetToStockCpu()
void Board::ResetToStockMem() void Board::ResetToStockMem()
{ {
Result rc = 0; Result rc = 0;
if(hosversionAtLeast(9,0,0)) if (hosversionAtLeast(9, 0, 0)) {
{
std::uint32_t confId = 0; std::uint32_t confId = 0;
rc = apmExtGetCurrentPerformanceConfiguration(&confId); rc = apmExtGetCurrentPerformanceConfiguration(&confId);
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
SysClkApmConfiguration* apmConfiguration = NULL; SysClkApmConfiguration *apmConfiguration = NULL;
for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) for (size_t i = 0; sysclk_g_apm_configurations[i].id; i++) {
{ if (sysclk_g_apm_configurations[i].id == confId) {
if(sysclk_g_apm_configurations[i].id == confId)
{
apmConfiguration = &sysclk_g_apm_configurations[i]; apmConfiguration = &sysclk_g_apm_configurations[i];
break; break;
} }
} }
if(!apmConfiguration) if (!apmConfiguration) {
{
ERROR_THROW("Unknown apm configuration: %x", confId); ERROR_THROW("Unknown apm configuration: %x", confId);
} }
Board::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz); Board::SetHz(SysClkModule_MEM, apmConfiguration->mem_hz);
} } else {
else
{
std::uint32_t mode = 0; std::uint32_t mode = 0;
rc = apmExtGetPerformanceMode(&mode); rc = apmExtGetPerformanceMode(&mode);
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
@@ -386,31 +352,25 @@ void Board::ResetToStockMem()
void Board::ResetToStockGpu() void Board::ResetToStockGpu()
{ {
Result rc = 0; Result rc = 0;
if(hosversionAtLeast(9,0,0)) if (hosversionAtLeast(9, 0, 0)) {
{
std::uint32_t confId = 0; std::uint32_t confId = 0;
rc = apmExtGetCurrentPerformanceConfiguration(&confId); rc = apmExtGetCurrentPerformanceConfiguration(&confId);
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
SysClkApmConfiguration* apmConfiguration = NULL; SysClkApmConfiguration *apmConfiguration = NULL;
for(size_t i = 0; sysclk_g_apm_configurations[i].id; i++) for (size_t i = 0; sysclk_g_apm_configurations[i].id; i++) {
{ if (sysclk_g_apm_configurations[i].id == confId) {
if(sysclk_g_apm_configurations[i].id == confId)
{
apmConfiguration = &sysclk_g_apm_configurations[i]; apmConfiguration = &sysclk_g_apm_configurations[i];
break; break;
} }
} }
if(!apmConfiguration) if (!apmConfiguration) {
{
ERROR_THROW("Unknown apm configuration: %x", confId); ERROR_THROW("Unknown apm configuration: %x", confId);
} }
Board::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz); Board::SetHz(SysClkModule_GPU, apmConfiguration->gpu_hz);
} } else {
else
{
std::uint32_t mode = 0; std::uint32_t mode = 0;
rc = apmExtGetPerformanceMode(&mode); rc = apmExtGetPerformanceMode(&mode);
ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode"); ASSERT_RESULT_OK(rc, "apmExtGetPerformanceMode");
@@ -419,29 +379,56 @@ void Board::ResetToStockGpu()
ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode"); ASSERT_RESULT_OK(rc, "apmExtSysRequestPerformanceMode");
} }
} }
std::uint32_t Board::GetTemperatureMilli(SysClkThermalSensor sensor) std::uint32_t Board::GetTemperatureMilli(SysClkThermalSensor sensor)
{ {
std::int32_t millis = 0; std::int32_t millis = 0;
Result res;
u16 data;
u8 reg = MAX17050_TEMP;
if(sensor == SysClkThermalSensor_SOC) switch (sensor) {
{ case SysClkThermalSensor_SOC:
millis = tmp451TempSoc(); millis = tmp451TempSoc();
} break;
else if(sensor == SysClkThermalSensor_PCB) case SysClkThermalSensor_PCB:
{
millis = tmp451TempPcb(); millis = tmp451TempPcb();
} break;
else if(sensor == SysClkThermalSensor_Skin) case SysClkThermalSensor_Skin:
{
if(HOSSVC_HAS_TC) if (HOSSVC_HAS_TC) {
{
Result rc; Result rc;
rc = tcGetSkinTemperatureMilliC(&millis); rc = tcGetSkinTemperatureMilliC(&millis);
ASSERT_RESULT_OK(rc, "tcGetSkinTemperatureMilliC"); ASSERT_RESULT_OK(rc, "tcGetSkinTemperatureMilliC");
} }
} break;
else case HocClkThermalSensor_Battery:
{ I2cSession session;
res = i2cOpenSession(&session, I2cDevice_Max17050);
if (R_FAILED(res)) {
millis = -1;
break;
}
data = 0;
res = i2csessionSendAuto(&session, &reg, 1, I2cTransactionOption_Start);
if (R_FAILED(res)) {
i2csessionClose(&session);
millis = -1;
break;
}
res = i2csessionReceiveAuto(&session, (u8 *) &data, 2, I2cTransactionOption_Stop);
i2csessionClose(&session);
millis = (std::int32_t) data * 10 / 256;
break;
case HocClkThermalSensor_PMIC:
millis = 50000; // Literally the only reasonable value the PMIC can return
break;
default:
ASSERT_ENUM_VALID(SysClkThermalSensor, sensor); ASSERT_ENUM_VALID(SysClkThermalSensor, sensor);
} }
@@ -450,14 +437,13 @@ std::uint32_t Board::GetTemperatureMilli(SysClkThermalSensor sensor)
std::int32_t Board::GetPowerMw(SysClkPowerSensor sensor) std::int32_t Board::GetPowerMw(SysClkPowerSensor sensor)
{ {
switch(sensor) switch (sensor) {
{ case SysClkPowerSensor_Now:
case SysClkPowerSensor_Now: return max17050PowerNow();
return max17050PowerNow(); case SysClkPowerSensor_Avg:
case SysClkPowerSensor_Avg: return max17050PowerAvg();
return max17050PowerAvg(); default:
default: ASSERT_ENUM_VALID(SysClkPowerSensor, sensor);
ASSERT_ENUM_VALID(SysClkPowerSensor, sensor);
} }
return 0; return 0;
@@ -465,24 +451,23 @@ std::int32_t Board::GetPowerMw(SysClkPowerSensor sensor)
std::uint32_t Board::GetRamLoad(SysClkRamLoad loadSource) std::uint32_t Board::GetRamLoad(SysClkRamLoad loadSource)
{ {
switch(loadSource) switch (loadSource) {
{ case SysClkRamLoad_All:
case SysClkRamLoad_All: return t210EmcLoadAll();
return t210EmcLoadAll(); case SysClkRamLoad_Cpu:
case SysClkRamLoad_Cpu: return t210EmcLoadCpu();
return t210EmcLoadCpu(); default:
default: ASSERT_ENUM_VALID(SysClkRamLoad, loadSource);
ASSERT_ENUM_VALID(SysClkRamLoad, loadSource);
} }
return 0; return 0;
} }
SysClkSocType Board::GetSocType() { SysClkSocType Board::GetSocType()
{
return g_socType; return g_socType;
} }
void Board::FetchHardwareInfos() void Board::FetchHardwareInfos()
{ {
u64 sku = 0; u64 sku = 0;
@@ -494,17 +479,16 @@ void Board::FetchHardwareInfos()
splExit(); splExit();
switch(sku) switch (sku) {
{ case 2:
case 2: case 3:
case 3: case 5:
case 5: g_socType = SysClkSocType_Mariko;
g_socType = SysClkSocType_Mariko; break;
break; case 4:
case 4: g_socType = SysClkSocType_MarikoLite;
g_socType = SysClkSocType_MarikoLite; break;
break; default:
default: g_socType = SysClkSocType_Erista;
g_socType = SysClkSocType_Erista;
} }
} }

View File

@@ -33,6 +33,7 @@
class Board class Board
{ {
public: public:
u16 max17050_get_reg(u8 reg);
static const char* GetProfileName(SysClkProfile profile, bool pretty); static const char* GetProfileName(SysClkProfile profile, bool pretty);
static const char* GetModuleName(SysClkModule module, bool pretty); static const char* GetModuleName(SysClkModule module, bool pretty);
static const char* GetThermalSensorName(SysClkThermalSensor sensor, bool pretty); static const char* GetThermalSensorName(SysClkThermalSensor sensor, bool pretty);

View File

@@ -32,6 +32,7 @@
#include "process_management.h" #include "process_management.h"
#include "errors.h" #include "errors.h"
#include "ipc_service.h" #include "ipc_service.h"
#include "emc_patcher.h"
#define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0)) #define HOSPPC_HAS_BOOST (hosversionAtLeast(7,0,0))
@@ -236,26 +237,40 @@ void ClockManager::Tick()
std::uint32_t nearestHz = 0; std::uint32_t nearestHz = 0;
std::uint32_t mode = 0; std::uint32_t mode = 0;
AppletOperationMode opMode = appletGetOperationMode(); #define EMC_MAX_VOLTAGE_SAFETY_CHECK 1400000
if(this->config->GetConfigValue(HocClkConfigValue_EMCVdd2VoltageuV) < EMC_MAX_VOLTAGE_SAFETY_CHECK) {
EMCpatcher::set_sd1_voltage((u32)this->config->GetConfigValue(HocClkConfigValue_EMCVdd2VoltageuV));
} else {
ResetToStockClocks(); // Clean up after boost mode
}
AppletOperationMode opMode = appletGetOperationMode(); // Used to get if docked or handheld
Result rc = apmExtGetCurrentPerformanceConfiguration(&mode); Result rc = apmExtGetCurrentPerformanceConfiguration(&mode);
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration"); ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
if(this->config->GetConfigValue(HocClkConfigValue_HandheldTDP) && opMode == AppletOperationMode_Handheld) { if(this->config->GetConfigValue(HocClkConfigValue_HandheldTDP) && opMode == AppletOperationMode_Handheld) {
if(Board::GetSocType() == SysClkSocType_MarikoLite) { if(Board::GetSocType() == SysClkSocType_MarikoLite) {
if(Board::GetPowerMw(SysClkPowerSensor_Now) < -(int)this->config->GetConfigValue(HocClkConfigValue_LiteTDPLimit)) { if(Board::GetPowerMw(SysClkPowerSensor_Avg) < -(int)this->config->GetConfigValue(HocClkConfigValue_LiteTDPLimit)) {
ResetToStockClocks(); ResetToStockClocks();
return; return;
} }
} else { } else {
if(Board::GetPowerMw(SysClkPowerSensor_Now) < -(int)this->config->GetConfigValue(HocClkConfigValue_HandheldTDPLimit)) { if(Board::GetPowerMw(SysClkPowerSensor_Avg) < -(int)this->config->GetConfigValue(HocClkConfigValue_HandheldTDPLimit)) {
ResetToStockClocks(); ResetToStockClocks();
return; return;
} }
} }
} }
if(apmExtIsBoostMode(mode) && !this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode)) { if(apmExtIsBoostMode(mode) && !this->config->GetConfigValue(HocClkConfigValue_OverwriteBoostMode)) { // Stock boost mode
ResetToStockClocks(); // ResetToStockClocks();
if(Board::GetSocType() == SysClkSocType_MarikoLite || Board::GetSocType() == SysClkSocType_Mariko) {
Board::SetHz((SysClkModule)SysClkModule_CPU, (u32)this->config->GetConfigValue(HocClkConfigValue_MarikoBoostClock) * 1000000);
} else {
Board::SetHz((SysClkModule)SysClkModule_CPU, (u32)this->config->GetConfigValue(HocClkConfigValue_EristaBoostClock) * 1000000);
}
return; return;
} }
@@ -443,3 +458,51 @@ void ClockManager::SetRNXRTMode(ReverseNXMode mode)
{ {
this->rnxSync->SetRTMode(mode); this->rnxSync->SetRTMode(mode);
} }
// void ClockManager::set_sd1_voltage(uint32_t voltage_uv)
// {
// // SD1 parameters
// const u32 uv_step = 12500;
// const u32 uv_min = 600000;
// const u32 uv_max = 1237500;
// const u8 volt_addr = 0x17; // MAX77620_REG_SD1
// const u8 volt_mask = 0x7F; // MAX77620_SD1_VOLT_MASK
// // Validate input voltage
// if (voltage_uv < uv_min || voltage_uv > uv_max)
// return;
// // Calculate voltage multiplier
// u32 mult = (voltage_uv + uv_step - 1 - uv_min) / uv_step;
// mult = mult & volt_mask;
// // Open I2C session to MAX77620 PMIC
// I2cSession session;
// Result res = i2cOpenSession(&session, I2cDevice_Max77620Pmic);
// if (R_FAILED(res)) {
// return;
// }
// // Read current register value
// u8 current_val = 0;
// res = i2csessionSendAuto(&session, &volt_addr, 1, I2cTransactionOption_Start);
// if (R_FAILED(res)) {
// i2csessionClose(&session);
// return;
// }
// res = i2csessionReceiveAuto(&session, &current_val, 1, I2cTransactionOption_Stop);
// if (R_FAILED(res)) {
// i2csessionClose(&session);
// return;
// }
// // Mask in the new voltage bits, preserving other bits
// u8 new_val = (current_val & ~volt_mask) | mult;
// // Write back register with START and STOP conditions
// u8 write_buf[2] = {volt_addr, new_val};
// res = i2csessionSendAuto(&session, write_buf, sizeof(write_buf), I2cTransactionOption_All);
// i2csessionClose(&session);
// }

View File

@@ -62,7 +62,7 @@ class ClockManager
std::uint32_t count; std::uint32_t count;
std::uint32_t list[SYSCLK_FREQ_LIST_MAX]; std::uint32_t list[SYSCLK_FREQ_LIST_MAX];
} freqTable[SysClkModule_EnumMax]; } freqTable[SysClkModule_EnumMax];
// void set_sd1_voltage(uint32_t uv);
protected: protected:
bool IsAssignableHz(SysClkModule module, std::uint32_t hz); bool IsAssignableHz(SysClkModule module, std::uint32_t hz);
std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile); std::uint32_t GetMaxAllowedHz(SysClkModule module, SysClkProfile profile);

View File

@@ -1,7 +1,26 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <switch.h>
#include "emc_patcher.h" #include "emc_patcher.h"
#include "file_utils.h" #include "file_utils.h"
#include "board.h" #include "board.h"
#include "i2c.h"
#include "maxXXXXX.h"
#define MC_BASE 0x70019000 #define MC_BASE 0x70019000
#define EMC_BASE 0x7001B000 #define EMC_BASE 0x7001B000
@@ -60,18 +79,19 @@ void EMCpatcher::Run()
{ {
std::scoped_lock lock{this->patcherMutex}; std::scoped_lock lock{this->patcherMutex};
this->config->Refresh(); this->config->Refresh();
this->ApplyEMCPatch(); // this->ApplyEMCPatch();
this->I2CApplyVoltage();
} }
void EMCpatcher::ApplyEMCPatch() void EMCpatcher::ApplyEMCPatch()
{ {
if(HOSSVC_HAS_MM) { // only for 10.0.0+, older versions need rewrites if(HOSSVC_HAS_MM) { // only for 10.0.0+, older versions need rewrites
u64 mc_virt_addr = 0; // u64 mc_virt_addr = 0;
u64 mc_out_size = 0; // u64 mc_out_size = 0;
u64 emc_virt_addr = 0; // u64 emc_virt_addr = 0;
u64 emc_out_size = 0; // u64 emc_out_size = 0;
Result rc; // Result rc;
// rc = svcQueryMemoryMapping(&mc_virt_addr, &mc_out_size, MC_BASE, MC_EMC_BASE_SIZE); // map mc // rc = svcQueryMemoryMapping(&mc_virt_addr, &mc_out_size, MC_BASE, MC_EMC_BASE_SIZE); // map mc
// ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping"); // ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping");
@@ -79,9 +99,9 @@ void EMCpatcher::ApplyEMCPatch()
// rc = svcQueryMemoryMapping(&emc_virt_addr, &emc_out_size, EMC_BASE, MC_EMC_BASE_SIZE); // map emc // rc = svcQueryMemoryMapping(&emc_virt_addr, &emc_out_size, EMC_BASE, MC_EMC_BASE_SIZE); // map emc
// ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping"); // ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping");
write_reg64(EMC_BASE, EMC_RAS_0, 1); // write_reg64(EMC_BASE, EMC_RAS_0, 1);
write_reg64(EMC_BASE, EMC_TIMING_CONTROL_0, 0x1); // apply shadow regs // write_reg64(EMC_BASE, EMC_TIMING_CONTROL_0, 0x1); // apply shadow regs
// svcUnmapMemory((void *)mc_virt_addr, (void *)MC_BASE, MC_EMC_BASE_SIZE); // clean up // svcUnmapMemory((void *)mc_virt_addr, (void *)MC_BASE, MC_EMC_BASE_SIZE); // clean up
@@ -89,3 +109,68 @@ void EMCpatcher::ApplyEMCPatch()
} }
} }
#define EMC_MAX_VOLTAGE_SAFETY_CHECK 1350000
#define SD3_UV_MIN 600000
#define SD3_UV_STEP 12500
#define I2C_DEVICE(bus, addr) ((I2cDevice)(((bus) << 8) | (addr)))
#define I2C_5 5
#define MAX77620_I2C_ADDR 0x3C
#define I2cDevice_MAX77620 I2C_DEVICE(I2C_5, MAX77620_I2C_ADDR)
void EMCpatcher::I2CApplyVoltage()
{
if(this->config->GetConfigValue(HocClkConfigValue_EMCVdd2VoltageuV) < EMC_MAX_VOLTAGE_SAFETY_CHECK) {
set_sd1_voltage((u32)this->config->GetConfigValue(HocClkConfigValue_EMCVdd2VoltageuV));
}
}
void EMCpatcher::set_sd1_voltage(uint32_t voltage_uv)
{
// SD1 parameters
const u32 uv_step = 12500;
const u32 uv_min = 600000;
const u32 uv_max = 1237500;
const u8 volt_addr = 0x17; // MAX77620_REG_SD1
const u8 volt_mask = 0x7F; // MAX77620_SD1_VOLT_MASK
// Validate input voltage
if (voltage_uv < uv_min || voltage_uv > uv_max)
return;
// Calculate voltage multiplier
u32 mult = (voltage_uv + uv_step - 1 - uv_min) / uv_step;
mult = mult & volt_mask;
// Open I2C session to MAX77620 PMIC
I2cSession session;
Result res = i2cOpenSession(&session, I2cDevice_Max77620Pmic);
if (R_FAILED(res)) {
return;
}
// Read current register value
u8 current_val = 0;
res = i2csessionSendAuto(&session, &volt_addr, 1, I2cTransactionOption_Start);
if (R_FAILED(res)) {
i2csessionClose(&session);
return;
}
res = i2csessionReceiveAuto(&session, &current_val, 1, I2cTransactionOption_Stop);
if (R_FAILED(res)) {
i2csessionClose(&session);
return;
}
// Mask in the new voltage bits, preserving other bits
u8 new_val = (current_val & ~volt_mask) | mult;
// Write back register with START and STOP conditions
u8 write_buf[2] = {volt_addr, new_val};
res = i2csessionSendAuto(&session, write_buf, sizeof(write_buf), I2cTransactionOption_All);
i2csessionClose(&session);
}

View File

@@ -1,3 +1,20 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once #pragma once
#include <mutex> #include <mutex>
@@ -67,4 +84,6 @@ public:
void Run(); void Run();
void ApplyEMCPatch(); void ApplyEMCPatch();
void I2CApplyVoltage();
static void set_sd1_voltage(uint32_t uv);
}; };

View File

@@ -1,3 +1,20 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "fancontrol.h" #include "fancontrol.h"
#include "tmp451.h" #include "tmp451.h"

View File

@@ -1,3 +1,20 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once #pragma once
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -1,3 +1,20 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef I2C_H #ifndef I2C_H
#define I2C_H #define I2C_H

View File

@@ -1,3 +1,20 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "integrations.h" #include "integrations.h"
ReverseNXSync::ReverseNXSync() ReverseNXSync::ReverseNXSync()

View File

@@ -1,3 +1,20 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once #pragma once
#include <atomic> #include <atomic>
#include <cstdio> #include <cstdio>

View File

@@ -191,7 +191,7 @@ Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8*
return ipcSrv->SetReverseNXRTMode(mode); return ipcSrv->SetReverseNXRTMode(mode);
} }
break; break;
case HocClkIpcCmd_UpdateEMCRegs: // Trigger, not data case HocClkIpcCmd_UpdateEMC: // Trigger, not data
return ipcSrv->PatchEmcRegs(); return ipcSrv->PatchEmcRegs();
break; break;
} }
@@ -354,6 +354,6 @@ Result IpcService::SetReverseNXRTMode(ReverseNXMode mode) {
Result IpcService::PatchEmcRegs() { Result IpcService::PatchEmcRegs() {
EMCpatcher::GetInstance()->Run(); // EMCpatcher::GetInstance()->Run();
return 0; return 0;
} }

View File

@@ -38,6 +38,9 @@
#include "clock_manager.h" #include "clock_manager.h"
#include "ipc_service.h" #include "ipc_service.h"
#include "fancontrol.h" #include "fancontrol.h"
#include "emc_patcher.h"
#include "pwm_dimming.h"
#define INNER_HEAP_SIZE 0x30000 #define INNER_HEAP_SIZE 0x30000
extern "C" extern "C"
@@ -113,17 +116,25 @@ int main(int argc, char** argv)
{ {
Board::Initialize(); Board::Initialize();
ProcessManagement::Initialize(); ProcessManagement::Initialize();
PWMDimmer::Initialize();
ProcessManagement::WaitForQLaunch(); ProcessManagement::WaitForQLaunch();
ClockManager* clockMgr = new ClockManager(); ClockManager* clockMgr = new ClockManager();
IpcService* ipcSrv = new IpcService(clockMgr); IpcService* ipcSrv = new IpcService(clockMgr);
EMCpatcher* emcPatcher = new EMCpatcher();
PWMDimmer* pwmDimmer = PWMDimmer::GetInstance();
FileUtils::LogLine("Ready"); FileUtils::LogLine("Ready");
clockMgr->SetRunning(true); clockMgr->SetRunning(true);
clockMgr->GetConfig()->SetEnabled(true); clockMgr->GetConfig()->SetEnabled(true);
ipcSrv->SetRunning(true); ipcSrv->SetRunning(true);
pwmDimmer->Initialize();
emcPatcher->GetConfig()->SetEnabled(true);
emcPatcher->Run();
pwmDimmer->Start();
TemperaturePoint *table; TemperaturePoint *table;
ReadConfigFile(&table); ReadConfigFile(&table);
InitFanController(table); InitFanController(table);

View File

@@ -0,0 +1,691 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
/*
* Defining registers address and its bit definitions of MAX77620 and MAX20024
*
* Copyright (c) 2019-2020 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <switch.h>
#ifndef MAX77XXX_H
#define MAX77XXX_H
#define MAX17050_BOARD_CGAIN 2 /* Actual: 1.99993 */
#define MAX17050_BOARD_SNS_RESISTOR_UOHM 5000 /* 0.005 Ohm */
#define MAX17050_STATUS_BattAbsent BIT(3)
/* Consider RepCap which is less then 10 units below FullCAP full */
#define MAX17050_FULL_THRESHOLD 10
#define MAX17050_CHARACTERIZATION_DATA_SIZE 48
#define MAXIM17050_I2C_ADDR 0x36
enum MAX17050_reg {
MAX17050_STATUS = 0x00,
MAX17050_VALRT_Th = 0x01,
MAX17050_TALRT_Th = 0x02,
MAX17050_SALRT_Th = 0x03,
MAX17050_AtRate = 0x04,
MAX17050_RepCap = 0x05,
MAX17050_RepSOC = 0x06,
MAX17050_Age = 0x07,
MAX17050_TEMP = 0x08,
MAX17050_VCELL = 0x09,
MAX17050_Current = 0x0A,
MAX17050_AvgCurrent = 0x0B,
MAX17050_SOC = 0x0D,
MAX17050_AvSOC = 0x0E,
MAX17050_RemCap = 0x0F,
MAX17050_FullCAP = 0x10,
MAX17050_TTE = 0x11,
MAX17050_QRTbl00 = 0x12,
MAX17050_FullSOCThr = 0x13,
MAX17050_RSLOW = 0x14,
MAX17050_AvgTA = 0x16,
MAX17050_Cycles = 0x17,
MAX17050_DesignCap = 0x18,
MAX17050_AvgVCELL = 0x19,
MAX17050_MinMaxTemp = 0x1A,
MAX17050_MinMaxVolt = 0x1B,
MAX17050_MinMaxCurr = 0x1C,
MAX17050_CONFIG = 0x1D,
MAX17050_ICHGTerm = 0x1E,
MAX17050_AvCap = 0x1F,
MAX17050_ManName = 0x20,
MAX17050_DevName = 0x21,
MAX17050_QRTbl10 = 0x22,
MAX17050_FullCAPNom = 0x23,
MAX17050_TempNom = 0x24,
MAX17050_TempLim = 0x25,
MAX17050_TempHot = 0x26,
MAX17050_AIN = 0x27,
MAX17050_LearnCFG = 0x28,
MAX17050_FilterCFG = 0x29,
MAX17050_RelaxCFG = 0x2A,
MAX17050_MiscCFG = 0x2B,
MAX17050_TGAIN = 0x2C,
MAX17050_TOFF = 0x2D,
MAX17050_CGAIN = 0x2E,
MAX17050_COFF = 0x2F,
MAX17050_QRTbl20 = 0x32,
MAX17050_SOC_empty = 0x33,
MAX17050_T_empty = 0x34,
MAX17050_FullCAP0 = 0x35,
MAX17050_LAvg_empty = 0x36,
MAX17050_FCTC = 0x37,
MAX17050_RCOMP0 = 0x38,
MAX17050_TempCo = 0x39,
MAX17050_V_empty = 0x3A,
MAX17050_K_empty0 = 0x3B,
MAX17050_TaskPeriod = 0x3C,
MAX17050_FSTAT = 0x3D,
MAX17050_TIMER = 0x3E,
MAX17050_SHDNTIMER = 0x3F,
MAX17050_QRTbl30 = 0x42,
MAX17050_dQacc = 0x45,
MAX17050_dPacc = 0x46,
MAX17050_VFSOC0 = 0x48,
Max17050_QH0 = 0x4C,
MAX17050_QH = 0x4D,
MAX17050_QL = 0x4E,
MAX17050_MinVolt = 0x50, // Custom ID. Not to be sent to i2c.
MAX17050_MaxVolt = 0x51, // Custom ID. Not to be sent to i2c.
MAX17050_VFSOC0Enable = 0x60,
MAX17050_MODELEnable1 = 0x62,
MAX17050_MODELEnable2 = 0x63,
MAX17050_MODELChrTbl = 0x80,
MAX17050_OCV = 0xEE,
MAX17050_OCVInternal = 0xFB,
MAX17050_VFSOC = 0xFF,
};
#define MAX77620_I2C_ADDR 0x3C
/* GLOBAL, PMIC, GPIO, FPS, ONOFFC, CID Registers */
#define MAX77620_REG_CNFGGLBL1 0x00
#define MAX77620_CNFGGLBL1_LBRSTEN BIT(0)
#define MAX77620_CNFGGLBL1_LBDAC_MASK 0x0E
#define MAX77620_CNFGGLBL1_LBDAC_2700 (0 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_2800 (1 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_2900 (2 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3000 (3 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3100 (4 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3200 (5 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3300 (6 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3400 (7 << 1)
#define MAX77620_CNFGGLBL1_LBHYST_100 (0 << 4)
#define MAX77620_CNFGGLBL1_LBHYST_200 (1 << 4)
#define MAX77620_CNFGGLBL1_LBHYST_300 (2 << 4)
#define MAX77620_CNFGGLBL1_LBHYST_400 (3 << 4)
#define MAX77620_CNFGGLBL1_MPPLD BIT(6)
#define MAX77620_CNFGGLBL1_LBDAC_EN BIT(7)
#define MAX77620_REG_CNFGGLBL2 0x01
#define MAX77620_TWD_MASK 0x3
#define MAX77620_TWD_2s 0x0
#define MAX77620_TWD_16s 0x1
#define MAX77620_TWD_64s 0x2
#define MAX77620_TWD_128s 0x3
#define MAX77620_WDTEN BIT(2)
#define MAX77620_WDTSLPC BIT(3)
#define MAX77620_WDTOFFC BIT(4)
#define MAX77620_GLBL_LPM BIT(5)
#define MAX77620_I2CTWD_MASK 0xC0
#define MAX77620_I2CTWD_DISABLED 0x00
#define MAX77620_I2CTWD_1_33ms 0x40
#define MAX77620_I2CTWD_35_7ms 0x80
#define MAX77620_I2CTWD_41_7ms 0xC0
#define MAX77620_REG_CNFGGLBL3 0x02
#define MAX77620_WDTC_MASK 0x3
#define MAX77620_REG_CNFG1_32K 0x03
#define MAX77620_CNFG1_PWR_MD_32K_MASK 0x3
#define MAX77620_CNFG1_32K_OUT0_EN BIT(2)
#define MAX77620_CNFG1_32KLOAD_MASK 0x30
#define MAX77620_CNFG1_32K_OK BIT(7)
#define MAX77620_REG_CNFGBBC 0x04
#define MAX77620_CNFGBBC_ENABLE BIT(0)
#define MAX77620_CNFGBBC_CURRENT_MASK 0x06
#define MAX77620_CNFGBBC_CURRENT_SHIFT 1
#define MAX77620_CNFGBBC_VOLTAGE_MASK 0x18
#define MAX77620_CNFGBBC_VOLTAGE_SHIFT 3
#define MAX77620_CNFGBBC_LOW_CURRENT_DISABLE BIT(5)
#define MAX77620_CNFGBBC_RESISTOR_MASK 0xC0
#define MAX77620_CNFGBBC_RESISTOR_SHIFT 6
#define MAX77620_CNFGBBC_RESISTOR_100 (0 << MAX77620_CNFGBBC_RESISTOR_SHIFT)
#define MAX77620_CNFGBBC_RESISTOR_1K (1 << MAX77620_CNFGBBC_RESISTOR_SHIFT)
#define MAX77620_CNFGBBC_RESISTOR_3K (2 << MAX77620_CNFGBBC_RESISTOR_SHIFT)
#define MAX77620_CNFGBBC_RESISTOR_6K (3 << MAX77620_CNFGBBC_RESISTOR_SHIFT)
#define MAX77620_REG_IRQTOP 0x05
#define MAX77620_REG_IRQTOPM 0x0D
#define MAX77620_IRQ_TOP_ONOFF_MASK BIT(1)
#define MAX77620_IRQ_TOP_32K_MASK BIT(2)
#define MAX77620_IRQ_TOP_RTC_MASK BIT(3)
#define MAX77620_IRQ_TOP_GPIO_MASK BIT(4)
#define MAX77620_IRQ_TOP_LDO_MASK BIT(5)
#define MAX77620_IRQ_TOP_SD_MASK BIT(6)
#define MAX77620_IRQ_TOP_GLBL_MASK BIT(7)
#define MAX77620_REG_INTLBT 0x06
#define MAX77620_REG_INTENLBT 0x0E
#define MAX77620_IRQ_GLBLM_MASK BIT(0)
#define MAX77620_IRQ_TJALRM2_MASK BIT(1)
#define MAX77620_IRQ_TJALRM1_MASK BIT(2)
#define MAX77620_IRQ_LBM_MASK BIT(3)
#define MAX77620_REG_IRQSD 0x07
#define MAX77620_REG_IRQMASKSD 0x0F
#define MAX77620_IRQSD_PFI_SD3 BIT(4)
#define MAX77620_IRQSD_PFI_SD2 BIT(5)
#define MAX77620_IRQSD_PFI_SD1 BIT(6)
#define MAX77620_IRQSD_PFI_SD0 BIT(7)
#define MAX77620_REG_IRQ_LVL2_L0_7 0x08 // LDO number that irq occurred.
#define MAX77620_REG_IRQ_MSK_L0_7 0x10
#define MAX77620_REG_IRQ_LVL2_L8 \
0x09 // LDO number that irq occurred. Only bit0: LDO8 is valid.
#define MAX77620_REG_IRQ_MSK_L8 0x11
#define MAX77620_REG_IRQ_LVL2_GPIO 0x0A // Edge detection interrupt.
#define MAX77620_REG_ONOFFIRQ 0x0B
#define MAX77620_REG_ONOFFIRQM 0x12
#define MAX77620_ONOFFIRQ_MRWRN BIT(0)
#define MAX77620_ONOFFIRQ_EN0_1SEC BIT(1)
#define MAX77620_ONOFFIRQ_EN0_F BIT(2)
#define MAX77620_ONOFFIRQ_EN0_R BIT(3)
#define MAX77620_ONOFFIRQ_LID_F BIT(4)
#define MAX77620_ONOFFIRQ_LID_R BIT(5)
#define MAX77620_ONOFFIRQ_ACOK_F BIT(6)
#define MAX77620_ONOFFIRQ_ACOK_R BIT(7)
#define MAX77620_REG_NVERC 0x0C // Shutdown reason (non-volatile).
#define MAX77620_NVERC_SHDN BIT(0)
#define MAX77620_NVERC_WTCHDG BIT(1)
#define MAX77620_NVERC_HDRST BIT(2)
#define MAX77620_NVERC_TOVLD BIT(3)
#define MAX77620_NVERC_MBLSD BIT(4)
#define MAX77620_NVERC_MBO BIT(5)
#define MAX77620_NVERC_MBU BIT(6)
#define MAX77620_NVERC_RSTIN BIT(7)
#define MAX77620_REG_STATLBT 0x13
#define MAX77620_REG_STATSD 0x14
#define MAX77620_REG_ONOFFSTAT 0x15
#define MAX77620_ONOFFSTAT_LID BIT(0)
#define MAX77620_ONOFFSTAT_ACOK BIT(1)
#define MAX77620_ONOFFSTAT_EN0 BIT(2)
/* SD and LDO Registers */
#define MAX77620_REG_SD0 0x16
#define MAX77620_REG_SD1 0x17
#define MAX77620_REG_SD2 0x18
#define MAX77620_REG_SD3 0x19
#define MAX77620_REG_SD4 0x1A
#define MAX77620_REG_DVSSD0 0x1B
#define MAX77620_REG_DVSSD1 0x1C
#define MAX77620_SDX_VOLT_MASK 0xFF
#define MAX77620_SD0_VOLT_MASK 0x7F // Max is 0x40.
#define MAX77620_SD1_VOLT_MASK 0x7F // Max is 0x4C.
#define MAX77620_LDO_VOLT_MASK 0x3F
#define MAX77620_REG_SD0_CFG 0x1D
#define MAX77620_REG_SD1_CFG 0x1E
#define MAX77620_REG_SD2_CFG 0x1F
#define MAX77620_REG_SD3_CFG 0x20
#define MAX77620_REG_SD4_CFG 0x21
#define MAX77620_SD_SR_MASK 0xC0
#define MAX77620_SD_SR_SHIFT 6
#define MAX77620_SD_POWER_MODE_MASK 0x30
#define MAX77620_SD_POWER_MODE_SHIFT 4
#define MAX77620_SD_CFG1_ADE_MASK BIT(3)
#define MAX77620_SD_CFG1_ADE_DISABLE 0
#define MAX77620_SD_CFG1_ADE_ENABLE BIT(3)
#define MAX77620_SD_FPWM_MASK 0x04
#define MAX77620_SD_FPWM_SHIFT 2
#define MAX77620_SD_FSRADE_MASK 0x01
#define MAX77620_SD_FSRADE_SHIFT 0
#define MAX77620_SD_CFG1_FPWM_SD_MASK BIT(2)
#define MAX77620_SD_CFG1_FPWM_SD_SKIP 0
#define MAX77620_SD_CFG1_FPWM_SD_FPWM BIT(2)
#define MAX77620_SD_CFG1_MPOK_MASK BIT(1)
#define MAX77620_SD_CFG1_FSRADE_SD_MASK BIT(0)
#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0
#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE BIT(0)
#define MAX77620_REG_SD_CFG2 0x22
#define MAX77620_SD_CNF2_RSVD BIT(0)
#define MAX77620_SD_CNF2_ROVS_EN_SD1 BIT(1)
#define MAX77620_SD_CNF2_ROVS_EN_SD0 BIT(2)
#define MAX77620_REG_LDO0_CFG 0x23
#define MAX77620_REG_LDO0_CFG2 0x24
#define MAX77620_REG_LDO1_CFG 0x25
#define MAX77620_REG_LDO1_CFG2 0x26
#define MAX77620_REG_LDO2_CFG 0x27
#define MAX77620_REG_LDO2_CFG2 0x28
#define MAX77620_REG_LDO3_CFG 0x29
#define MAX77620_REG_LDO3_CFG2 0x2A
#define MAX77620_REG_LDO4_CFG 0x2B
#define MAX77620_REG_LDO4_CFG2 0x2C
#define MAX77620_REG_LDO5_CFG 0x2D
#define MAX77620_REG_LDO5_CFG2 0x2E
#define MAX77620_REG_LDO6_CFG 0x2F
#define MAX77620_REG_LDO6_CFG2 0x30
#define MAX77620_REG_LDO7_CFG 0x31
#define MAX77620_REG_LDO7_CFG2 0x32
#define MAX77620_REG_LDO8_CFG 0x33
#define MAX77620_REG_LDO8_CFG2 0x34
/*! LDO CFG */
#define MAX77620_LDO_POWER_MODE_SHIFT 6
#define MAX77620_LDO_POWER_MODE_MASK (3 << MAX77620_LDO_POWER_MODE_SHIFT)
#define MAX77620_POWER_MODE_NORMAL 3
#define MAX77620_POWER_MODE_LPM 2
#define MAX77620_POWER_MODE_GLPM 1
#define MAX77620_POWER_MODE_DISABLE 0
/*! LDO CFG2 */
#define MAX77620_LDO_CFG2_SS_MASK (1 << 0)
#define MAX77620_LDO_CFG2_SS_FAST (0 << 0)
#define MAX77620_LDO_CFG2_SS_SLOW (1 << 0)
#define MAX77620_LDO_CFG2_ADE_MASK (1 << 1)
#define MAX77620_LDO_CFG2_ADE_DISABLE (0 << 1)
#define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1)
#define MAX77620_LDO_CFG2_MPOK_MASK BIT(2)
#define MAX77620_LDO_CFG2_POK_MASK BIT(3)
#define MAX77620_LDO_CFG2_COMP_SHIFT 4
#define MAX77620_LDO_CFG2_COMP_MASK (3 << MAX77620_LDO_COMP_SHIFT)
#define MAX77620_LDO_CFG2_COMP_SLOW 3
#define MAX77620_LDO_CFG2_COMP_MID_SLOW 2
#define MAX77620_LDO_CFG2_COMP_MID_FAST 1
#define MAX77620_LDO_CFG2_COMP_FAST 0
#define MAX77620_LDO_CFG2_ALPM_EN_MASK BIT(6)
#define MAX77620_LDO_CFG2_OVCLMP_MASK BIT(7)
#define MAX77620_REG_LDO_CFG3 0x35
#define MAX77620_LDO_BIAS_EN BIT(0)
#define MAX77620_TRACK4_SHIFT 5
#define MAX77620_TRACK4_MASK (1 << MAX77620_TRACK4_SHIFT)
#define MAX77620_REG_GPIO0 0x36
#define MAX77620_REG_GPIO1 0x37
#define MAX77620_REG_GPIO2 0x38
#define MAX77620_REG_GPIO3 0x39
#define MAX77620_REG_GPIO4 0x3A
#define MAX77620_REG_GPIO5 0x3B
#define MAX77620_REG_GPIO6 0x3C
#define MAX77620_REG_GPIO7 0x3D
#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0)
#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0)
#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN (0 << 0)
#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1)
#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1)
#define MAX77620_CNFG_GPIO_DIR_OUTPUT (0 << 1)
#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW (0 << 3)
#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4)
#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4)
#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5)
#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6)
#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6)
#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6)
#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6)
#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6)
#define MAX77620_GPIO_OUTPUT_DISABLE 0
#define MAX77620_GPIO_OUTPUT_ENABLE 1
#define MAX77620_REG_PUE_GPIO 0x3E // Gpio Pullup resistor enable.
#define MAX77620_REG_PDE_GPIO 0x3F // Gpio Pulldown resistor enable.
#define MAX77620_REG_AME_GPIO \
0x40 // Gpio pinmuxing. Clear bits are Standard GPIO.
#define MAX77620_REG_ONOFFCNFG1 0x41
#define MAX20024_ONOFFCNFG1_CLRSE 0x18
#define MAX77620_ONOFFCNFG1_PWR_OFF BIT(1)
#define MAX77620_ONOFFCNFG1_SLPEN BIT(2)
#define MAX77620_ONOFFCNFG1_MRT_SHIFT 0x3
#define MAX77620_ONOFFCNFG1_MRT_MASK 0x38
#define MAX77620_ONOFFCNFG1_RSVD BIT(6)
#define MAX77620_ONOFFCNFG1_SFT_RST BIT(7)
#define MAX77620_REG_ONOFFCNFG2 0x42
#define MAX77620_ONOFFCNFG2_WK_EN0 BIT(0)
#define MAX77620_ONOFFCNFG2_WK_ALARM2 BIT(1)
#define MAX77620_ONOFFCNFG2_WK_ALARM1 BIT(2)
#define MAX77620_ONOFFCNFG2_WK_MBATT \
BIT(3) // MBATT event generates a wakeup signal. use it in android/l4t?
#define MAX77620_ONOFFCNFG2_WK_ACOK BIT(4)
#define MAX77620_ONOFFCNFG2_SLP_LPM_MSK BIT(5)
#define MAX77620_ONOFFCNFG2_WD_RST_WK BIT(6)
#define MAX77620_ONOFFCNFG2_SFT_RST_WK BIT(7)
/* FPS Registers */
#define MAX77620_REG_FPS_CFG0 0x43 // FPS0.
#define MAX77620_REG_FPS_CFG1 0x44 // FPS1.
#define MAX77620_REG_FPS_CFG2 0x45 // FPS2.
#define MAX77620_FPS_ENFPS_SW_MASK 0x01
#define MAX77620_FPS_ENFPS_SW 0x01
#define MAX77620_FPS_EN_SRC_SHIFT 1
#define MAX77620_FPS_EN_SRC_MASK 0x06
#define MAX77620_FPS_TIME_PERIOD_SHIFT 3
#define MAX77620_FPS_TIME_PERIOD_MASK 0x38
#define MAX77620_REG_FPS_LDO0 0x46
#define MAX77620_REG_FPS_LDO1 0x47
#define MAX77620_REG_FPS_LDO2 0x48
#define MAX77620_REG_FPS_LDO3 0x49
#define MAX77620_REG_FPS_LDO4 0x4A
#define MAX77620_REG_FPS_LDO5 0x4B
#define MAX77620_REG_FPS_LDO6 0x4C
#define MAX77620_REG_FPS_LDO7 0x4D
#define MAX77620_REG_FPS_LDO8 0x4E
#define MAX77620_REG_FPS_SD0 0x4F
#define MAX77620_REG_FPS_SD1 0x50
#define MAX77620_REG_FPS_SD2 0x51
#define MAX77620_REG_FPS_SD3 0x52
#define MAX77620_REG_FPS_SD4 0x53
#define MAX77620_REG_FPS_GPIO1 0x54
#define MAX77620_REG_FPS_GPIO2 0x55
#define MAX77620_REG_FPS_GPIO3 0x56
#define MAX77620_REG_FPS_RSO 0x57
#define MAX77620_FPS_PD_PERIOD_SHIFT 0
#define MAX77620_FPS_PD_PERIOD_MASK 0x07
#define MAX77620_FPS_PU_PERIOD_SHIFT 3
#define MAX77620_FPS_PU_PERIOD_MASK 0x38
#define MAX77620_FPS_SRC_SHIFT 6
#define MAX77620_FPS_SRC_MASK 0xC0
#define MAX77620_FPS_COUNT 3
#define MAX77620_FPS_PERIOD_MIN_US 40
#define MAX77620_FPS_PERIOD_MAX_US 2560
#define MAX77620_REG_CID0 0x58
#define MAX77620_REG_CID1 0x59
#define MAX77620_REG_CID2 0x5A
#define MAX77620_REG_CID3 0x5B
#define MAX77620_REG_CID4 0x5C // OTP version.
#define MAX77620_REG_CID5 0x5D // ES version.
#define MAX77620_CID_DIDO_MASK 0xF
#define MAX77620_CID_DIDO_SHIFT 0
#define MAX77620_CID_DIDM_MASK 0xF0
#define MAX77620_CID_DIDM_SHIFT 4
/* Device Identification Metal */
#define MAX77620_CID5_DIDM(n) (((n) >> 4) & 0xF)
/* Device Indentification OTP */
#define MAX77620_CID5_DIDO(n) ((n) & 0xF)
#define MAX77620_REG_DVSSD4 0x5E
#define MAX20024_REG_MAX_ADD 0x70
#define MAX77620_IRQ_LVL2_GPIO_EDGE0 BIT(0)
#define MAX77620_IRQ_LVL2_GPIO_EDGE1 BIT(1)
#define MAX77620_IRQ_LVL2_GPIO_EDGE2 BIT(2)
#define MAX77620_IRQ_LVL2_GPIO_EDGE3 BIT(3)
#define MAX77620_IRQ_LVL2_GPIO_EDGE4 BIT(4)
#define MAX77620_IRQ_LVL2_GPIO_EDGE5 BIT(5)
#define MAX77620_IRQ_LVL2_GPIO_EDGE6 BIT(6)
#define MAX77620_IRQ_LVL2_GPIO_EDGE7 BIT(7)
/* Interrupts */
enum {
MAX77620_IRQ_TOP_GLBL, /* Low-Battery */
MAX77620_IRQ_TOP_SD, /* SD power fail */
MAX77620_IRQ_TOP_LDO, /* LDO power fail */
MAX77620_IRQ_TOP_GPIO, /* TOP GPIO internal int to MAX77620 */
MAX77620_IRQ_TOP_RTC, /* RTC */
MAX77620_IRQ_TOP_32K, /* 32kHz oscillator */
MAX77620_IRQ_TOP_ONOFF, /* ON/OFF oscillator */
MAX77620_IRQ_LBT_MBATLOW, /* Thermal alarm status, > 120C */
MAX77620_IRQ_LBT_TJALRM1, /* Thermal alarm status, > 120C */
MAX77620_IRQ_LBT_TJALRM2, /* Thermal alarm status, > 140C */
};
/* GPIOs */
enum {
MAX77620_GPIO0,
MAX77620_GPIO1,
MAX77620_GPIO2,
MAX77620_GPIO3,
MAX77620_GPIO4,
MAX77620_GPIO5,
MAX77620_GPIO6,
MAX77620_GPIO7,
MAX77620_GPIO_NR,
};
/* FPS Source */
enum max77620_fps_src {
MAX77620_FPS_SRC_0,
MAX77620_FPS_SRC_1,
MAX77620_FPS_SRC_2,
MAX77620_FPS_SRC_NONE,
MAX77620_FPS_SRC_DEF,
};
#define MAX77812_PHASE31_CPU_I2C_ADDR \
0x31 // High power GPU. 2 Outputs: 3-phase M1 + 1-phase M4.
#define MAX77812_PHASE211_CPU_I2C_ADDR \
0x33 // Low power GPU. 3 Outputs: 2-phase M1 + 1-phase M3 + 1-phase M4.
#define MAX77812_REG_RSET 0x00
#define MAX77812_REG_INT_SRC 0x01
#define MAX77812_REG_INT_SRC_M 0x02
#define MAX77812_REG_TOPSYS_INT 0x03
#define MAX77812_REG_TOPSYS_INT_M 0x04
#define MAX77812_REG_TOPSYS_STAT 0x05
#define MAX77812_REG_EN_CTRL 0x06
#define MAX77812_EN_CTRL_ENABLE 1
#define MAX77812_EN_CTRL_EN_M1_SHIFT 0
#define MAX77812_EN_CTRL_EN_M1_MASK (1 << MAX77812_EN_CTRL_EN_M1_SHIFT)
#define MAX77812_EN_CTRL_EN_M2_SHIFT 2
#define MAX77812_EN_CTRL_EN_M2_MASK (1 << MAX77812_EN_CTRL_EN_M2_SHIFT)
#define MAX77812_EN_CTRL_EN_M3_SHIFT 4
#define MAX77812_EN_CTRL_EN_M3_MASK (1 << MAX77812_EN_CTRL_EN_M3_SHIFT)
#define MAX77812_EN_CTRL_EN_M4_SHIFT 6
#define MAX77812_EN_CTRL_EN_M4_MASK (1 << MAX77812_EN_CTRL_EN_M4_SHIFT)
#define MAX77812_REG_STUP_DLY2 0x07
#define MAX77812_REG_STUP_DLY3 0x08
#define MAX77812_REG_STUP_DLY4 0x09
#define MAX77812_REG_SHDN_DLY1 0x0A
#define MAX77812_REG_SHDN_DLY2 0x0B
#define MAX77812_REG_SHDN_DLY3 0x0C
#define MAX77812_REG_SHDN_DLY4 0x0D
#define MAX77812_REG_WDTRSTB_DEB 0x0E
#define MAX77812_REG_GPI_FUNC 0x0F
#define MAX77812_REG_GPI_DEB1 0x10
#define MAX77812_REG_GPI_DEB2 0x11
#define MAX77812_REG_GPI_PD_CTRL 0x12
#define MAX77812_REG_PROT_CFG 0x13
#define MAX77812_REG_VERSION 0x14
#define MAX77812_REG_I2C_CFG 0x15
#define MAX77812_REG_BUCK_INT 0x20
#define MAX77812_REG_BUCK_INT_M 0x21
#define MAX77812_REG_BUCK_STAT 0x22
#define MAX77812_REG_M1_VOUT 0x23 // GPU.
#define MAX77812_REG_M2_VOUT 0x24
#define MAX77812_REG_M3_VOUT 0x25 // DRAM on PHASE211.
#define MAX77812_REG_M4_VOUT 0x26 // CPU.
#define MAX77812_REG_M1_VOUT_D 0x27
#define MAX77812_REG_M2_VOUT_D 0x28
#define MAX77812_REG_M3_VOUT_D 0x29
#define MAX77812_REG_M4_VOUT_D 0x2A
#define MAX77812_REG_M1_VOUT_S 0x2B
#define MAX77812_REG_M2_VOUT_S 0x2C
#define MAX77812_REG_M3_VOUT_S 0x2D
#define MAX77812_REG_M4_VOUT_S 0x2E
#define MAX77812_REG_M1_CFG 0x2F // HOS: M1_ILIM - 7.2A/4.8A.
#define MAX77812_REG_M2_CFG 0x30 // HOS: M2_ILIM - 7.2A/4.8A.
#define MAX77812_REG_M3_CFG 0x31 // HOS: M3_ILIM - 7.2A/4.8A.
#define MAX77812_REG_M4_CFG 0x32 // HOS: M4_ILIM - 7.2A/4.8A.
#define MAX77812_REG_GLB_CFG1 0x33 // HOS: B_SD_SR/B_SS_SR - 5mV/us.
#define MAX77812_REG_GLB_CFG2 0x34 // HOS: B_RD_SR/B_RU_SR - 5mV/us
#define MAX77812_REG_GLB_CFG3 0x35
/*! Protected area and settings only for MAX77812_ES2_VERSION */
#define MAX77812_REG_GLB_CFG4 0x36 // QS: 0xBB.
#define MAX77812_REG_GLB_CFG5 0x37 // QS: 0x39. ES2: Set to 0x3E.
#define MAX77812_REG_GLB_CFG6 0x38 // QS: 0x88. ES2: Set to 0x90.
#define MAX77812_REG_GLB_CFG7 0x39 // QS: 0x04.
#define MAX77812_REG_GLB_CFG8 0x3A // QS: 0x3A. ES2: Set to 0x3A.
#define MAX77812_REG_PROT_ACCESS 0xFD // 0x00: Lock, 0x5A: Unlock.
#define MAX77812_REG_UNKNOWN 0xFE
#define MAX77812_REG_EN_CTRL_MASK(n) BIT(n)
#define MAX77812_START_SLEW_RATE_MASK 0x07
#define MAX77812_SHDN_SLEW_RATE_MASK 0x70
#define MAX77812_RAMPUP_SLEW_RATE_MASK 0x07
#define MAX77812_RAMPDOWN_SLEW_RATE_MASK 0x70
#define MAX77812_SLEW_RATE_SHIFT 4
#define MAX77812_OP_ACTIVE_DISCHARGE_MASK BIT(7)
#define MAX77812_PEAK_CURRENT_LMT_MASK 0x70
#define MAX77812_SWITCH_FREQ_MASK 0x0C
#define MAX77812_FORCED_PWM_MASK BIT(1)
#define MAX77812_SLEW_RATE_CNTRL_MASK BIT(0)
#define MAX77812_START_SHD_DELAY_MASK 0x1F
#define MAX77812_VERSION_MASK 0x07
#define MAX77812_ES2_VERSION 0x04
#define MAX77812_QS_VERSION 0x05
#define MAX77812_BUCK_VOLT_MASK 0xFF
#define BQ24193_I2C_ADDR 0x6B
// REG 0 masks.
#define BQ24193_INCONFIG_INLIMIT_MASK (7 << 0)
#define BQ24193_INCONFIG_VINDPM_MASK 0x78
#define BQ24193_INCONFIG_HIZ_EN_MASK (1 << 7)
// REG 1 masks.
#define BQ24193_PORCONFIG_BOOST_MASK (1 << 0)
#define BQ24193_PORCONFIG_SYSMIN_MASK (7 << 1) // 3000uV HOS default.
#define BQ24193_PORCONFIG_CHGCONFIG_MASK (3 << 4)
#define BQ24193_PORCONFIG_CHGCONFIG_CHARGER_EN (1 << 4)
#define BQ24193_PORCONFIG_I2CWATCHDOG_MASK (1 << 6)
#define BQ24193_PORCONFIG_RESET_MASK (1 << 7)
// REG 2 masks.
#define BQ24193_CHRGCURR_20PCT_MASK (1 << 0)
#define BQ24193_CHRGCURR_ICHG_MASK 0xFC
// REG 3 masks.
#define BQ24193_PRECHRG_ITERM 0x0F
#define BQ24193_PRECHRG_IPRECHG 0xF0
// REG 4 masks.
#define BQ24193_CHRGVOLT_VTHRES (1 << 0)
#define BQ24193_CHRGVOLT_BATTLOW (1 << 1)
#define BQ24193_CHRGVOLT_VREG 0xFC
// REG 5 masks.
#define BQ24193_CHRGTERM_ISET_MASK (1 << 0)
#define BQ24193_CHRGTERM_CHGTIMER_MASK (3 << 1)
#define BQ24193_CHRGTERM_ENTIMER_MASK (1 << 3)
#define BQ24193_CHRGTERM_WATCHDOG_MASK (3 << 4)
#define BQ24193_CHRGTERM_TERM_ST_MASK (1 << 6)
#define BQ24193_CHRGTERM_TERM_EN_MASK (1 << 7)
// REG 6 masks.
#define BQ24193_IRTHERMAL_THERM_MASK (3 << 0)
#define BQ24193_IRTHERMAL_VCLAMP_MASK (7 << 2)
#define BQ24193_IRTHERMAL_BATTCOMP_MASK (7 << 5)
// REG 7 masks.
#define BQ24193_MISC_INT_MASK (3 << 0)
#define BQ24193_MISC_VSET_MASK (1 << 4)
#define BQ24193_MISC_BATFET_DI_MASK (1 << 5)
#define BQ24193_MISC_TMR2X_EN_MASK (1 << 6)
#define BQ24193_MISC_DPDM_EN_MASK (1 << 7)
// REG 8 masks.
#define BQ24193_STATUS_VSYS_MASK (1 << 0)
#define BQ24193_STATUS_THERM_MASK (1 << 1)
#define BQ24193_STATUS_PG_MASK (1 << 2)
#define BQ24193_STATUS_DPM_MASK (1 << 3)
#define BQ24193_STATUS_CHRG_MASK (3 << 4)
#define BQ24193_STATUS_VBUS_MASK (3 << 6)
// REG 9 masks.
#define BQ24193_FAULT_THERM_MASK (7 << 0)
#define BQ24193_FAULT_BATT_OVP_MASK (1 << 3)
#define BQ24193_FAULT_CHARGE_MASK (3 << 4)
#define BQ24193_FAULT_BOOST_MASK (1 << 6)
#define BQ24193_FAULT_WATCHDOG_MASK (1 << 7)
// REG A masks.
#define BQ24193_VENDORPART_DEV_MASK (3 << 0)
#define BQ24193_VENDORPART_PN_MASK (7 << 3)
enum BQ24193_reg {
BQ24193_InputSource = 0x00,
BQ24193_PORConfig = 0x01,
BQ24193_ChrgCurr = 0x02,
BQ24193_PreChrgTerm = 0x03,
BQ24193_ChrgVolt = 0x04,
BQ24193_ChrgTermTimer = 0x05,
BQ24193_IRCompThermal = 0x06,
BQ24193_Misc = 0x07,
BQ24193_Status = 0x08,
BQ24193_FaultReg = 0x09,
BQ24193_VendorPart = 0x0A,
};
enum BQ24193_reg_prop {
BQ24193_InputVoltageLimit, // REG 0.
BQ24193_InputCurrentLimit, // REG 0.
BQ24193_SystemMinimumVoltage, // REG 1.
BQ24193_FastChargeCurrentLimit, // REG 2.
BQ24193_ChargeVoltageLimit, // REG 4.
BQ24193_RechargeThreshold, // REG 4.
BQ24193_ThermalRegulation, // REG 6.
BQ24193_ChargeStatus, // REG 8.
BQ24193_TempStatus, // REG 9.
BQ24193_DevID, // REG A.
BQ24193_ProductNumber, // REG A.
};
#endif /* MAX77XXX_H */

View File

@@ -0,0 +1,121 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <cstdint>
#include <mutex>
#include <switch.h>
#include "config.h"
#include "errors.h"
#include "file_utils.h"
#include "pwm_dimming.h"
#define PWM_DIMMING_HZ_RATE 5e3 // 5KHz PWM dimming, should be decent for most people
#define DUTY_CYCLE_LEGNTH_NS (1e9 / (double)(PWM_DIMMING_HZ_RATE))
float initial_brightness = 1.0f;
float current_brightness = 1.0f;
Thread pwmThread;
PWMDimmer *PWMDimmer::instance = nullptr;
PWMDimmer *PWMDimmer::GetInstance()
{
return instance;
}
void PWMDimmer::Initialize()
{
if (!instance) {
instance = new PWMDimmer();
FileUtils::LogLine("[pwmDim] Initialized PWMDimmer");
}
}
Config *PWMDimmer::GetConfig()
{
return this->config;
}
void PWMDimmer::Exit()
{
if (instance) {
FileUtils::LogLine("[pwmDim] Exiting PWMDimmer");
delete instance;
instance = nullptr;
}
}
PWMDimmer::PWMDimmer()
{
this->config = Config::CreateDefault();
}
PWMDimmer::~PWMDimmer()
{
delete this->config;
}
void PWMDimmer::Start()
{
std::scoped_lock lock{ this->patcherMutex };
this->config->Refresh();
this->StartPWMDimming();
u64 sku = 0;
Result rc = splInitialize();
ASSERT_RESULT_OK(rc, "splInitialize");
rc = splGetConfig(SplConfigItem_HardwareType, &sku);
ASSERT_RESULT_OK(rc, "splGetConfig");
splExit();
if (sku == HocClkConsoleType_OLED && this->config->GetConfigValue(HocClkConfigValue_PWMDimming)) {
lblGetCurrentBrightnessSetting(&initial_brightness);
Result rc =
threadCreate(&pwmThread, ThreadEntry, this, NULL, 0x4000, 0x2B, -2);
if (R_FAILED(rc)) {
return;
}
rc = threadStart(&pwmThread);
if (R_FAILED(rc)) {
threadClose(&pwmThread);
return;
}
}
}
void PWMDimmer::ThreadEntry(void *arg)
{
auto *self = static_cast<PWMDimmer *>(arg);
self->StartPWMDimming();
}
void PWMDimmer::StartPWMDimming()
{
Result rc = lblInitialize();
if (R_FAILED(rc)) {
return;
}
for (;;) {
lblGetCurrentBrightnessSetting(&current_brightness);
lblSetCurrentBrightnessSetting(current_brightness);
svcSleepThread(DUTY_CYCLE_LEGNTH_NS / 2);
lblSetCurrentBrightnessSetting(0.0f);
svcSleepThread(DUTY_CYCLE_LEGNTH_NS / 2);
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include <mutex>
#include <cstdint>
#include "config.h"
#include "errors.h"
class PWMDimmer
{
public:
static PWMDimmer* GetInstance();
static void Initialize();
Config *GetConfig();
static void Exit();
PWMDimmer();
~PWMDimmer();
static void ThreadEntry(void* arg);
void Start();
void StartPWMDimming();
private:
static PWMDimmer* instance;
Config* config;
std::mutex patcherMutex;
};