sysclk: code cleanup and fan speed

also fix incompatibility with nx fancontrol
This commit is contained in:
souldbminersmwc
2025-12-20 13:45:45 -05:00
parent b125ce6d84
commit 02a50867b3
21 changed files with 176 additions and 310 deletions

View File

@@ -0,0 +1,260 @@
/*
* Battery Info Driver for Nintendo Switch
* Single-header library for accessing battery information
*
* Usage:
* #define BATTERY_INFO_IMPLEMENTATION
* #include "battery_info.h"
*/
#pragma once
#include <switch.h>
#include <inttypes.h>
#include <string.h>
// Battery charging flags
typedef enum {
BatteryFlag_NoHub = BIT(0), // Hub is disconnected
BatteryFlag_Rail = BIT(8), // At least one Joy-con is charging from rail
BatteryFlag_SPDSRC = BIT(12), // OTG
BatteryFlag_ACC = BIT(16) // Accessory
} BatteryChargeFlags;
// Power Delivery Controller State (BM92T series)
typedef enum {
PDState_NewPDO = 1, // Received new Power Data Object
PDState_NoPD = 2, // No Power Delivery source is detected
PDState_AcceptedRDO = 3 // Received and accepted Request Data Object
} BatteryPDControllerState;
// Charger type detection
typedef enum {
ChargerType_None = 0,
ChargerType_PD = 1,
ChargerType_TypeC_1500mA = 2,
ChargerType_TypeC_3000mA = 3,
ChargerType_DCP = 4, // Dedicated Charging Port
ChargerType_CDP = 5, // Charging Downstream Port
ChargerType_SDP = 6, // Standard Downstream Port
ChargerType_Apple_500mA = 7,
ChargerType_Apple_1000mA = 8,
ChargerType_Apple_2000mA = 9
} BatteryChargerType;
// Power role (USB Power Delivery)
typedef enum {
PowerRole_Sink = 1, // Device is receiving power
PowerRole_Source = 2 // Device is providing power
} BatteryPowerRole;
// Complete battery charge information structure
typedef struct {
int32_t InputCurrentLimit; // Input (Sink) current limit in mA
int32_t VBUSCurrentLimit; // Output (Source/VBUS/OTG) current limit in mA
int32_t ChargeCurrentLimit; // Battery charging current limit in mA
int32_t ChargeVoltageLimit; // Battery charging voltage limit in mV
int32_t unk_x10; // Unknown field (possibly enum)
int32_t unk_x14; // Unknown field (possibly flags)
BatteryPDControllerState PDControllerState; // PD Controller State
int32_t BatteryTemperature; // Battery temperature in milli-Celsius
int32_t RawBatteryCharge; // Battery charge in per cent-mille (100% = 100000)
int32_t VoltageAvg; // Average voltage in mV
int32_t BatteryAge; // Battery health (capacity full/design) in pcm
BatteryPowerRole PowerRole; // Current power role
BatteryChargerType ChargerType; // Type of charger connected
int32_t ChargerVoltageLimit; // Charger voltage limit in mV
int32_t ChargerCurrentLimit; // Charger current limit in mA
BatteryChargeFlags Flags; // Various status flags
} BatteryChargeInfo;
// Helper macro to check if battery charging is enabled
#define IS_BATTERY_CHARGING_ENABLED(info) (((info)->unk_x14 >> 8) & 1)
// Initialize the battery info driver
Result batteryInfoInitialize(void);
// Cleanup the battery info driver
void batteryInfoExit(void);
// Get complete battery charge information
Result batteryInfoGetChargeInfo(BatteryChargeInfo *out);
// Get battery charge percentage (0-100)
Result batteryInfoGetChargePercentage(u32 *out);
// Check if enough power is being supplied
Result batteryInfoIsEnoughPowerSupplied(bool *out);
// Battery charge control functions
Result batteryInfoEnableCharging(void);
Result batteryInfoDisableCharging(void);
Result batteryInfoEnableFastCharging(void);
Result batteryInfoDisableFastCharging(void);
// Helper functions to get human-readable strings
const char* batteryInfoGetChargerTypeString(BatteryChargerType type);
const char* batteryInfoGetPowerRoleString(BatteryPowerRole role);
const char* batteryInfoGetPDStateString(BatteryPDControllerState state);
// Convenience functions for common values
static inline int batteryInfoGetTemperatureMiliCelsius(BatteryChargeInfo *info) {
return info->BatteryTemperature;
}
static inline float batteryInfoGetChargePercent(BatteryChargeInfo *info) {
return (float)info->RawBatteryCharge / 1000.0f;
}
static inline float batteryInfoGetBatteryHealthPercent(BatteryChargeInfo *info) {
return (float)info->BatteryAge / 1000.0f;
}
static inline bool batteryInfoIsCharging(BatteryChargeInfo *info) {
return IS_BATTERY_CHARGING_ENABLED(info);
}
// String lookup tables
static const char* s_chargerTypeStrings[] = {
"None",
"Power Delivery",
"USB-C @ 1.5A",
"USB-C @ 3.0A",
"USB-DCP",
"USB-CDP",
"USB-SDP",
"Apple @ 0.5A",
"Apple @ 1.0A",
"Apple @ 2.0A",
};
static const char* s_powerRoleStrings[] = {
"Unknown",
"Sink",
"Source",
};
static const char* s_pdStateStrings[] = {
"Unknown",
"New PDO Received",
"No PD Source",
"RDO Accepted"
};
// Internal PSM service handle
static Service g_psmService = {0};
static bool g_batteryInfoInitialized = false;
// Internal PSM command implementations
static Result psmGetBatteryChargeInfoFields(BatteryChargeInfo *out) {
if (!g_batteryInfoInitialized)
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
return serviceDispatchOut(&g_psmService, 17, *out);
}
static Result psmEnableBatteryCharging_internal(void) {
if (!g_batteryInfoInitialized)
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
return serviceDispatch(&g_psmService, 2);
}
static Result psmDisableBatteryCharging_internal(void) {
if (!g_batteryInfoInitialized)
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
return serviceDispatch(&g_psmService, 3);
}
static Result psmEnableFastBatteryCharging_internal(void) {
if (!g_batteryInfoInitialized)
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
return serviceDispatch(&g_psmService, 10);
}
static Result psmDisableFastBatteryCharging_internal(void) {
if (!g_batteryInfoInitialized)
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
return serviceDispatch(&g_psmService, 11);
}
// Public API implementations
Result batteryInfoInitialize(void) {
if (g_batteryInfoInitialized)
return 0;
Result rc = psmInitialize();
if (R_SUCCEEDED(rc)) {
memcpy(&g_psmService, psmGetServiceSession(), sizeof(Service));
g_batteryInfoInitialized = true;
}
return rc;
}
void batteryInfoExit(void) {
if (g_batteryInfoInitialized) {
psmExit();
memset(&g_psmService, 0, sizeof(Service));
g_batteryInfoInitialized = false;
}
}
Result batteryInfoGetChargeInfo(BatteryChargeInfo *out) {
if (!out)
return MAKERESULT(Module_Libnx, LibnxError_BadInput);
return psmGetBatteryChargeInfoFields(out);
}
Result batteryInfoGetChargePercentage(u32 *out) {
if (!g_batteryInfoInitialized)
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
return psmGetBatteryChargePercentage(out);
}
Result batteryInfoIsEnoughPowerSupplied(bool *out) {
if (!g_batteryInfoInitialized)
return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
return psmIsEnoughPowerSupplied(out);
}
Result batteryInfoEnableCharging(void) {
return psmEnableBatteryCharging_internal();
}
Result batteryInfoDisableCharging(void) {
return psmDisableBatteryCharging_internal();
}
Result batteryInfoEnableFastCharging(void) {
return psmEnableFastBatteryCharging_internal();
}
Result batteryInfoDisableFastCharging(void) {
return psmDisableFastBatteryCharging_internal();
}
const char* batteryInfoGetChargerTypeString(BatteryChargerType type) {
if (type < 0 || type > ChargerType_Apple_2000mA)
return "Unknown";
return s_chargerTypeStrings[type];
}
const char* batteryInfoGetPowerRoleString(BatteryPowerRole role) {
if (role < PowerRole_Sink || role > PowerRole_Source)
return s_powerRoleStrings[0];
return s_powerRoleStrings[role];
}
const char* batteryInfoGetPDStateString(BatteryPDControllerState state) {
if (state < PDState_NewPDO || state > PDState_AcceptedRDO)
return s_pdStateStrings[0];
return s_pdStateStrings[state];
}

