- [Sys-clk-OC overlay] Report info on battery current flow, battery cycle (might be useless, reset after each reboot)

- Remove InfoNX as voltage is not reported accurate
This commit is contained in:
KazushiM
2022-01-01 01:01:50 +08:00
parent 4b0bd74e80
commit 82fccb99e1
13 changed files with 109 additions and 731 deletions

View File

@@ -1,11 +0,0 @@
build/
*.ovl
*.elf
*.nacp
*.nro
*.DS_Store

View File

@@ -1,208 +0,0 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
# NO_NACP: if set to anything, no .nacp file is generated.
# APP_TITLE is the name of the app stored in the .nacp file (Optional)
# APP_AUTHOR is the author of the app stored in the .nacp file (Optional)
# APP_VERSION is the version of the app stored in the .nacp file (Optional)
# APP_TITLEID is the titleID of the app stored in the .nacp file (Optional)
# ICON is the filename of the icon (.jpg), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
APP_TITLE := InfoNX
APP_VERSION := 1.0.1
TARGET := InfoNX-ovl
BUILD := build
SOURCES := source
DATA := data
INCLUDES := libs/libtesla/include
NO_ICON := 1
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -Os -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ -DAPP_VERSION="\"$(APP_VERSION)\""
CXXFLAGS := $(CFLAGS) -fno-exceptions -std=c++20
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
ifneq (,$(findstring $(TARGET).jpg,$(icons)))
export APP_ICON := $(TOPDIR)/$(TARGET).jpg
else
ifneq (,$(findstring icon.jpg,$(icons)))
export APP_ICON := $(TOPDIR)/icon.jpg
endif
endif
else
export APP_ICON := $(TOPDIR)/$(ICON)
endif
ifeq ($(strip $(NO_ICON)),)
export NROFLAGS += --icon=$(APP_ICON)
endif
ifeq ($(strip $(NO_NACP)),)
export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp
endif
ifneq ($(APP_TITLEID),)
export NACPFLAGS += --titleid=$(APP_TITLEID)
endif
ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@rm -fr $(BUILD) $(TARGET).ovl $(TARGET).nro $(TARGET).nacp $(TARGET).elf
#---------------------------------------------------------------------------------
else
.PHONY: all
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).ovl
$(OUTPUT).ovl : $(OUTPUT).elf $(OUTPUT).nacp
@elf2nro $< $@ $(NROFLAGS)
@echo "built ... $(notdir $(OUTPUT).ovl)"
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@@ -1,411 +0,0 @@
#define TESLA_INIT_IMPL // If you have more than one file using the tesla header, only define this in the main one
#include <tesla.hpp> // The Tesla Header
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <switch.h>
#include "rgltr.h"
#define IS_BAT_CHARGE_ON ((_batteryChargeInfoFields->unk_x14 >> 8) & 1)
#define IS_SLOW_CHARGE_ON (_batteryChargeInfoFields->ChargeCurrentLimit < 1024)
static Service g_rgltrSrv;
Result rgltrInitialize(void) {
if(hosversionBefore(8,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return smGetService(&g_rgltrSrv, "rgltr");
}
void rgltrExit(void) {
serviceClose(&g_rgltrSrv);
}
Service* rgltrGetServiceSession(void) {
return &g_rgltrSrv;
}
Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id) {
const u32 in = module_id;
return serviceDispatchIn(&g_rgltrSrv, 0, in,
.out_num_objects = 1,
.out_objects = &session_out->s,
);
}
Result rgltrGetPowerModuleNumLimit(u32 *out) {
return serviceDispatchOut(&g_rgltrSrv, 3, *out);
}
void rgltrCloseSession(RgltrSession* session) {
serviceClose(&session->s);
}
Result rgltrGetVoltageEnabled(RgltrSession* session, u32 *out) {
return serviceDispatchOut(&session->s, 2, *out);
}
Result rgltrGetVoltage(RgltrSession* session, u32 *out_volt) {
return serviceDispatchOut(&session->s, 4, *out_volt);
}
///* Notes VoltageAvg
//
// Vavg time = 175.8ms x 2^(6+VOLT), default: VOLT = 2 (Vavg time = 45s)
//
///End of Notes
extern "C" bool is_mariko();
extern "C" void clk_check();
typedef struct {
u32 cpu_out_hz;
u32 gpu_out_hz;
u32 emc_out_hz;
u32 cpu_out_volt;
u32 gpu_out_volt;
u32 emc_out_volt;
} ClkFields;
bool is_mariko() {
u64 hardware_type = 0;
splInitialize();
splGetConfig(SplConfigItem_HardwareType, &hardware_type);
splExit();
switch(hardware_type) {
case 0: //Icosa
case 1: //Copper
return false;
case 2: //Hoag
case 3: //Iowa
case 4: //Calcio
case 5: //Aula
return true;
default:
return false;
}
}
void clk_check(ClkFields *out, bool mariko) {
int res = 0;
ClkrstSession clkrstSession;
RgltrSession rgltrSession;
res = clkrstInitialize();
if(R_FAILED(res)) {
fatalThrow(res);
}
res = rgltrInitialize();
if(R_FAILED(res)) {
fatalThrow(res);
}
rgltrOpenSession(&rgltrSession, mariko ? PcvPowerDomainId_Max77812_Cpu : PcvPowerDomainId_Max77621_Cpu);
clkrstOpenSession(&clkrstSession, PcvModuleId_CpuBus, 3);
rgltrGetVoltage(&rgltrSession, &out->cpu_out_volt);
clkrstGetClockRate(&clkrstSession, &out->cpu_out_hz);
clkrstCloseSession(&clkrstSession);
rgltrCloseSession(&rgltrSession);
rgltrOpenSession(&rgltrSession, mariko ? PcvPowerDomainId_Max77812_Gpu : PcvPowerDomainId_Max77621_Gpu);
clkrstOpenSession(&clkrstSession, PcvModuleId_GPU, 3);
rgltrGetVoltage(&rgltrSession, &out->gpu_out_volt);
clkrstGetClockRate(&clkrstSession, &out->gpu_out_hz);
clkrstCloseSession(&clkrstSession);
rgltrCloseSession(&rgltrSession);
rgltrOpenSession(&rgltrSession, mariko ? PcvPowerDomainId_Max77812_Dram : PcvPowerDomainId_Max77620_Sd1);
clkrstOpenSession(&clkrstSession, PcvModuleId_EMC, 3);
rgltrGetVoltage(&rgltrSession, &out->emc_out_volt);
clkrstGetClockRate(&clkrstSession, &out->emc_out_hz);
clkrstCloseSession(&clkrstSession);
rgltrCloseSession(&rgltrSession);
clkrstExit();
rgltrExit();
}
typedef enum {
NoHub = BIT(0), //If hub is disconnected
Rail = BIT(8), //At least one Joy-con is charging from rail
SPDSRC = BIT(12), //OTG
ACC = BIT(16) //Accessory
} BatteryChargeInfoFieldsFlags;
typedef enum {
NewPDO = 1, //Received new Power Data Object
NoPD = 2, //No Power Delivery source is detected
AcceptedRDO = 3 //Received and accepted Request Data Object
} BatteryChargeInfoFieldsPDControllerState; //BM92T series
const char* strPDControllerState[] = {
"Received new PDO",
"No PD Source",
"Received/Accpted RDO"
};
typedef enum {
None = 0,
PD = 1,
TypeC_1500mA = 2,
TypeC_3000mA = 3,
DCP = 4,
CDP = 5,
SDP = 6,
Apple_500mA = 7,
Apple_1000mA = 8,
Apple_2000mA = 9
} BatteryChargeInfoFieldsChargerType;
const char* strChargerType[] = {
"None",
"PD",
"USB-C@1.5A",
"USB-C@3.0A",
"USB-DCP",
"USB-CDP",
"USB-SDP",
"Apple@0.5A",
"Apple@1.0A",
"Apple@2.0A",
};
typedef enum {
Sink = 1,
Source = 2
} BatteryChargeInfoFieldsPowerRole;
const char* strPowerRole[] = {
"Sink",
"Source",
};
typedef struct {
int32_t InputCurrentLimit; //Input (Sink) current limit in mA
int32_t VBUSCurrentLimit; //Output (Source/VBUS/OTG) current limit in mA
int32_t ChargeCurrentLimit; //Battery charging current limit in mA (512mA when Docked, 768mA when BatteryTemperature < 17.0 C)
int32_t ChargeVoltageLimit; //Battery charging voltage limit in mV (3952mV when BatteryTemperature >= 51.0 C)
int32_t unk_x10; //Possibly an emum, getting the same value as PowerRole in all tested cases
int32_t unk_x14; //Possibly flags
BatteryChargeInfoFieldsPDControllerState PDControllerState; //Power Delivery Controller State
int32_t BatteryTemperature; //Battery temperature in milli C
int32_t RawBatteryCharge; //Raw battery charged capacity per cent-mille (i.e. 100% = 100000 pcm)
int32_t VoltageAvg; //Voltage avg in mV (more in Notes)
int32_t BatteryAge; //Battery age (capacity full / capacity design) per cent-mille (i.e. 100% = 100000 pcm)
BatteryChargeInfoFieldsPowerRole PowerRole;
BatteryChargeInfoFieldsChargerType ChargerType;
int32_t ChargerVoltageLimit; //Charger and external device voltage limit in mV
int32_t ChargerCurrentLimit; //Charger and external device current limit in mA
BatteryChargeInfoFieldsFlags Flags; //Unknown flags
} BatteryChargeInfoFields;
Result psmGetBatteryChargeInfoFields(Service* psmService, BatteryChargeInfoFields *out) {
return serviceDispatchOut(psmService, 17, *out);
}
static Result psmEnableBatteryCharging(Service* psmService) {
return serviceDispatch(psmService, 2);
}
static Result psmDisableBatteryCharging(Service* psmService) {
return serviceDispatch(psmService, 3);
}
static Result psmDisableSlowCharging(Service* psmService) {
return serviceDispatch(psmService, 10);
}
static Result psmEnableSlowCharging(Service* psmService) {
return serviceDispatch(psmService, 11);
}
FanController g_ICon;
float rotationSpeedLevel = 0;
unsigned int changeSlowChargingState = 0;
unsigned int changeDisableChargingState = 0;
bool threadexit = false;
int32_t socTempMili = 0;
char Print_x[800];
Thread t0;
bool IsEnoughPowerSupplied;
void Loop(void*) {
static Service* psmService = psmGetServiceSession();
static BatteryChargeInfoFields* _batteryChargeInfoFields = new BatteryChargeInfoFields;
static ClkFields* _clkFields = new ClkFields;
while (threadexit == false) {
if(changeSlowChargingState)
{
changeSlowChargingState = 0;
if(IS_SLOW_CHARGE_ON)
psmDisableSlowCharging(psmService);
else
psmEnableSlowCharging(psmService);
}
if(changeDisableChargingState)
{
changeDisableChargingState = 0;
if(IS_BAT_CHARGE_ON)
psmDisableBatteryCharging(psmService);
else
psmEnableBatteryCharging(psmService);
}
psmGetBatteryChargeInfoFields(psmService, _batteryChargeInfoFields);
clk_check(_clkFields, is_mariko());
tsGetTemperatureMilliC(TsLocation_External, &socTempMili);
psmIsEnoughPowerSupplied(&IsEnoughPowerSupplied);
fanControllerGetRotationSpeedLevel(&g_ICon, &rotationSpeedLevel);
snprintf(Print_x, sizeof(Print_x),
"Current Limit: %u mA IN, %u mA OUT"
"\nBattery Charg. Limit: %u mA, %u mV"
"\nunk_x10: 0x%08" PRIx32
"\nunk_x14: 0x%08" PRIx32
"\nPD Contr. State: %s (%u)"
"\nBattery Temp.: %.2f\u00B0C"
"\nRaw Battery Charge: %.2f%%"
"\nVoltage Avg: %u mV"
"\nBattery Age: %.2f%%"
"\nPower Role: %s (%u)"
"\nCharger Type: %s (%u)"
"\nCharger Limit: %u mV, %u mA"
"\nunk_x3c: 0x%08" PRIx32
"\nEnough Power Supplied: %s"
"\n"
"\nCPU Clock: %6.1f MHz"
"\nCPU Volt : %6.1f mV\n"
"\nGPU Clock: %6.1f MHz"
"\nGPU Volt : %6.1f mV\n"
"\nEMC Clock: %6.1f MHz"
"\nEMC Volt : %6.1f mV\n"
"\nSoC Temp : %2.2f \u00B0C"
"\nFan Speed: %2.2f %%\n"
"\nL-Stick: Slow Charging(0.5A) (%s)"
"\nR-Stick: Disable Charging (%s)"
,
_batteryChargeInfoFields->InputCurrentLimit,
_batteryChargeInfoFields->VBUSCurrentLimit,
_batteryChargeInfoFields->ChargeCurrentLimit,
_batteryChargeInfoFields->ChargeVoltageLimit,
_batteryChargeInfoFields->unk_x10,
_batteryChargeInfoFields->unk_x14,
strPDControllerState[_batteryChargeInfoFields->PDControllerState], _batteryChargeInfoFields->PDControllerState,
(float)_batteryChargeInfoFields->BatteryTemperature / 1000,
(float)_batteryChargeInfoFields->RawBatteryCharge / 1000,
_batteryChargeInfoFields->VoltageAvg,
(float)_batteryChargeInfoFields->BatteryAge / 1000,
strPowerRole[_batteryChargeInfoFields->PowerRole], _batteryChargeInfoFields->PowerRole,
strChargerType[_batteryChargeInfoFields->ChargerType], _batteryChargeInfoFields->ChargerType,
_batteryChargeInfoFields->ChargerVoltageLimit,
_batteryChargeInfoFields->ChargerCurrentLimit,
(int32_t)_batteryChargeInfoFields->Flags,
IsEnoughPowerSupplied ? "Yes" : "No",
(double)_clkFields->cpu_out_hz / 1000000,
(double)_clkFields->cpu_out_volt / 1000,
(double)_clkFields->gpu_out_hz / 1000000,
(double)_clkFields->gpu_out_volt / 1000,
(double)_clkFields->emc_out_hz / 1000000,
(double)_clkFields->emc_out_volt / 1000,
(float)socTempMili / 1000,
rotationSpeedLevel * 100,
IS_SLOW_CHARGE_ON ? "ON" : "OFF",
!IS_BAT_CHARGE_ON ? "ON" : "OFF"
);
svcSleepThread(800'000'000);
}
delete _batteryChargeInfoFields;
delete _clkFields;
}
class GuiTest : public tsl::Gui {
public:
GuiTest(u8 arg1, u8 arg2, bool arg3) { }
// Called when this Gui gets loaded to create the UI
// Allocate all elements on the heap. libtesla will make sure to clean them up when not needed anymore
virtual tsl::elm::Element* createUI() override {
// A OverlayFrame is the base element every overlay consists of. This will draw the default Title and Subtitle.
// If you need more information in the header or want to change it's look, use a HeaderOverlayFrame.
auto frame = new tsl::elm::OverlayFrame("InfoNX", APP_VERSION);
// A list that can contain sub elements and handles scrolling
auto list = new tsl::elm::List();
list->addItem(new tsl::elm::CustomDrawer([](tsl::gfx::Renderer *renderer, s32 x, s32 y, s32 w, s32 h) {
renderer->drawString(Print_x, false, x, y+20, 15, renderer->a(0xFFFF));
}), 500);
// Add the list to the frame for it to be drawn
frame->setContent(list);
// Return the frame to have it become the top level element of this Gui
return frame;
}
// Called once every frame to update values
virtual void update() override {}
// Called once every frame to handle inputs not handled by other UI elements
virtual bool handleInput(u64 keysDown, u64 keysHeld, const HidTouchState &touchPos, HidAnalogStickState joyStickPosLeft, HidAnalogStickState joyStickPosRight) override {
if (keysHeld & HidNpadButton_A) {
tsl::hlp::requestForeground(false);
return true;
}
if (keysHeld & HidNpadButton_StickL) {
changeSlowChargingState++;
return true;
}
if (keysHeld & HidNpadButton_StickR) {
changeDisableChargingState++;
return true;
}
return false; // Return true here to singal the inputs have been consumed
}
};
class OverlayTest : public tsl::Overlay {
public:
// libtesla already initialized fs, hid, pl, pmdmnt, hid:sys and set:sys
virtual void initServices() override {
smInitialize();
psmInitialize();
tsInitialize();
fanInitialize();
fanOpenController(&g_ICon, 0x3D000001);
threadCreate(&t0, Loop, NULL, NULL, 0x4000, 0x3F, -2);
threadStart(&t0);
} // Called at the start to initialize all services necessary for this Overlay
virtual void exitServices() override {
threadexit = true;
threadWaitForExit(&t0);
threadClose(&t0);
fanControllerClose(&g_ICon);
fanExit();
tsExit();
psmExit();
smExit();
} // Callet at the end to clean up all services previously initialized
virtual void onShow() override {} // Called before overlay wants to change from invisible to visible state
virtual void onHide() override {} // Called before overlay wants to change from visible to invisible state
virtual std::unique_ptr<tsl::Gui> loadInitialGui() override {
return initially<GuiTest>(1, 2, true); // Initial Gui to load. It's possible to pass arguments to it's constructor like this
}
};
int main(int argc, char **argv) {
return tsl::loop<OverlayTest>(argc, argv);
}

View File

@@ -1,41 +0,0 @@
typedef enum {
PcvPowerDomain_Max77620_Sd0 = 0,
PcvPowerDomain_Max77620_Sd1 = 1,
PcvPowerDomain_Max77620_Sd2 = 2,
PcvPowerDomain_Max77620_Sd3 = 3,
PcvPowerDomain_Max77620_Ldo0 = 4,
PcvPowerDomain_Max77620_Ldo1 = 5,
PcvPowerDomain_Max77620_Ldo2 = 6,
PcvPowerDomain_Max77620_Ldo3 = 7,
PcvPowerDomain_Max77620_Ldo4 = 8,
PcvPowerDomain_Max77620_Ldo5 = 9,
PcvPowerDomain_Max77620_Ldo6 = 10,
PcvPowerDomain_Max77620_Ldo7 = 11,
PcvPowerDomain_Max77620_Ldo8 = 12,
PcvPowerDomain_Max77621_Cpu = 13,
PcvPowerDomain_Max77621_Gpu = 14,
PcvPowerDomain_Max77812_Cpu = 15,
PcvPowerDomain_Max77812_Gpu = 16,
PcvPowerDomain_Max77812_Dram = 17,
} PowerDomain;
typedef enum {
PcvPowerDomainId_Max77620_Sd0 = 0x3A000080,
PcvPowerDomainId_Max77620_Sd1 = 0x3A000081,
PcvPowerDomainId_Max77620_Sd2 = 0x3A000082,
PcvPowerDomainId_Max77620_Sd3 = 0x3A000083,
PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0,
PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1,
PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2,
PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3,
PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4,
PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5,
PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6,
PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7,
PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8,
PcvPowerDomainId_Max77621_Cpu = 0x3A000003,
PcvPowerDomainId_Max77621_Gpu = 0x3A000004,
PcvPowerDomainId_Max77812_Cpu = 0x3A000003,
PcvPowerDomainId_Max77812_Gpu = 0x3A000004,
PcvPowerDomainId_Max77812_Dram = 0x3A000005,
} PowerDomainId;

View File

@@ -1,22 +0,0 @@
#pragma once
//#include "../types.h"
//#include "../sf/service.h"
//#include "../services/pcv.h"
#include <switch.h>
#include "pcv_types.h"
typedef struct {
Service s;
} RgltrSession;
Result rgltrInitialize(void);
void rgltrExit(void);
Service* rgltrGetServiceSession(void);
Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id);
void rgltrCloseSession(RgltrSession* session);
Result rgltrGetVoltage(RgltrSession* session, u32 *out_volt);
Result rgltrGetPowerModuleNumLimit(u32 *out);
Result rgltrGetVoltageEnabled(RgltrSession* session, u32 *out);

