From 28862f69f8c4a5f5aca74072affbe00e2bb49d2e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 9 Nov 2020 23:14:31 -0800 Subject: [PATCH] powctl: implement max17050 custom parameter init --- .../nintendo_nx/powctl_battery_driver.cpp | 61 ++- .../nintendo_nx/powctl_bq24193_driver.hpp | 18 + .../nintendo_nx/powctl_charger_driver.cpp | 6 + .../powctl_max17050_custom_parameters.inc | 200 +++++++++ .../nintendo_nx/powctl_max17050_driver.cpp | 420 ++++++++++++++++++ .../nintendo_nx/powctl_max17050_driver.hpp | 79 ++++ 6 files changed, 780 insertions(+), 4 deletions(-) create mode 100644 libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc create mode 100644 libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp create mode 100644 libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp index 2e3b6dd00..e59436231 100644 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp @@ -14,19 +14,72 @@ * along with this program. If not, see . */ #include +#include "../../powctl_device_management.hpp" +#include "powctl_retry_helper.hpp" #include "powctl_battery_driver.hpp" +#include "powctl_max17050_driver.hpp" namespace ams::powctl::impl::board::nintendo_nx { + namespace { + + constinit std::optional g_battery_device; + + Max17050Driver &GetMax17050Driver() { + static Max17050Driver s_max17050_driver; + return s_max17050_driver; + } + + } + /* Generic API. */ void BatteryDriver::InitializeDriver() { - /* TODO */ - AMS_ABORT(); + /* Initialize gpio library. */ + gpio::Initialize(); + + /* Create battery device. */ + g_battery_device.emplace(this->IsEventHandlerEnabled()); + + /* Initialize the Max17050Driver. */ + { + size_t battery_vendor_size; + char battery_vendor[0x18] = {}; + if (R_FAILED(cal::GetBatteryVendor(std::addressof(battery_vendor_size), battery_vendor, sizeof(battery_vendor)))) { + battery_vendor[7] = 'A'; + battery_vendor_size = 0; + } + + u8 battery_version = 0; + if (R_FAILED(cal::GetBatteryVersion(std::addressof(battery_version)))) { + battery_version = 0; + } + + GetMax17050Driver().Initialize(battery_vendor, battery_version); + } + + /* Register our device. */ + this->RegisterDevice(std::addressof(*g_battery_device)); + + /* Register the charger device's code. */ + R_ABORT_UNLESS(powctl::impl::RegisterDeviceCode(powctl::DeviceCode_Max17050, std::addressof(*g_battery_device))); + } void BatteryDriver::FinalizeDriver() { - /* TODO */ - AMS_ABORT(); + /* Unregister the charger device code. */ + powctl::impl::UnregisterDeviceCode(powctl::DeviceCode_Max17050); + + /* Unregister our device. */ + this->UnregisterDevice(std::addressof(*g_battery_device)); + + /* Finalize Max17050Driver. */ + GetMax17050Driver().Finalize(); + + /* Destroy the charger device. */ + g_battery_device = std::nullopt; + + /* Finalize gpio library. */ + gpio::Finalize(); } Result BatteryDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) { diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp index 5c20aebe6..01d9b906c 100644 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp @@ -50,10 +50,28 @@ namespace ams::powctl::impl::board::nintendo_nx { void Initialize() { std::scoped_lock lk(this->mutex); if ((this->init_count++) == 0) { + /* Initialize i2c library. */ + i2c::InitializeEmpty(); + + /* Open session. */ + R_ABORT_UNLESS(i2c::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Bq24193)); + + /* Initialize session. */ R_ABORT_UNLESS(this->InitializeSession()); } } + void Finalize() { + std::scoped_lock lk(this->mutex); + if ((--this->init_count) == 0) { + /* Close session. */ + i2c::CloseSession(this->i2c_session); + + /* Finalize i2c library. */ + i2c::Finalize(); + } + } + Result SetPreChargeCurrentLimit(int ma); Result SetTerminationCurrentLimit(int ma); diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp index 753f41a2e..3771bbb44 100644 --- a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp @@ -81,6 +81,12 @@ namespace ams::powctl::impl::board::nintendo_nx { /* Destroy the charger device. */ g_charger_device = std::nullopt; + + /* Finalize gpio library. */ + gpio::Finalize(); + + /* Finalize Bq24193Driver. */ + GetBq24193Driver().Finalize(); } Result ChargerDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) { diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc new file mode 100644 index 000000000..03c5a02f5 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* NOTE: This file is auto-generated by max17050_parameters_gen.py, do not edit manually. */ + +constexpr inline const CustomParameters CustomParameters0A = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0053, + .tempco = 0x1C22, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5786, + .qresidual10 = 0x3184, + .qresidual20 = 0x1E00, + .qresidual30 = 0x1502, + .fullcap = 0x2476, + .vffullcap = 0x2476, + .modeltbl = { + 0x9FF0, 0xAD30, 0xB5D0, 0xB9C0, 0xBAD0, 0xBBE0, 0xBC30, 0xBC90, + 0xBCE0, 0xBD40, 0xBE70, 0xC0E0, 0xC4E0, 0xC890, 0xCC90, 0xD0F0, + 0x0170, 0x0480, 0x0590, 0x0BE0, 0x0A00, 0x3C00, 0x3810, 0x3A00, + 0x3A30, 0x19F0, 0x0EF0, 0x0AF0, 0x0BD0, 0x07F0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters0R = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0048, + .tempco = 0x2034, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5A00, + .qresidual10 = 0x3B00, + .qresidual20 = 0x0F80, + .qresidual30 = 0x0B02, + .fullcap = 0x2466, + .vffullcap = 0x2466, + .modeltbl = { + 0x9C50, 0xAD90, 0xB270, 0xB6A0, 0xB8F0, 0xBB10, 0xBC00, 0xBD00, + 0xBD70, 0xBE70, 0xBF50, 0xC1F0, 0xC380, 0xC590, 0xC8E0, 0xD0B0, + 0x00D0, 0x0150, 0x0300, 0x0D00, 0x0E00, 0x1900, 0x2AC0, 0x2830, + 0x1760, 0x18F0, 0x0DF0, 0x0BC0, 0x0DF0, 0x0BF0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters0M = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0085, + .tempco = 0x1625, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x3100, + .qresidual10 = 0x1B00, + .qresidual20 = 0x1000, + .qresidual30 = 0x0C81, + .fullcap = 0x227A, + .vffullcap = 0x227A, + .modeltbl = { + 0xA340, 0xB840, 0xB900, 0xBB70, 0xBC90, 0xBD20, 0xBDC0, 0xBEA0, + 0xBF70, 0xC030, 0xC210, 0xC3F0, 0xC800, 0xC9E0, 0xCCA0, 0xD090, + 0x0160, 0x3800, 0x0800, 0x1E00, 0x2550, 0x3060, 0x15D0, 0x1810, + 0x1490, 0x0B80, 0x0BF0, 0x0AF0, 0x0CB0, 0x06F0, 0x09D0, 0x09D0, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters1 = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0040, + .tempco = 0x1624, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4690, + .qresidual10 = 0x2605, + .qresidual20 = 0x1605, + .qresidual30 = 0x0F05, + .fullcap = 0x1AE4, + .vffullcap = 0x1AE4, + .modeltbl = { + 0x8B50, 0x9C20, 0xACF0, 0xB160, 0xB3A0, 0xB5B0, 0xB950, 0xBBE0, + 0xBDC0, 0xBEF0, 0xC140, 0xC250, 0xC600, 0xC960, 0xCCE0, 0xD060, + 0x0070, 0x00F0, 0x0440, 0x0400, 0x0500, 0x0400, 0x0D00, 0x3270, + 0x0FB0, 0x0AF0, 0x10F0, 0x0CE0, 0x09E0, 0x07F0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1584, +}; + +constexpr inline const CustomParameters CustomParameters2A = { + .relaxcfg = 0x203B, + .rcomp0 = 0x004A, + .tempco = 0x1D23, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4000, + .qresidual10 = 0x1E80, + .qresidual20 = 0x0D83, + .qresidual30 = 0x0783, + .fullcap = 0x1C20, + .vffullcap = 0x1C20, + .modeltbl = { + 0x8040, 0x9A30, 0xB430, 0xB770, 0xBAB0, 0xBBC0, 0xBD00, 0xBE50, + 0xBF70, 0xC0D0, 0xC300, 0xC590, 0xC960, 0xCD40, 0xD1F0, 0xD5C0, + 0x0040, 0x0060, 0x0510, 0x0D30, 0x16C0, 0x2160, 0x1380, 0x1A10, + 0x0EC0, 0x0CE0, 0x08F0, 0x0940, 0x0920, 0x06F0, 0x06C0, 0x06C0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x1680, +}; + +constexpr inline const CustomParameters CustomParameters2R = { + .relaxcfg = 0x203B, + .rcomp0 = 0x004C, + .tempco = 0x2D32, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5900, + .qresidual10 = 0x2900, + .qresidual20 = 0x1100, + .qresidual30 = 0x0B00, + .fullcap = 0x1CCE, + .vffullcap = 0x1CCE, + .modeltbl = { + 0x8E10, 0x9FC0, 0xA880, 0xB750, 0xBA10, 0xBB30, 0xBD20, 0xBE80, + 0xC0A0, 0xC350, 0xC670, 0xC8C0, 0xCCF0, 0xD050, 0xD140, 0xD5F0, + 0x0020, 0x00D0, 0x0200, 0x0E00, 0x1300, 0x1B00, 0x1930, 0x1150, + 0x0BF0, 0x07E0, 0x0AD0, 0x06F0, 0x07F0, 0x0EF0, 0x04F0, 0x04F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x170B, +}; + +constexpr inline const CustomParameters CustomParameters2M = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0049, + .tempco = 0x222A, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4F00, + .qresidual10 = 0x2680, + .qresidual20 = 0x1205, + .qresidual30 = 0x0C87, + .fullcap = 0x1C68, + .vffullcap = 0x1C68, + .modeltbl = { + 0x8E40, 0xB570, 0xB8F0, 0xBB00, 0xBC20, 0xBCC0, 0xBE30, 0xBFE0, + 0xC200, 0xC400, 0xC720, 0xCB50, 0xCF00, 0xD100, 0xD480, 0xD5C0, + 0x00C0, 0x0C00, 0x0A10, 0x1800, 0x2C00, 0x1C10, 0x12D0, 0x09F0, + 0x0AF0, 0x0850, 0x09F0, 0x06F0, 0x06B0, 0x07E0, 0x01D0, 0x01D0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x16B9, +}; + diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp new file mode 100644 index 000000000..61e049084 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "powctl_max17050_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace max17050 { + + constexpr inline u8 Status = 0x00; + constexpr inline u8 VAlrtThreshold = 0x01; + constexpr inline u8 TAlrtThreshold = 0x02; + constexpr inline u8 SocAlrtThreshold = 0x03; + constexpr inline u8 AtRate = 0x04; + constexpr inline u8 RemCapRep = 0x05; + constexpr inline u8 SocRep = 0x06; + constexpr inline u8 Age = 0x07; + constexpr inline u8 Temperature = 0x08; + constexpr inline u8 VCell = 0x09; + constexpr inline u8 Current = 0x0A; + constexpr inline u8 AverageCurrent = 0x0B; + + constexpr inline u8 SocMix = 0x0D; + constexpr inline u8 SocAv = 0x0E; + constexpr inline u8 RemCapMix = 0x0F; + constexpr inline u8 FullCap = 0x10; + constexpr inline u8 Tte = 0x11; + constexpr inline u8 QResidual00 = 0x12; + constexpr inline u8 FullSocThr = 0x13; + + + constexpr inline u8 AverageTemp = 0x16; + constexpr inline u8 Cycles = 0x17; + constexpr inline u8 DesignCap = 0x18; + constexpr inline u8 AverageVCell = 0x19; + constexpr inline u8 MaxMinTemp = 0x1A; + constexpr inline u8 MaxMinVoltage = 0x1B; + constexpr inline u8 MaxMinCurrent = 0x1C; + constexpr inline u8 Config = 0x1D; + constexpr inline u8 IChgTerm = 0x1E; + constexpr inline u8 RemCapAv = 0x1F; + + constexpr inline u8 Version = 0x21; + constexpr inline u8 QResidual10 = 0x22; + constexpr inline u8 FullCapNom = 0x23; + constexpr inline u8 TempNom = 0x24; + constexpr inline u8 TempLim = 0x25; + + constexpr inline u8 Ain = 0x27; + constexpr inline u8 LearnCfg = 0x28; + constexpr inline u8 FilterCfg = 0x29; + constexpr inline u8 RelaxCfg = 0x2A; + constexpr inline u8 MiscCfg = 0x2B; + constexpr inline u8 TGain = 0x2C; + constexpr inline u8 TOff = 0x2D; + constexpr inline u8 CGain = 0x2E; + constexpr inline u8 COff = 0x2F; + + + constexpr inline u8 QResidual20 = 0x32; + + + + constexpr inline u8 IAvgEmpty = 0x36; + constexpr inline u8 FCtc = 0x37; + constexpr inline u8 RComp0 = 0x38; + constexpr inline u8 TempCo = 0x39; + constexpr inline u8 VEmpty = 0x3A; + + + constexpr inline u8 FStat = 0x3D; + constexpr inline u8 Timer = 0x3E; + constexpr inline u8 ShdnTimer = 0x3F; + + + constexpr inline u8 QResidual30 = 0x42; + + + constexpr inline u8 DQAcc = 0x45; + constexpr inline u8 DPAcc = 0x46; + + constexpr inline u8 SocVf0 = 0x48; + + constexpr inline u8 Qh0 = 0x4C; + constexpr inline u8 Qh = 0x4D; + + constexpr inline u8 SocVfAccess = 0x60; + + constexpr inline u8 ModelAccess0 = 0x62; + constexpr inline u8 ModelAccess1 = 0x63; + + constexpr inline u8 ModelChrTblStart = 0x80; + constexpr inline u8 ModelChrTblEnd = 0xB0; + + + constexpr inline u8 VFocV = 0xFB; + constexpr inline u8 SocVf = 0xFF; + + constexpr inline size_t ModelChrTblSize = ModelChrTblEnd - ModelChrTblStart; + + namespace { + + struct CustomParameters { + u16 relaxcfg; + u16 rcomp0; + u16 tempco; + u16 ichgterm; + u16 tgain; + u16 toff; + u16 vempty; + u16 qresidual00; + u16 qresidual10; + u16 qresidual20; + u16 qresidual30; + u16 fullcap; + u16 vffullcap; + u16 modeltbl[ModelChrTblSize]; + u16 fullsocthr; + u16 iavgempty; + }; + + #include "powctl_max17050_custom_parameters.inc" + + const CustomParameters &GetCustomParameters(const char *battery_vendor, u8 battery_version) { + if (battery_version == 2) { + if (battery_vendor[7] == 'M') { + return CustomParameters2M; + } else if (battery_vendor[7] == 'R') { + return CustomParameters2R; + } else /* if (battery_vendor[7] == 'A') */ { + return CustomParameters2A; + } + } else if (battery_version == 1) { + return CustomParameters1; + } else /* if (battery_version == 0) */ { + if (battery_vendor[7] == 'M') { + return CustomParameters0M; + } else if (battery_vendor[7] == 'R') { + return CustomParameters0R; + } else /* if (battery_vendor[7] == 'A') */ { + return CustomParameters0A; + } + } + } + + } + + } + + namespace { + + ALWAYS_INLINE Result ReadWriteRegister(const i2c::I2cSession &session, u8 address, u16 mask, u16 value) { + /* Read the current value. */ + u16 cur_val; + R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); + + /* Update the value. */ + const u16 new_val = (cur_val & ~mask) | (value & mask); + R_TRY(i2c::WriteSingleRegister(session, address, new_val)); + + return ResultSuccess(); + } + + ALWAYS_INLINE Result ReadRegister(const i2c::I2cSession &session, u8 address, u16 *out) { + return i2c::ReadSingleRegister(session, address, out); + } + + ALWAYS_INLINE Result WriteRegister(const i2c::I2cSession &session, u8 address, u16 val) { + return i2c::WriteSingleRegister(session, address, val); + } + + ALWAYS_INLINE bool WriteValidateRegister(const i2c::I2cSession &session, u8 address, u16 val) { + /* Write the value. */ + R_ABORT_UNLESS(WriteRegister(session, address, val)); + + /* Give it time to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(3)); + + /* Read it back. */ + u16 new_val; + R_ABORT_UNLESS(ReadRegister(session, address, std::addressof(new_val))); + + return new_val == val; + } + + ALWAYS_INLINE Result ReadWriteValidateRegister(const i2c::I2cSession &session, u8 address, u16 mask, u16 value) { + /* Read the current value. */ + u16 cur_val; + R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); + + /* Update the value. */ + const u16 new_val = (cur_val & ~mask) | (value & mask); + while (!WriteValidateRegister(session, address, new_val)) { /* ... */ } + + return ResultSuccess(); + } + + } + + Result Max17050Driver::InitializeSession(const char *battery_vendor, u8 battery_version) { + /* Get the custom parameters. */ + const auto ¶ms = max17050::GetCustomParameters(battery_vendor, battery_version); + + /* We only want to write the parameters on power on reset. */ + R_SUCCEED_IF(!this->IsPowerOnReset()); + + /* Set that we need to restore parameters. */ + R_TRY(this->SetNeedToRestoreParameters(true)); + + /* Wait for our configuration to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(500)); + + /* Write initial config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::Config, 0x7210)); + + /* Write initial filter config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::FilterCfg, 0x8784)); + + /* Write relax config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::RelaxCfg, params.relaxcfg)); + + /* Write initial learn config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::LearnCfg, 0x2603)); + + /* Write fullsocthr. */ + R_TRY(WriteRegister(this->i2c_session, max17050::FullSocThr, params.fullsocthr)); + + /* Write iavgempty. */ + R_TRY(WriteRegister(this->i2c_session, max17050::IAvgEmpty, params.iavgempty)); + + /* Unlock model table, write model table. */ + do { + R_TRY(this->UnlockModelTable()); + R_TRY(this->SetModelTable(params.modeltbl)); + } while (!this->IsModelTableSet(params.modeltbl)); + + /* Lock the model table, trying up to ten times. */ + { + size_t i = 0; + while (true) { + ++i; + + R_TRY(this->LockModelTable()); + + if (this->IsModelTableLocked()) { + break; + } + + R_SUCCEED_IF(i >= 10); + } + } + + /* Write and validate rcomp0 */ + while (!WriteValidateRegister(this->i2c_session, max17050::RComp0, params.rcomp0)) { /* ... */ } + + /* Write and validate tempco */ + while (!WriteValidateRegister(this->i2c_session, max17050::TempCo, params.tempco)) { /* ... */ } + + /* Write ichgterm. */ + R_TRY(WriteRegister(this->i2c_session, max17050::IChgTerm, params.ichgterm)); + + /* Write tgain. */ + R_TRY(WriteRegister(this->i2c_session, max17050::TGain, params.tgain)); + + /* Write toff. */ + R_TRY(WriteRegister(this->i2c_session, max17050::TOff, params.toff)); + + /* Write and validate vempty. */ + while (!WriteValidateRegister(this->i2c_session, max17050::VEmpty, params.vempty)) { /* ... */ } + + /* Write and validate qresidual. */ + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual00, params.qresidual00)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual10, params.qresidual10)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual20, params.qresidual20)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual30, params.qresidual30)) { /* ... */ } + + /* Write capacity parameters. */ + while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ } + R_TRY(WriteRegister(this->i2c_session, max17050::DesignCap, params.vffullcap)); + while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ } + + /* Give some time for configuration to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(350)); + + /* Write vfsoc to vfsoc0, qh, to qh0. */ + u16 vfsoc, qh; + { + R_TRY(ReadRegister(this->i2c_session, max17050::SocVf, std::addressof(vfsoc))); + R_TRY(this->UnlockVfSoc()); + while (!WriteValidateRegister(this->i2c_session, max17050::SocVf0, vfsoc)) { /* ... */ } + R_TRY(ReadRegister(this->i2c_session, max17050::Qh, std::addressof(qh))); + R_TRY(WriteRegister(this->i2c_session, max17050::Qh0, qh)); + R_TRY(this->LockVfSoc()); + } + + /* Reset cycles. */ + while (!WriteValidateRegister(this->i2c_session, max17050::Cycles, 0x0060)) { /* ... */ } + + /* Load new capacity parameters. */ + const u16 remcap = static_cast((vfsoc * params.vffullcap) / 0x6400); + const u16 repcap = static_cast(remcap * (params.fullcap / params.vffullcap)); + const u16 dpacc = 0x0C80; + const u16 dqacc = params.vffullcap / 0x10; + while (!WriteValidateRegister(this->i2c_session, max17050::RemCapMix, remcap)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::RemCapRep, repcap)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DPAcc, dpacc)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DQAcc, dqacc)) { /* ... */ } + + /* Write capacity parameters. */ + while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ } + R_TRY(WriteRegister(this->i2c_session, max17050::DesignCap, params.vffullcap)); + while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ } + + /* Write soc rep. */ + R_TRY(WriteRegister(this->i2c_session, max17050::SocRep, vfsoc)); + + /* Clear power on reset. */ + R_TRY(ReadWriteValidateRegister(this->i2c_session, max17050::Status, 0x0002, 0x0000)); + + /* Set cgain. */ + R_TRY(WriteRegister(this->i2c_session, max17050::CGain, 0x7FFF)); + + return ResultSuccess(); + } + + Result Max17050Driver::SetMaximumShutdownTimerThreshold() { + return WriteRegister(this->i2c_session, max17050::ShdnTimer, 0xE000); + } + + bool Max17050Driver::IsPowerOnReset() { + /* Get the register. */ + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::Status, std::addressof(val))); + + /* Extract the value. */ + return (val & 0x0002) != 0; + } + + Result Max17050Driver::LockVfSoc() { + return WriteRegister(this->i2c_session, max17050::SocVfAccess, 0x0000); + } + + Result Max17050Driver::UnlockVfSoc() { + return WriteRegister(this->i2c_session, max17050::SocVfAccess, 0x0080); + } + + Result Max17050Driver::LockModelTable() { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess0, 0x0000)); + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess1, 0x0000)); + return ResultSuccess(); + } + + Result Max17050Driver::UnlockModelTable() { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess0, 0x0059)); + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess1, 0x00C4)); + return ResultSuccess(); + } + + bool Max17050Driver::IsModelTableLocked() { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::ModelChrTblStart + i, std::addressof(val))); + + if (val != 0) { + return false; + } + } + + return true; + } + + Result Max17050Driver::SetModelTable(const u16 *model_table) { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelChrTblStart + i, model_table[i])); + } + + return ResultSuccess(); + } + + bool Max17050Driver::IsModelTableSet(const u16 *model_table) { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::ModelChrTblStart + i, std::addressof(val))); + + if (val != model_table[i]) { + return false; + } + } + + return true; + } + + Result Max17050Driver::GetNeedToRestoreParameters(bool *out) { + /* Get the register. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::MiscCfg, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x8000) != 0; + return ResultSuccess(); + } + + Result Max17050Driver::SetNeedToRestoreParameters(bool en) { + return ReadWriteRegister(this->i2c_session, max17050::MiscCfg, 0x8000, en ? 0x8000 : 0); + } + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp new file mode 100644 index 000000000..0cdbfbd51 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace max17050 { + + } + + class Max17050Driver { + private: + os::SdkMutex mutex; + int init_count; + i2c::I2cSession i2c_session; + private: + Result InitializeSession(const char *battery_vendor, u8 battery_version); + Result SetMaximumShutdownTimerThreshold(); + + bool IsPowerOnReset(); + Result LockVfSoc(); + Result UnlockVfSoc(); + Result LockModelTable(); + Result UnlockModelTable(); + bool IsModelTableLocked(); + Result SetModelTable(const u16 *model_table); + bool IsModelTableSet(const u16 *model_table); + public: + Max17050Driver() : mutex(), init_count(0), i2c_session() { + /* ... */ + } + + void Initialize(const char *battery_vendor, u8 battery_version) { + std::scoped_lock lk(this->mutex); + if ((this->init_count++) == 0) { + /* Initialize i2c library. */ + i2c::InitializeEmpty(); + + /* Open session. */ + R_ABORT_UNLESS(i2c::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max17050)); + + /* Initialize session. */ + R_ABORT_UNLESS(this->InitializeSession(battery_vendor, battery_version)); + + /* Set shutdown timer threshold to the maximum value. */ + R_ABORT_UNLESS(this->SetMaximumShutdownTimerThreshold()); + } + } + + void Finalize() { + std::scoped_lock lk(this->mutex); + if ((--this->init_count) == 0) { + /* Close session. */ + i2c::CloseSession(this->i2c_session); + + /* Finalize i2c library. */ + i2c::Finalize(); + } + } + + Result GetNeedToRestoreParameters(bool *out); + Result SetNeedToRestoreParameters(bool en); + }; + +}