View File

@@ -0,0 +1,57 @@
#pragma once
#include <switch.h>
// To use i2c service, sm and i2c should be intialized via smInitialize() and i2cInitialize().
Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val);
Result I2cRead_OutU8(I2cDevice dev, u8 reg, u8 *out);
Result I2cRead_OutU16(I2cDevice dev, u8 reg, u16 *out);
// Max17050 fuel gauge
float I2c_Max17050_GetBatteryCurrent();
const u8 MAX17050_CURRENT_REG = 0x0A;
// Buck Converter
typedef enum I2c_BuckConverter_Reg {
I2c_Max77620_SD1VOLT_REG = 0x17, // Used for Erista DDR VDDQ+VDD2 / Mariko VDD2
I2c_Max77621_VOLT_REG = 0x00,
I2c_Max77812_CPUVOLT_REG = 0x26,
I2c_Max77812_GPUVOLT_REG = 0x23,
I2c_Max77812_MEMVOLT_REG = 0x25, // Master 3 (GPU 1 + 2, DRAM 3, CPU 4), used for Mariko VDDQ
} I2c_BuckConverter_Reg;
typedef struct I2c_BuckConverter_Domain {
I2cDevice device;
I2c_BuckConverter_Reg reg;
u8 volt_mask;
u32 uv_step;
u32 uv_min;
u32 uv_max;
u8 por_val;
} I2c_BuckConverter_Domain;
const I2c_BuckConverter_Domain I2c_Erista_CPU = { I2cDevice_Max77621Cpu, I2c_Max77621_VOLT_REG, 0x7F, 6250, 606250, 1400000, };
const I2c_BuckConverter_Domain I2c_Erista_GPU = { I2cDevice_Max77621Gpu, I2c_Max77621_VOLT_REG, 0x7F, 6250, 606250, 1400000, };
const I2c_BuckConverter_Domain I2c_Erista_DRAM = { I2cDevice_Max77620Pmic, I2c_Max77620_SD1VOLT_REG, 0x7F, 12500, 600000, 1250000, };
const I2c_BuckConverter_Domain I2c_Mariko_CPU = { I2cDevice_Max77812_2, I2c_Max77812_CPUVOLT_REG, 0xFF, 5000, 250000, 1525000, 0x78 };
const I2c_BuckConverter_Domain I2c_Mariko_GPU = { I2cDevice_Max77812_2, I2c_Max77812_GPUVOLT_REG, 0xFF, 5000, 250000, 1525000, 0x78 };
const I2c_BuckConverter_Domain I2c_Mariko_DRAM_VDDQ = { I2cDevice_Max77812_2, I2c_Max77812_MEMVOLT_REG, 0xFF, 5000, 250000, 700000, 0x78 };
const I2c_BuckConverter_Domain I2c_Mariko_DRAM_VDD2 = { I2cDevice_Max77620Pmic, I2c_Max77620_SD1VOLT_REG, 0x7F, 12500, 600000, 1250000, };
u32 I2c_BuckConverter_GetMvOut(const I2c_BuckConverter_Domain* domain);
Result I2c_BuckConverter_SetMvOut(const I2c_BuckConverter_Domain* domain, u32 mvolt);
// Bq24193 Battery management
u32 I2c_Bq24193_Convert_Raw_mA(u8 raw);
u8 I2c_Bq24193_Convert_mA_Raw(u32 ma);
Result I2c_Bq24193_GetFastChargeCurrentLimit(u32 *ma);
Result I2c_Bq24193_SetFastChargeCurrentLimit(u32 ma);
const u32 MA_RANGE_MIN = 512;
const u32 MA_RANGE_MAX = 4544;
const u8 BQ24193_CHARGE_CURRENT_CONTROL_REG = 0x2;