View File

@@ -384,10 +384,8 @@ int main(int argc, char* argv[])
for (int j = 0; j < testThreads; j++)
testJobId[j] = -1;
ull count = 0;
for (int j = 0; j < testThreads; )
{
count++;
switch (testWorkerReport[j])
{
case 0:

View File

@@ -1,5 +1,4 @@
/*
* --------------------------------------------------------------------------
/* --------------------------------------------------------------------------
* "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
@@ -27,7 +26,7 @@ void MiscGui::preDraw(tsl::gfx::Renderer* render)
{
BaseMenuGui::preDraw(render);
render->drawString(this->psmOutput, false, 40, 300, SMALL_TEXT_SIZE, DESC_COLOR);
render->drawString(this->infoOutput, false, 40, 300, SMALL_TEXT_SIZE, DESC_COLOR);
}
void MiscGui::listUI()
@@ -70,7 +69,7 @@ void MiscGui::update()
{
frameCounter = 0;
PsmUpdate();
PsmGetInfo(this->psmOutput, sizeof(this->psmOutput));
GetInfo(this->infoOutput, sizeof(this->infoOutput));
this->chargingToggle->setState(this->PsmIsCharging());
this->fastChargingToggle->setState(this->PsmIsFastCharging());
}

View File

@@ -1,5 +1,4 @@
/*
* --------------------------------------------------------------------------
/* --------------------------------------------------------------------------
* "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
@@ -102,6 +101,13 @@ class MiscGui : public BaseMenuGui
ChargeInfoFlags Flags; //Unknown flags
} ChargeInfo;
typedef enum
{
Max17050Reg_Current = 0x0A,
Max17050Reg_AvgCurrent = 0x0B,
Max17050Reg_Cycle = 0x17,
} Max17050Reg;
void PsmUpdate(uint32_t dispatchId = 0)
{
smInitialize();
@@ -140,35 +146,114 @@ class MiscGui : public BaseMenuGui
return this->isEnoughPowerSupplied;
}
void PsmGetInfo(char* out, size_t outsize)
Result I2cReadRegHandler(u8 reg, I2cDevice dev, u16 *out)
{
// ams::fatal::srv::StopSoundTask::StopSound()
// I2C Bus Communication Reference: https://www.ti.com/lit/an/slva704/slva704.pdf
struct { u8 reg; } __attribute__((packed)) cmd;
struct { u16 val; } __attribute__((packed)) rec;
I2cSession _session;
Result res = i2cOpenSession(&_session, dev);
if (res)
return res;
cmd.reg = reg;
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
if (res)
{
i2csessionClose(&_session);
return res;
}
res = i2csessionReceiveAuto(&_session, &rec, sizeof(rec), I2cTransactionOption_All);
if (res)
{
i2csessionClose(&_session);
return res;
}
*out = rec.val;
i2csessionClose(&_session);
return 0;
}
bool Max17050ReadReg(u8 reg, u16 *out)
{
u16 data = 0;
Result res = I2cReadRegHandler(reg, I2cDevice_Max17050, &data);
if (res)
{
*out = res;
return false;
}
*out = data;
return true;
}
void GetInfo(char* out, size_t outsize)
{
float chargerVoltLimit = (float)chargeInfo->ChargerVoltageLimit / 1000;
float chargerCurrLimit = (float)chargeInfo->ChargerCurrentLimit / 1000;
float chargerOutWatts = chargerVoltLimit * chargerCurrLimit;
float batCurrent = 0;
float batCycleCount = 0;
char chargeVoltLimit[20] = "";
if (chargeInfo->ChargeVoltageLimit)
snprintf(chargeVoltLimit, sizeof(chargeVoltLimit), ", %u mV", chargeInfo->ChargeVoltageLimit);
// From Hekate, too lazy to query configuration from reg
constexpr float max17050SenseResistor = 5.; // in uOhm
constexpr float max17050CGain = 1.99993;
// Init, read registers and exit I2C service all here to save resources
{
smInitialize();
i2cInitialize();
u16 data = 0;
if (Max17050ReadReg(Max17050Reg_Cycle, &data))
batCycleCount = data / 100.;
if (Max17050ReadReg(Max17050Reg_Current, &data))
batCurrent = (s16)data * (1.5625 / (max17050SenseResistor * max17050CGain));
i2cExit();
smExit();
}
char batWattsInfo[20] = "";
if (std::abs(batCurrent) > 100)
snprintf(batWattsInfo, sizeof(batWattsInfo), " (%+.2f W)", batCurrent * (float)chargeInfo->VoltageAvg / 1000'000);
snprintf(out, outsize,
"Charger: %s %.1fV/%.1fA (%.1fW)"
"\n%s"
"\nBattery: %.3fV %.2f\u00B0C"
"\nCurrent Limit: %u mA IN, %u mA OUT"
"\nCharging Limit: %u mA%s"
"\nRaw Charge: %.2f%%"
"\nBattery Age: %.2f%%"
"\nPower Role: %s"
"%s"
"\nCharger: %s %.1fV/%.1fA (%.1fW)"
"\nBattery: %.3fV %.2f\u00B0C"
"\nCurrent Limit: %u mA IN, %u mA OUT"
"\nCharging Limit: %u mA%s"
"\nRaw Charge: %.2f%%"
"\nBattery Age: %.2f%%"
"\nPower Role: %s"
"\nCycle Count: %.2f (Reset at power-up)"
"\nCurrent Flow: %+.2f mA%s"
,
ChargeInfoChargerTypeToStr(chargeInfo->ChargerType), chargerVoltLimit, chargerCurrLimit, chargerOutWatts,
PsmIsEnoughPowerSupplied() ? "Enough Power Supplied" : "",
ChargeInfoChargerTypeToStr(chargeInfo->ChargerType), chargerVoltLimit, chargerCurrLimit, chargerOutWatts,
(float)chargeInfo->VoltageAvg / 1000,
(float)chargeInfo->BatteryTemperature / 1000,
chargeInfo->InputCurrentLimit, chargeInfo->VBUSCurrentLimit,
chargeInfo->ChargeCurrentLimit, chargeVoltLimit,
(float)chargeInfo->RawBatteryCharge / 1000,
(float)chargeInfo->BatteryAge / 1000,
ChargeInfoPowerRoleToStr(chargeInfo->PowerRole)
ChargeInfoPowerRoleToStr(chargeInfo->PowerRole),
batCycleCount,
batCurrent, batWattsInfo
);
}
@@ -196,6 +281,6 @@ class MiscGui : public BaseMenuGui
ChargeInfo* chargeInfo;
bool isEnoughPowerSupplied = false;
char psmOutput[800] = "";
char infoOutput[800] = "";
int frameCounter = 60;
};