From 15c752f52df6d9dc7306c022462c7438c1367fbe Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 10 Nov 2020 01:27:35 -0800 Subject: [PATCH] powctl: add battery driver logic (missing impl) --- .../nintendo_nx/powctl_battery_driver.cpp | 315 ++++++++++++++---- .../nintendo_nx/powctl_max17050_driver.cpp | 5 + .../nintendo_nx/powctl_max17050_driver.hpp | 67 +++- 3 files changed, 328 insertions(+), 59 deletions(-) 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 e59436231..71397db7e 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 @@ -30,6 +30,8 @@ namespace ams::powctl::impl::board::nintendo_nx { return s_max17050_driver; } + constexpr inline const double SenseResistorValue = 0.005; + } /* Generic API. */ @@ -115,148 +117,345 @@ namespace ams::powctl::impl::board::nintendo_nx { } Result BatteryDriver::GetBatterySocRep(float *out_percent, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetSocRep(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); } Result BatteryDriver::GetBatterySocVf(float *out_percent, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetSocVf(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); } Result BatteryDriver::GetBatteryFullCapacity(int *out_mah, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double mah; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetFullCapacity(std::addressof(mah), SenseResistorValue)); + + /* Set output. */ + *out_mah = mah; + return ResultSuccess(); } Result BatteryDriver::GetBatteryRemainingCapacity(int *out_mah, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double mah; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetRemainingCapacity(std::addressof(mah), SenseResistorValue)); + + /* Set output. */ + *out_mah = mah; + return ResultSuccess(); } Result BatteryDriver::SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageMinimumAlertThreshold(percentage)); + + return ResultSuccess(); } Result BatteryDriver::SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageMaximumAlertThreshold(percentage)); + + return ResultSuccess(); } Result BatteryDriver::SetBatteryPercentageFullThreshold(IDevice *device, float percentage) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageFullThreshold(percentage)); + + return ResultSuccess(); } Result BatteryDriver::GetBatteryAverageCurrent(int *out_ma, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ma; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageCurrent(std::addressof(ma), SenseResistorValue)); + + /* Set output. */ + *out_ma = ma; + return ResultSuccess(); } Result BatteryDriver::GetBatteryCurrent(int *out_ma, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ma; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetCurrent(std::addressof(ma), SenseResistorValue)); + + /* Set output. */ + *out_ma = ma; + return ResultSuccess(); } Result BatteryDriver::GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(dst != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(out_size != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(dst_size == sizeof(max17050::InternalState), powctl::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(reinterpret_cast(dst), alignof(max17050::InternalState)), powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().ReadInternalState()); + GetMax17050Driver().GetInternalState(static_cast(dst)); + + return ResultSuccess(); } Result BatteryDriver::SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(src != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(src_size == sizeof(max17050::InternalState), powctl::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(reinterpret_cast(src), alignof(max17050::InternalState)), powctl::ResultInvalidArgument()); + + GetMax17050Driver().SetInternalState(*static_cast(src)); + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().WriteInternalState()); + + return ResultSuccess(); } Result BatteryDriver::GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetNeedToRestoreParameters(out)); + + return ResultSuccess(); } Result BatteryDriver::SetBatteryNeedToRestoreParameters(IDevice *device, bool en) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetNeedToRestoreParameters(en)); + + return ResultSuccess(); } Result BatteryDriver::IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().IsI2cShutdownEnabled(out)); + + return ResultSuccess(); } Result BatteryDriver::SetBatteryI2cShutdownEnabled(IDevice *device, bool en) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetI2cShutdownEnabled(en)); + + return ResultSuccess(); } Result BatteryDriver::IsBatteryPresent(bool *out, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the battery status. */ + u16 status; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetStatus(std::addressof(status))); + + /* Set output. */ + *out = (status & 0x0008) == 0; + return ResultSuccess(); } Result BatteryDriver::GetBatteryCycles(int *out, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the battery cycles. */ + u16 cycles; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetCycles(std::addressof(cycles))); + + /* Set output. */ + *out = cycles; + return ResultSuccess(); } Result BatteryDriver::SetBatteryCycles(IDevice *device, int cycles) { - /* TODO */ - AMS_ABORT(); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(cycles == 0, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().ResetCycles()); + + return ResultSuccess(); } Result BatteryDriver::GetBatteryAge(float *out_percent, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAge(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); } Result BatteryDriver::GetBatteryTemperature(float *out_c, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double temp; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetTemperature(std::addressof(temp))); + + /* Set output. */ + *out_c = temp; + return ResultSuccess(); } Result BatteryDriver::GetBatteryMaximumTemperature(float *out_c, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + u8 max_temp; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetMaximumTemperature(std::addressof(max_temp))); + + /* Set output. */ + *out_c = static_cast(max_temp); + return ResultSuccess(); } Result BatteryDriver::SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMinimumAlertThreshold(c)); + + return ResultSuccess(); } Result BatteryDriver::SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMaximumAlertThreshold(c)); + + return ResultSuccess(); } Result BatteryDriver::GetBatteryVCell(int *out_mv, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetVCell(out_mv)); + + return ResultSuccess(); } Result BatteryDriver::GetBatteryAverageVCell(int *out_mv, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCell(out_mv)); + + return ResultSuccess(); } Result BatteryDriver::GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ms; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCellTime(std::addressof(ms))); + + /* Set output. */ + *out = TimeSpan::FromMicroSeconds(static_cast(ms * 1000.0)); + return ResultSuccess(); } Result BatteryDriver::SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMinimumAlertThreshold(mv)); + + return ResultSuccess(); } Result BatteryDriver::GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetOpenCircuitVoltage(out_mv)); + + return ResultSuccess(); } Result BatteryDriver::SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) { - /* TODO */ - AMS_ABORT(); + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMaximumAlertThreshold(mv)); + + return ResultSuccess(); } } 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 index 61e049084..a10eecb69 100644 --- 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 @@ -156,6 +156,7 @@ namespace ams::powctl::impl::board::nintendo_nx { } } + } } @@ -417,4 +418,8 @@ namespace ams::powctl::impl::board::nintendo_nx { return ReadWriteRegister(this->i2c_session, max17050::MiscCfg, 0x8000, en ? 0x8000 : 0); } + Result Max17050Driver::ResetCycles() { + return WriteRegister(this->i2c_session, max17050::Cycles, 0x0060); + } + } 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 index 0cdbfbd51..7d01e30f2 100644 --- 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 @@ -20,6 +20,19 @@ namespace ams::powctl::impl::board::nintendo_nx { namespace max17050 { + struct InternalState { + u16 rcomp0; + u16 tempco; + u16 fullcap; + u16 cycles; + u16 fullcapnom; + u16 iavgempty; + u16 qresidual00; + u16 qresidual10; + u16 qresidual20; + u16 qresidual30; + }; + } class Max17050Driver { @@ -27,6 +40,7 @@ namespace ams::powctl::impl::board::nintendo_nx { os::SdkMutex mutex; int init_count; i2c::I2cSession i2c_session; + max17050::InternalState internal_state; private: Result InitializeSession(const char *battery_vendor, u8 battery_version); Result SetMaximumShutdownTimerThreshold(); @@ -40,7 +54,7 @@ namespace ams::powctl::impl::board::nintendo_nx { Result SetModelTable(const u16 *model_table); bool IsModelTableSet(const u16 *model_table); public: - Max17050Driver() : mutex(), init_count(0), i2c_session() { + Max17050Driver() : mutex(), init_count(0), i2c_session(), internal_state() { /* ... */ } @@ -72,8 +86,59 @@ namespace ams::powctl::impl::board::nintendo_nx { } } + Result ReadInternalState(); + Result WriteInternalState(); + + void GetInternalState(max17050::InternalState *dst) { + *dst = this->internal_state; + } + + void SetInternalState(const max17050::InternalState &src) { + this->internal_state = src; + } + + Result GetSocRep(double *out); + Result GetSocVf(double *out); + + Result GetFullCapacity(double *out, double sense_resistor); + Result GetRemainingCapacity(double *out, double sense_resistor); + + Result SetPercentageMinimumAlertThreshold(int percentage); + Result SetPercentageMaximumAlertThreshold(int percentage); + + Result SetPercentageFullThreshold(double percentage); + + Result GetAverageCurrent(double *out, double sense_resistor); + Result GetCurrent(double *out, double sense_resistor); + Result GetNeedToRestoreParameters(bool *out); Result SetNeedToRestoreParameters(bool en); + + Result IsI2cShutdownEnabled(bool *out); + Result SetI2cShutdownEnabled(bool en); + + Result GetStatus(u16 *out); + + Result GetCycles(u16 *out); + Result ResetCycles(); + + Result GetAge(double *out); + + Result GetTemperature(double *out); + + Result GetMaximumTemperature(u8 *out); + + Result SetTemperatureMinimumAlertThreshold(int c); + Result SetTemperatureMaximumAlertThreshold(int c); + + Result GetVCell(int *out); + Result GetAverageVCell(int *out); + Result GetAverageVCellTime(double *out); + + Result GetOpenCircuitVoltage(int *out); + + Result SetVoltageMinimumAlertThreshold(int mv); + Result SetVoltageMaximumAlertThreshold(int mv); }; }