View File

@@ -0,0 +1,27 @@
#pragma once
#include <string>
#include <ctime>
#include <cstdio>
static void writeNotification(const std::string& message) {
const char* flagPath = "sdmc:/config/ultrahand/flags/NOTIFICATIONS.flag";
FILE* flagFile = fopen(flagPath, "r");
if (!flagFile) {
return;
}
fclose(flagFile);
std::string filename = "Horizon OC -" + std::to_string(std::time(nullptr)) + ".notify";
std::string fullPath = "sdmc:/config/ultrahand/notifications/" + filename;
FILE* file = fopen(fullPath.c_str(), "w");
if (file) {
fprintf(file, "{\n");
fprintf(file, " \"text\": \"%s\",\n", message.c_str());
fprintf(file, " \"fontSize\": 28\n");
fprintf(file, "}\n");
fclose(file);
}
}

View File

@@ -0,0 +1,95 @@
#pragma once
/*
* SDx actual min is 625 mV. Multipliers 0/1 reserved.
* SD0 max is 1400 mV
* SD1 max is 1550 mV
* SD2 max is 3787.5 mV
* SD3 max is 3787.5 mV
*/
/*
* Switch Power domains (max77620):
* Name | Usage | uV step | uV min | uV default | uV max | Init
*-------+---------------+---------+--------+------------+---------+------------------
* sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1)
* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1)
* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv)
* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 |
* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1)
* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv)
* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 |
* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv)
* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | 0.85V (AO, pcv)
* ldo5 | GC Card | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv)
* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V (pcv)
* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | 1.05V (pcv)
* ldo8 | XUSB, DP, MCU | 50000 | 800000 | 1050000 | 2800000 | 1.05V/2.8V (pcv)
*/
// GPIOs T210: 3: 3.3V, 5: CPU PMIC, 6: GPU PMIC, 7: DSI/VI 1.2V powered by ldo0.
/*
* OTP: T210 - T210B01:
* SD0: 1.0V 1.05V - SoC. EN Based on FPSSRC.
* SD1: 1.15V 1.1V - DRAM for T210. EN Based on FPSSRC.
* SD2: 1.35V 1.35V
* SD3: 1.8V 1.8V
* All powered off?
* LDO0: -- -- - Display
* LDO1: 1.05V 1.05V
* LDO2: -- -- - SD
* LDO3: 3.1V 3.1V - GC ASIC
* LDO4: 1.0V 0.8V - Needed for RTC domain on T210.
* LDO5: 3.1V 3.1V
* LDO6: 2.8V 2.9V - Touch.
* LDO7: 1.05V 1.0V
* LDO8: 1.05V 1.0V
*/
/*
* MAX77620_AME_GPIO: control GPIO modes (bits 0 - 7 correspond to GPIO0 - GPIO7); 0 -> GPIO, 1 -> alt-mode
* MAX77620_REG_GPIOx: 0x9 sets output and enable
*/
typedef enum {
PcvPowerDomain_Max77620_Sd0 = 0,
PcvPowerDomain_Max77620_Sd1 = 1,
PcvPowerDomain_Max77620_Sd2 = 2,
PcvPowerDomain_Max77620_Sd3 = 3,
PcvPowerDomain_Max77620_Ldo0 = 4,
PcvPowerDomain_Max77620_Ldo1 = 5,
PcvPowerDomain_Max77620_Ldo2 = 6,
PcvPowerDomain_Max77620_Ldo3 = 7,
PcvPowerDomain_Max77620_Ldo4 = 8,
PcvPowerDomain_Max77620_Ldo5 = 9,
PcvPowerDomain_Max77620_Ldo6 = 10,
PcvPowerDomain_Max77620_Ldo7 = 11,
PcvPowerDomain_Max77620_Ldo8 = 12,
PcvPowerDomain_Max77621_Cpu = 13,
PcvPowerDomain_Max77621_Gpu = 14,
PcvPowerDomain_Max77812_Cpu = 15,
PcvPowerDomain_Max77812_Gpu = 16,
PcvPowerDomain_Max77812_Dram = 17,
} PowerDomain;
typedef enum {
PcvPowerDomainId_Max77620_Sd0 = 0x3A000080,
PcvPowerDomainId_Max77620_Sd1 = 0x3A000081, // vdd2
PcvPowerDomainId_Max77620_Sd2 = 0x3A000082,
PcvPowerDomainId_Max77620_Sd3 = 0x3A000083,
PcvPowerDomainId_Max77620_Ldo0 = 0x3A0000A0,
PcvPowerDomainId_Max77620_Ldo1 = 0x3A0000A1,
PcvPowerDomainId_Max77620_Ldo2 = 0x3A0000A2,
PcvPowerDomainId_Max77620_Ldo3 = 0x3A0000A3,
PcvPowerDomainId_Max77620_Ldo4 = 0x3A0000A4,
PcvPowerDomainId_Max77620_Ldo5 = 0x3A0000A5,
PcvPowerDomainId_Max77620_Ldo6 = 0x3A0000A6,
PcvPowerDomainId_Max77620_Ldo7 = 0x3A0000A7,
PcvPowerDomainId_Max77620_Ldo8 = 0x3A0000A8,
PcvPowerDomainId_Max77621_Cpu = 0x3A000003,
PcvPowerDomainId_Max77621_Gpu = 0x3A000004,
PcvPowerDomainId_Max77812_Cpu = 0x3A000003,
PcvPowerDomainId_Max77812_Gpu = 0x3A000004,
PcvPowerDomainId_Max77812_Dram = 0x3A000005, // vddq
} PowerDomainId;

View File

@@ -0,0 +1,22 @@
#pragma once
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
Service s;
} PwmChannelSession;
Result pwmInitialize(void);
void pwmExit(void);
Service* pwmGetServiceSession(void);
Result pwmOpenSession2(PwmChannelSession *out, u32 device_code);
Result pwmChannelSessionGetDutyCycle(PwmChannelSession *c, double* out);
void pwmChannelSessionClose(PwmChannelSession *c);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -0,0 +1,19 @@
#pragma once
#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

@@ -0,0 +1,15 @@
#pragma once
#include <switch.h> // for Service, Result, hosversionBefore(), smGetService(), serviceClose(), etc.
#include "rgltr.h" // for RgltrSession, PowerDomainId, etc.
extern Service g_rgltrSrv;
Result rgltrInitialize(void);
void rgltrExit(void);
Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id);
Result rgltrGetVoltage(RgltrSession* session, u32* out_volt);
void rgltrCloseSession(RgltrSession* session);

View File

@@ -0,0 +1,65 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <switch/types.h>
#include <switch/result.h>
#include <switch/kernel/mutex.h>
#include <switch/sf/service.h>
#include <switch/services/sm.h>
typedef struct ServiceGuard {
Mutex mutex;
u32 refCount;
} ServiceGuard;
NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g)
{
mutexLock(&g->mutex);
return (g->refCount++) == 0;
}
NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void))
{
if (R_FAILED(rc)) {
cleanupFunc();
--g->refCount;
}
mutexUnlock(&g->mutex);
return rc;
}
NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
{
mutexLock(&g->mutex);
if (g->refCount && (--g->refCount) == 0)
cleanupFunc();
mutexUnlock(&g->mutex);
}
#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
\
static ServiceGuard g_##name##Guard; \
NX_INLINE Result _##name##Initialize _paramdecl; \
static void _##name##Cleanup(void); \
\
Result name##Initialize _paramdecl \
{ \
Result rc = 0; \
if (serviceGuardBeginInit(&g_##name##Guard)) \
rc = _##name##Initialize _parampass; \
return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \
} \
\
void name##Exit(void) \
{ \
serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \
}
#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())
#ifdef __cplusplus
}
#endif

View File

@@ -103,6 +103,7 @@ typedef enum
HocClkPartLoad_GPU,
HocClkPartLoad_CPUAvg,
HocClkPartLoad_BAT,
HocClkPartLoad_FAN,
SysClkPartLoad_EnumMax
} SysClkPartLoad;

View File

@@ -0,0 +1,186 @@
#include "i2c.h"
Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) {
// ams::fatal::srv::StopSoundTask::StopSound()
// I2C Bus Communication Reference: https://www.ti.com/lit/an/slva704/slva704.pdf
struct {
u8 reg;
u8 val;
} __attribute__((packed)) cmd;
I2cSession _session;
Result res = i2cOpenSession(&_session, dev);
if (res)
return res;
cmd.reg = reg;
cmd.val = val;
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
i2csessionClose(&_session);
return res;
}
Result I2cRead_OutU8(I2cDevice dev, u8 reg, u8 *out) {
struct { u8 reg; } __attribute__((packed)) cmd;
struct { u8 val; } __attribute__((packed)) rec;
I2cSession _session;
Result res = i2cOpenSession(&_session, dev);
if (res)
return res;
cmd.reg = reg;
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
if (res) {
i2csessionClose(&_session);
return res;
}
res = i2csessionReceiveAuto(&_session, &rec, sizeof(rec), I2cTransactionOption_All);
i2csessionClose(&_session);
if (res) {
return res;
}
*out = rec.val;
return 0;
}
Result I2cRead_OutU16(I2cDevice dev, u8 reg, u16 *out) {
struct { u8 reg; } __attribute__((packed)) cmd;
struct { u16 val; } __attribute__((packed)) rec;
I2cSession _session;
Result res = i2cOpenSession(&_session, dev);
if (res)
return res;
cmd.reg = reg;
res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All);
if (res) {
i2csessionClose(&_session);
return res;
}
res = i2csessionReceiveAuto(&_session, &rec, sizeof(rec), I2cTransactionOption_All);
i2csessionClose(&_session);
if (res) {
return res;
}
*out = rec.val;
return 0;
}
float I2c_Max17050_GetBatteryCurrent() {
u16 val;
Result res = I2cRead_OutU16(I2cDevice_Max17050, MAX17050_CURRENT_REG, &val);
if (res)
return 0.f;
const float SenseResistor = 5.; // in uOhm
const float CGain = 1.99993;
return (s16)val * (1.5625 / (SenseResistor * CGain));
}
u32 I2c_BuckConverter_MultiplierToMvOut(const I2c_BuckConverter_Domain* domain, u8 multiplier) {
return (domain->uv_min + domain->uv_step * multiplier) / 1000;
}
u8 I2c_BuckConverter_MvOutToMultiplier(const I2c_BuckConverter_Domain* domain, u32 mvolt) {
u32 uvolt = mvolt * 1000;
if (uvolt < domain->uv_min)
uvolt = domain->uv_min;
if (uvolt > domain->uv_max)
uvolt = domain->uv_max;
return (uvolt - domain->uv_min) / domain->uv_step;
}
u32 I2c_BuckConverter_GetMvOut(const I2c_BuckConverter_Domain* domain) {
u8 val;
// Retry 5 times if received POR value
for (int i = 0; i < 5; i++) {
if (R_FAILED(I2cRead_OutU8(domain->device, domain->reg, &val)))
return 0u;
// Wait 1us
svcSleepThread(1E3);
if (!domain->por_val || val != domain->por_val)
break;
}
return I2c_BuckConverter_MultiplierToMvOut(domain, val & domain->volt_mask);
}
Result I2c_BuckConverter_SetMvOut(const I2c_BuckConverter_Domain* domain, u32 mvolt) {
u8 val;
Result res = I2cRead_OutU8(domain->device, domain->reg, &val);
if (R_FAILED(res))
return res;
u8 multiplier = I2c_BuckConverter_MvOutToMultiplier(domain, mvolt);
val &= ~domain->volt_mask;
val |= multiplier & domain->volt_mask;
res = I2cSet_U8(domain->device, domain->reg, val);
if (R_FAILED(res))
return res;
// 5ms Ramp delay
svcSleepThread(5E6);
u8 new_val;
res = I2cRead_OutU8(domain->device, domain->reg, &new_val);
if (R_FAILED(res))
return res;
if (new_val != val)
return -1;
return 0;
}
u8 I2c_Bq24193_Convert_mA_Raw(u32 ma) {
// Adjustment is required
u8 raw = 0;
if (ma > MA_RANGE_MAX) // capping
ma = MA_RANGE_MAX;
bool pct20 = ma <= (MA_RANGE_MIN - 64);
if (pct20) {
ma = ma * 5;
raw |= 0x1;
}
ma -= ma % 100; // round to 100
ma -= (MA_RANGE_MIN - 64); // ceiling
raw |= (ma >> 6) << 2;
return raw;
};
u32 I2c_Bq24193_Convert_Raw_mA(u8 raw) {
// No adjustment is allowed
u32 ma = (((raw >> 2)) << 6) + MA_RANGE_MIN;
bool pct20 = raw & 1;
if (pct20)
ma = ma * 20 / 100;
return ma;
};
Result I2c_Bq24193_GetFastChargeCurrentLimit(u32 *ma) {
u8 raw;
Result res = I2cRead_OutU8(I2cDevice_Bq24193, BQ24193_CHARGE_CURRENT_CONTROL_REG, &raw);
if (res)
return res;
*ma = I2c_Bq24193_Convert_Raw_mA(raw);
return 0;
}
Result I2c_Bq24193_SetFastChargeCurrentLimit(u32 ma) {
u8 raw = I2c_Bq24193_Convert_mA_Raw(ma);
return I2cSet_U8(I2cDevice_Bq24193, BQ24193_CHARGE_CURRENT_CONTROL_REG, raw);
}

View File

@@ -0,0 +1,35 @@
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include <switch.h>
#include "service_guard.h"
#include "pwm.h"
static Service g_pwmSrv;
NX_GENERATE_SERVICE_GUARD(pwm);
Result _pwmInitialize(void) {
return smGetService(&g_pwmSrv, "pwm");
}
void _pwmCleanup(void) {
serviceClose(&g_pwmSrv);
}
Service* pwmGetServiceSession(void) {
return &g_pwmSrv;
}
Result pwmOpenSession2(PwmChannelSession *out, u32 device_code) {
return serviceDispatchIn(&g_pwmSrv, 2, device_code,
.out_num_objects = 1,
.out_objects = &out->s,
);
}
Result pwmChannelSessionGetDutyCycle(PwmChannelSession *c, double* out) {
return serviceDispatchOut(&c->s, 7, *out);
}
void pwmChannelSessionClose(PwmChannelSession *controller) {
serviceClose(&controller->s);
}

View File

@@ -0,0 +1,41 @@
#include <switch.h>
#include "rgltr.h"
#include "rgltr_services.h" // for extern Service g_rgltrSrv, etc.
// Global service handle
Service g_rgltrSrv;
Result rgltrInitialize(void) {
if (hosversionBefore(8, 0, 0)) {
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
}
return smGetService(&g_rgltrSrv, "rgltr");
}
void rgltrExit(void) {
serviceClose(&g_rgltrSrv);
}
Result rgltrOpenSession(RgltrSession* session_out, PowerDomainId module_id) {
const u32 in = (u32)module_id;
return serviceDispatchIn(
&g_rgltrSrv,
0,
in,
.out_num_objects = 1,
.out_objects = &session_out->s
);
}
Result rgltrGetVoltage(RgltrSession* session, u32* out_volt) {
u32 temp = 0;
Result rc = serviceDispatchOut(&session->s, 4, temp);
if (R_SUCCEEDED(rc)) {
*out_volt = temp;
}
return rc;
}
void rgltrCloseSession(RgltrSession* session) {
serviceClose(&session->s);
}