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

This reverts commit 15b7df8ef1.
This commit is contained in:
souldbminersmwc
2025-11-09 16:14:52 -05:00
parent 22ec140738
commit 21a3f953d7
3804 changed files with 435 additions and 570162 deletions

View File

@@ -1,495 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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 util::optional<BatteryDevice> g_battery_device;
constinit Max17050Driver g_max17050_driver;
ALWAYS_INLINE Max17050Driver &GetMax17050Driver() {
return g_max17050_driver;
}
constexpr inline const double SenseResistorValue = 0.005;
}
BatteryDevice::BatteryDevice(bool ev) : m_use_event_handler(ev), m_event_handler() {
if (m_use_event_handler) {
/* Create the system event. */
os::CreateSystemEvent(std::addressof(m_system_event), os::EventClearMode_ManualClear, true);
/* Create the handler. */
m_event_handler.emplace(this);
/* Register the event handler. */
powctl::impl::RegisterInterruptHandler(std::addressof(*m_event_handler));
}
}
/* Generic API. */
void BatteryDriver::InitializeDriver() {
/* 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() {
/* 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 = util::nullopt;
/* Finalize gpio library. */
gpio::Finalize();
}
Result BatteryDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Check that we support event handlers. */
R_UNLESS(this->IsEventHandlerEnabled(), powctl::ResultNotAvailable());
*out = device->SafeCastTo<BatteryDevice>().GetSystemEvent();
R_SUCCEED();
}
Result BatteryDriver::SetDeviceInterruptEnabled(IDevice *device, bool enable) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Set the interrupt enable. */
device->SafeCastTo<BatteryDevice>().SetInterruptEnabled(enable);
R_SUCCEED();
}
Result BatteryDriver::GetDeviceErrorStatus(u32 *out, IDevice *device) {
/* TODO */
AMS_UNUSED(out, device);
AMS_ABORT();
}
Result BatteryDriver::SetDeviceErrorStatus(IDevice *device, u32 status) {
/* TODO */
AMS_UNUSED(device, status);
AMS_ABORT();
}
Result BatteryDriver::GetBatteryChargePercentage(float *out_percent, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double percent{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetChargePercentage(std::addressof(percent)));
/* Set output. */
*out_percent = percent;
R_SUCCEED();
}
Result BatteryDriver::GetBatteryVoltageFuelGaugePercentage(float *out_percent, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double percent{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetVoltageFuelGaugePercentage(std::addressof(percent)));
/* Set output. */
*out_percent = percent;
R_SUCCEED();
}
Result BatteryDriver::GetBatteryFullCapacity(int *out_mah, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double mah{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetFullCapacity(std::addressof(mah), SenseResistorValue));
/* Set output. */
*out_mah = mah;
R_SUCCEED();
}
Result BatteryDriver::GetBatteryRemainingCapacity(int *out_mah, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double mah{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetRemainingCapacity(std::addressof(mah), SenseResistorValue));
/* Set output. */
*out_mah = mah;
R_SUCCEED();
}
Result BatteryDriver::SetBatteryChargePercentageMinimumAlertThreshold(IDevice *device, float percentage) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetChargePercentageMinimumAlertThreshold(percentage));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryChargePercentageMaximumAlertThreshold(IDevice *device, float percentage) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetChargePercentageMaximumAlertThreshold(percentage));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryVoltageFuelGaugePercentageMinimumAlertThreshold(IDevice *device, float percentage) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageFuelGaugePercentageMinimumAlertThreshold(percentage));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryVoltageFuelGaugePercentageMaximumAlertThreshold(IDevice *device, float percentage) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageFuelGaugePercentageMaximumAlertThreshold(percentage));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryFullChargeThreshold(IDevice *device, float percentage) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetFullChargeThreshold(percentage));
R_SUCCEED();
}
Result BatteryDriver::GetBatteryAverageCurrent(int *out_ma, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double ma{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageCurrent(std::addressof(ma), SenseResistorValue));
/* Set output. */
*out_ma = ma;
R_SUCCEED();
}
Result BatteryDriver::GetBatteryCurrent(int *out_ma, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double ma{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetCurrent(std::addressof(ma), SenseResistorValue));
/* Set output. */
*out_ma = ma;
R_SUCCEED();
}
Result BatteryDriver::GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) {
/* 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<uintptr_t>(dst), alignof(max17050::InternalState)), powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().ReadInternalState());
GetMax17050Driver().GetInternalState(static_cast<max17050::InternalState *>(dst));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) {
/* 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<uintptr_t>(src), alignof(max17050::InternalState)), powctl::ResultInvalidArgument());
GetMax17050Driver().SetInternalState(*static_cast<const max17050::InternalState *>(src));
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().WriteInternalState());
R_SUCCEED();
}
Result BatteryDriver::GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetNeedToRestoreParameters(out));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryNeedToRestoreParameters(IDevice *device, bool en) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Set the value. */
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetNeedToRestoreParameters(en));
R_SUCCEED();
}
Result BatteryDriver::IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().IsI2cShutdownEnabled(out));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryI2cShutdownEnabled(IDevice *device, bool en) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Set the value. */
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetI2cShutdownEnabled(en));
R_SUCCEED();
}
Result BatteryDriver::IsBatteryPresent(bool *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the battery status. */
u16 status;
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetStatus(std::addressof(status)));
/* Set output. */
*out = (status & 0x0008) == 0;
R_SUCCEED();
}
Result BatteryDriver::GetBatteryCycles(int *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the battery cycles. */
u16 cycles{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetCycles(std::addressof(cycles)));
/* Set output. */
*out = cycles;
R_SUCCEED();
}
Result BatteryDriver::SetBatteryCycles(IDevice *device, int cycles) {
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(cycles == 0, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().ResetCycles());
R_SUCCEED();
}
Result BatteryDriver::GetBatteryAge(float *out_percent, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double percent{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetAge(std::addressof(percent)));
/* Set output. */
*out_percent = percent;
R_SUCCEED();
}
Result BatteryDriver::GetBatteryTemperature(float *out_c, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double temp{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetTemperature(std::addressof(temp)));
/* Set output. */
*out_c = temp;
R_SUCCEED();
}
Result BatteryDriver::GetBatteryMaximumTemperature(float *out_c, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
u8 max_temp{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetMaximumTemperature(std::addressof(max_temp)));
/* Set output. */
*out_c = static_cast<float>(max_temp);
R_SUCCEED();
}
Result BatteryDriver::SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMinimumAlertThreshold(c));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMaximumAlertThreshold(c));
R_SUCCEED();
}
Result BatteryDriver::GetBatteryVCell(int *out_mv, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetVCell(out_mv));
R_SUCCEED();
}
Result BatteryDriver::GetBatteryAverageVCell(int *out_mv, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCell(out_mv));
R_SUCCEED();
}
Result BatteryDriver::GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
double ms{};
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCellTime(std::addressof(ms)));
/* Set output. */
*out = TimeSpan::FromMicroSeconds(static_cast<s64>(ms * 1000.0));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMinimumAlertThreshold(mv));
R_SUCCEED();
}
Result BatteryDriver::GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Get the value. */
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().GetOpenCircuitVoltage(out_mv));
R_SUCCEED();
}
Result BatteryDriver::SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMaximumAlertThreshold(mv));
R_SUCCEED();
}
}

View File

@@ -1,148 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../../../powctl_i_power_control_driver.hpp"
#include "powctl_interrupt_event_handler.hpp"
namespace ams::powctl::impl::board::nintendo::nx {
class BatteryDevice : public powctl::impl::IDevice {
NON_COPYABLE(BatteryDevice);
NON_MOVEABLE(BatteryDevice);
AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo::nx::BatteryDevice, ::ams::powctl::impl::IDevice);
private:
bool m_use_event_handler;
util::optional<BatteryInterruptEventHandler> m_event_handler;
os::SystemEventType m_system_event;
public:
BatteryDevice(bool ev);
os::SystemEventType *GetSystemEvent() { return std::addressof(m_system_event); }
void SetInterruptEnabled(bool en) {
if (m_use_event_handler) {
m_event_handler->SetInterruptEnabled(en);
}
}
};
class BatteryDriver : public IPowerControlDriver {
NON_COPYABLE(BatteryDriver);
NON_MOVEABLE(BatteryDriver);
AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo::nx::BatteryDriver, ::ams::powctl::impl::IPowerControlDriver);
public:
BatteryDriver(bool ev) : IPowerControlDriver(ev) { /* ... */ }
/* Generic API. */
virtual void InitializeDriver() override;
virtual void FinalizeDriver() override;
virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) override;
virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) override;
virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) override;
virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) override;
/* Battery API. */
virtual Result GetBatteryChargePercentage(float *out_percent, IDevice *device) override;
virtual Result GetBatteryVoltageFuelGaugePercentage(float *out_percent, IDevice *device) override;
virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) override;
virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) override;
virtual Result SetBatteryChargePercentageMinimumAlertThreshold(IDevice *device, float percentage) override;
virtual Result SetBatteryChargePercentageMaximumAlertThreshold(IDevice *device, float percentage) override;
virtual Result SetBatteryVoltageFuelGaugePercentageMinimumAlertThreshold(IDevice *device, float percentage) override;
virtual Result SetBatteryVoltageFuelGaugePercentageMaximumAlertThreshold(IDevice *device, float percentage) override;
virtual Result SetBatteryFullChargeThreshold(IDevice *device, float percentage) override;
virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) override;
virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) override;
virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) override;
virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) override;
virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) override;
virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) override;
virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) override;
virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) override;
virtual Result IsBatteryPresent(bool *out, IDevice *device) override;
virtual Result GetBatteryCycles(int *out, IDevice *device) override;
virtual Result SetBatteryCycles(IDevice *device, int cycles) override;
virtual Result GetBatteryAge(float *out_percent, IDevice *device) override;
virtual Result GetBatteryTemperature(float *out_c, IDevice *device) override;
virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) override;
virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) override;
virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) override;
virtual Result GetBatteryVCell(int *out_mv, IDevice *device) override;
virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) override;
virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) override;
virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) override;
virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) override;
virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) override;
/* Unsupported Charger API. */
virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) override { AMS_UNUSED(device, state); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) override { AMS_UNUSED(out_ma, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) override { AMS_UNUSED(device, ma); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) override { AMS_UNUSED(out_mv, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) override { AMS_UNUSED(device, mv); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) override { AMS_UNUSED(device, cfg); R_THROW(powctl::ResultNotSupported()); }
virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerHiZEnabled(IDevice *device, bool en) override { AMS_UNUSED(device, en); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) override { AMS_UNUSED(out_ma, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) override { AMS_UNUSED(device, ma); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) override { AMS_UNUSED(device, mv); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) override { AMS_UNUSED(device, ma); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) override { AMS_UNUSED(device, en); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) override { AMS_UNUSED(device, timeout); R_THROW(powctl::ResultNotSupported()); }
virtual Result ResetChargerWatchdogTimer(IDevice *device) override { AMS_UNUSED(device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) override { AMS_UNUSED(out_mo, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) override { AMS_UNUSED(device, mo); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) override { AMS_UNUSED(out_mv, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetChargerVoltageClamp(IDevice *device, int mv) override { AMS_UNUSED(device, mv); R_THROW(powctl::ResultNotSupported()); }
};
}

View File

@@ -1,73 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../../../powctl_device_management.hpp"
#include "powctl_board_impl.hpp"
#include "powctl_battery_driver.hpp"
#include "powctl_charger_driver.hpp"
namespace ams::powctl::impl::board::nintendo::nx {
namespace {
constinit util::optional<ChargerDriver> g_charger_driver;
constinit util::optional<BatteryDriver> g_battery_driver;
void InitializeChargerDriver(bool use_event_handlers) {
/* Create the charger driver. */
g_charger_driver.emplace(use_event_handlers);
/* Register the driver. */
powctl::impl::RegisterDriver(std::addressof(*g_charger_driver));
}
void InitializeBatteryDriver(bool use_event_handlers) {
/* Create the battery driver. */
g_battery_driver.emplace(use_event_handlers);
/* Register the driver. */
powctl::impl::RegisterDriver(std::addressof(*g_battery_driver));
}
void FinalizeChargerDriver() {
/* Unregister the driver. */
powctl::impl::UnregisterDriver(std::addressof(*g_charger_driver));
/* Destroy the battery driver. */
g_charger_driver = util::nullopt;
}
void FinalizeBatteryDriver() {
/* Unregister the driver. */
powctl::impl::UnregisterDriver(std::addressof(*g_battery_driver));
/* Destroy the battery driver. */
g_battery_driver = util::nullopt;
}
}
void Initialize(bool use_event_handlers) {
InitializeChargerDriver(use_event_handlers);
InitializeBatteryDriver(use_event_handlers);
}
void Finalize() {
FinalizeBatteryDriver();
FinalizeChargerDriver();
}
}

View File

@@ -1,26 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "powctl_interrupt_event_handler.hpp"
namespace ams::powctl::impl::board::nintendo::nx {
void Initialize(bool use_event_handlers);
void Finalize();
}

View File

@@ -1,414 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "powctl_bq24193_driver.hpp"
namespace ams::powctl::impl::board::nintendo::nx {
namespace bq24193 {
constexpr inline u8 InputSourceControl = 0x00;
constexpr inline u8 PowerOnConfiguration = 0x01;
constexpr inline u8 ChargeCurrentControl = 0x02;
constexpr inline u8 PreChargeTerminationCurrentControl = 0x03;
constexpr inline u8 ChargeVoltageControl = 0x04;
constexpr inline u8 ChargeTerminationTimerControl = 0x05;
constexpr inline u8 IrCompensationThermalRegulationControl = 0x06;
constexpr inline u8 MiscOperationControl = 0x07;
constexpr inline u8 SystemStatus = 0x08;
constexpr inline u8 Fault = 0x09;
constexpr inline u8 VendorPartRevisionStatus = 0x0A;
constexpr u8 EncodePreChargeCurrentLimit(int ma) {
constexpr int Minimum = 128;
constexpr int Maximum = 2048;
ma = std::max(std::min(ma, Maximum), Minimum);
return static_cast<u8>((static_cast<u32>(ma - Minimum) >> 7) << 4);
}
constexpr u8 EncodeTerminationCurrentLimit(int ma) {
constexpr int Minimum = 128;
constexpr int Maximum = 2048;
ma = std::max(std::min(ma, Maximum), Minimum);
return static_cast<u8>((static_cast<u32>(ma - Minimum) >> 7) << 0);
}
constexpr u8 EncodeMinimumSystemVoltageLimit(int mv) {
constexpr int Minimum = 3000;
constexpr int Maximum = 3700;
mv = std::max(std::min(mv, Maximum), Minimum);
return static_cast<u8>(((mv - Minimum) / 100) << 1);
}
constexpr u8 EncodeFastChargeCurrentLimit(int ma) {
constexpr int Minimum = 512;
constexpr int Maximum = 4544;
ma = std::max(std::min(ma, Maximum), Minimum);
return static_cast<u8>((static_cast<u32>(ma - Minimum) >> 6) << 2);
}
constexpr int DecodeFastChargeCurrentLimit(u8 reg) {
constexpr int Minimum = 512;
return Minimum + (static_cast<u32>(reg & 0xFC) << 4);
}
static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(512)) == 512);
static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(4544)) == 4544);
static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(576)) == 576);
constexpr u8 EncodeChargeVoltageLimit(int mv) {
constexpr int Minimum = 3504;
constexpr int Maximum = 4400;
mv = std::max(std::min(mv, Maximum), Minimum);
return static_cast<u8>((static_cast<u32>(mv - Minimum) >> 4) << 2);
}
constexpr int DecodeChargeVoltageLimit(u8 reg) {
constexpr int Minimum = 3504;
constexpr int Maximum = 4400;
return std::min<int>(Maximum, Minimum + (static_cast<u32>(reg & 0xFC) << 2));
}
static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(3504)) == 3504);
static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(4400)) == 4400);
static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(3520)) == 3520);
constexpr u8 EncodeChargerConfiguration(bq24193::ChargerConfiguration cfg) {
switch (cfg) {
case ChargerConfiguration_ChargeDisable: return 0x00;
case ChargerConfiguration_ChargeBattery: return 0x10;
case ChargerConfiguration_Otg: return 0x20;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
constexpr u8 EncodeWatchdogTimerSetting(int seconds) {
if (seconds == 0) {
return 0x00;
} else if (seconds < 80) {
return 0x10;
} else if (seconds < 160) {
return 0x20;
} else {
return 0x30;
}
}
constexpr u8 EncodeBatteryCompensation(int mo) {
constexpr int Minimum = 0;
constexpr int Maximum = 70;
mo = std::max(std::min(mo, Maximum), Minimum);
return static_cast<u8>((static_cast<u32>(mo - Minimum) / 10) << 5);
}
constexpr int DecodeBatteryCompensation(u8 reg) {
constexpr int Minimum = 0;
return Minimum + (static_cast<u32>(reg & 0xE0) >> 5) * 10;
}
static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(0)) == 0);
static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(70)) == 70);
static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(30)) == 30);
constexpr u8 EncodeVoltageClamp(int mv) {
constexpr int Minimum = 0;
constexpr int Maximum = 112;
mv = std::max(std::min(mv, Maximum), Minimum);
return static_cast<u8>((static_cast<u32>(mv - Minimum) >> 4) << 2);
}
constexpr int DecodeVoltageClamp(u8 reg) {
constexpr int Minimum = 0;
return Minimum + (static_cast<u32>(reg & 0x1C) << 2);
}
static_assert(DecodeVoltageClamp(EncodeVoltageClamp(0)) == 0);
static_assert(DecodeVoltageClamp(EncodeVoltageClamp(112)) == 112);
static_assert(DecodeVoltageClamp(EncodeVoltageClamp(64)) == 64);
constexpr u8 EncodeInputCurrentLimit(int ma) {
if (ma < 150) {
return 0;
} else if (ma < 500) {
return 1;
} else if (ma < 900) {
return 2;
} else if (ma < 1200) {
return 3;
} else if (ma < 1500) {
return 4;
} else if (ma < 2000) {
return 5;
} else if (ma < 3000) {
return 6;
} else{
return 7;
}
}
constexpr int DecodeInputCurrentLimit(u8 reg) {
switch (reg & 0x07) {
case 0: return 100;
case 1: return 150;
case 2: return 500;
case 3: return 900;
case 4: return 1200;
case 5: return 1500;
case 6: return 2000;
case 7: return 3000;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(100)) == 100);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(150)) == 150);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(500)) == 500);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(900)) == 900);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(1200)) == 1200);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(1500)) == 1500);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(2000)) == 2000);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(3000)) == 3000);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(0)) == 100);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(9999)) == 3000);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(149)) == 100);
static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(151)) == 150);
constexpr u8 EncodeInputVoltageLimit(int mv) {
constexpr int Minimum = 3880;
constexpr int Maximum = 5080;
mv = std::max(std::min(mv, Maximum), Minimum);
return static_cast<u8>(((static_cast<u32>(mv - Minimum) / 80) & 0xF) << 3);
}
constexpr u8 EncodeBoostModeCurrentLimit(int ma) {
return ma >= 1300 ? 1 : 0;
}
constexpr bq24193::ChargerStatus DecodeChargerStatus(u8 reg) {
switch (reg & 0x30) {
case 0x00: return bq24193::ChargerStatus_NotCharging;
case 0x10: return bq24193::ChargerStatus_PreCharge;
case 0x20: return bq24193::ChargerStatus_FastCharging;
case 0x30: return bq24193::ChargerStatus_ChargeTerminationDone;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
}
namespace {
ALWAYS_INLINE Result ReadWriteRegister(const i2c::I2cSession &session, u8 address, u8 mask, u8 value) {
/* Read the current value. */
u8 cur_val;
R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val)));
/* Update the value. */
const u8 new_val = (cur_val & ~mask) | (value & mask);
R_TRY(i2c::WriteSingleRegister(session, address, new_val));
R_SUCCEED();
}
}
Result Bq24193Driver::InitializeSession() {
/* Set fast charge current limit. */
R_TRY(this->SetFastChargeCurrentLimit(512));
/* Disable force 20 percent charge. */
R_TRY(this->SetForce20PercentChargeCurrent(false));
/* Set pre-charge current limit. */
R_TRY(this->SetPreChargeCurrentLimit(128));
/* Set termination current limit. */
R_TRY(this->SetTerminationCurrentLimit(128));
/* Set minimum system voltage limit. */
R_TRY(this->SetMinimumSystemVoltageLimit(3000));
/* Set watchdog timer setting. */
R_TRY(this->SetWatchdogTimerSetting(0));
/* Disable charging safety timer. */
R_TRY(this->SetChargingSafetyTimerEnabled(false));
/* Reset the watchdog timer. */
R_TRY(this->ResetWatchdogTimer());
R_SUCCEED();
}
Result Bq24193Driver::SetPreChargeCurrentLimit(int ma) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::PreChargeTerminationCurrentControl, 0xF0, bq24193::EncodePreChargeCurrentLimit(ma)));
}
Result Bq24193Driver::SetTerminationCurrentLimit(int ma) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::PreChargeTerminationCurrentControl, 0x0F, bq24193::EncodeTerminationCurrentLimit(ma)));
}
Result Bq24193Driver::SetMinimumSystemVoltageLimit(int mv) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::PowerOnConfiguration, 0x0E, bq24193::EncodeMinimumSystemVoltageLimit(mv)));
}
Result Bq24193Driver::SetChargingSafetyTimerEnabled(bool en) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::ChargeTerminationTimerControl, 0x08, en ? 0x08 : 0x00));
}
Result Bq24193Driver::GetForce20PercentChargeCurrent(bool *out) {
/* Get the register. */
u8 val;
R_TRY(i2c::ReadSingleRegister(m_i2c_session, bq24193::ChargeCurrentControl, std::addressof(val)));
/* Extract the value. */
*out = (val & 0x01) != 0;
R_SUCCEED();
}
Result Bq24193Driver::SetForce20PercentChargeCurrent(bool en) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::ChargeCurrentControl, 0x01, en ? 0x01 : 0x00));
}
Result Bq24193Driver::GetFastChargeCurrentLimit(int *out_ma) {
/* Get the register. */
u8 val;
R_TRY(i2c::ReadSingleRegister(m_i2c_session, bq24193::ChargeCurrentControl, std::addressof(val)));
/* Extract the value. */
*out_ma = bq24193::DecodeFastChargeCurrentLimit(val);
R_SUCCEED();
}
Result Bq24193Driver::SetFastChargeCurrentLimit(int ma) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::ChargeCurrentControl, 0xFC, bq24193::EncodeFastChargeCurrentLimit(ma)));
}
Result Bq24193Driver::GetChargeVoltageLimit(int *out_mv) {
/* Get the register. */
u8 val;
R_TRY(i2c::ReadSingleRegister(m_i2c_session, bq24193::ChargeVoltageControl, std::addressof(val)));
/* Extract the value. */
*out_mv = bq24193::DecodeChargeVoltageLimit(val);
R_SUCCEED();
}
Result Bq24193Driver::SetChargeVoltageLimit(int mv) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::ChargeVoltageControl, 0xFC, bq24193::EncodeChargeVoltageLimit(mv)));
}
Result Bq24193Driver::SetChargerConfiguration(bq24193::ChargerConfiguration cfg) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::PowerOnConfiguration, 0x30, bq24193::EncodeChargerConfiguration(cfg)));
}
Result Bq24193Driver::IsHiZEnabled(bool *out) {
/* Get the register. */
u8 val;
R_TRY(i2c::ReadSingleRegister(m_i2c_session, bq24193::InputSourceControl, std::addressof(val)));
/* Extract the value. */
*out = (val & 0x80) != 0;
R_SUCCEED();
}
Result Bq24193Driver::SetHiZEnabled(bool en) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::InputSourceControl, 0x80, en ? 0x80 : 0x00));
}
Result Bq24193Driver::GetInputCurrentLimit(int *out_ma) {
/* Get the register. */
u8 val;
R_TRY(i2c::ReadSingleRegister(m_i2c_session, bq24193::InputSourceControl, std::addressof(val)));
/* Extract the value. */
*out_ma = bq24193::DecodeInputCurrentLimit(val);
R_SUCCEED();
}
Result Bq24193Driver::SetInputCurrentLimit(int ma) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::InputSourceControl, 0x07, bq24193::EncodeInputCurrentLimit(ma)));
}
Result Bq24193Driver::SetInputVoltageLimit(int mv) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::InputSourceControl, 0x78, bq24193::EncodeInputVoltageLimit(mv)));
}
Result Bq24193Driver::SetBoostModeCurrentLimit(int ma) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::PowerOnConfiguration, 0x01, bq24193::EncodeBoostModeCurrentLimit(ma)));
}
Result Bq24193Driver::GetChargerStatus(bq24193::ChargerStatus *out) {
/* Get the register. */
u8 val;
R_TRY(i2c::ReadSingleRegister(m_i2c_session, bq24193::SystemStatus, std::addressof(val)));
/* Extract the value. */
*out = bq24193::DecodeChargerStatus(val);
R_SUCCEED();
}
Result Bq24193Driver::ResetWatchdogTimer() {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::PowerOnConfiguration, 0x40, 0x40));
}
Result Bq24193Driver::SetWatchdogTimerSetting(int seconds) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::ChargeTerminationTimerControl, 0x30, bq24193::EncodeWatchdogTimerSetting(seconds)));
}
Result Bq24193Driver::GetBatteryCompensation(int *out_mo) {
/* Get the register. */
u8 val;
R_TRY(i2c::ReadSingleRegister(m_i2c_session, bq24193::IrCompensationThermalRegulationControl, std::addressof(val)));
/* Extract the value. */
*out_mo = bq24193::DecodeBatteryCompensation(val);
R_SUCCEED();
}
Result Bq24193Driver::SetBatteryCompensation(int mo) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::IrCompensationThermalRegulationControl, 0xE0, bq24193::EncodeBatteryCompensation(mo)));
}
Result Bq24193Driver::GetVoltageClamp(int *out_mv) {
/* Get the register. */
u8 val;
R_TRY(i2c::ReadSingleRegister(m_i2c_session, bq24193::IrCompensationThermalRegulationControl, std::addressof(val)));
/* Extract the value. */
*out_mv = bq24193::DecodeVoltageClamp(val);
R_SUCCEED();
}
Result Bq24193Driver::SetVoltageClamp(int mv) {
R_RETURN(ReadWriteRegister(m_i2c_session, bq24193::IrCompensationThermalRegulationControl, 0x1C, bq24193::EncodeVoltageClamp(mv)));
}
}

View File

@@ -1,115 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::powctl::impl::board::nintendo::nx {
namespace bq24193 {
enum ChargerConfiguration {
ChargerConfiguration_ChargeDisable = 0,
ChargerConfiguration_ChargeBattery = 1,
ChargerConfiguration_Otg = 2,
};
enum ChargerStatus {
ChargerStatus_NotCharging = 0,
ChargerStatus_PreCharge = 1,
ChargerStatus_FastCharging = 2,
ChargerStatus_ChargeTerminationDone = 3,
};
}
class Bq24193Driver {
private:
os::SdkMutex m_mutex;
int m_init_count;
i2c::I2cSession m_i2c_session;
private:
Result InitializeSession();
public:
constexpr Bq24193Driver() : m_mutex(), m_init_count(0), m_i2c_session() {
/* ... */
}
void Initialize() {
std::scoped_lock lk(m_mutex);
if ((m_init_count++) == 0) {
/* Initialize i2c library. */
i2c::InitializeEmpty();
/* Open session. */
R_ABORT_UNLESS(i2c::OpenSession(std::addressof(m_i2c_session), i2c::DeviceCode_Bq24193));
/* Initialize session. */
R_ABORT_UNLESS(this->InitializeSession());
}
}
void Finalize() {
std::scoped_lock lk(m_mutex);
if ((--m_init_count) == 0) {
/* Close session. */
i2c::CloseSession(m_i2c_session);
/* Finalize i2c library. */
i2c::Finalize();
}
}
Result SetPreChargeCurrentLimit(int ma);
Result SetTerminationCurrentLimit(int ma);
Result SetMinimumSystemVoltageLimit(int mv);
Result SetChargingSafetyTimerEnabled(bool en);
Result GetForce20PercentChargeCurrent(bool *out);
Result SetForce20PercentChargeCurrent(bool en);
Result GetFastChargeCurrentLimit(int *out_ma);
Result SetFastChargeCurrentLimit(int ma);
Result GetChargeVoltageLimit(int *out_mv);
Result SetChargeVoltageLimit(int mv);
Result SetChargerConfiguration(bq24193::ChargerConfiguration cfg);
Result IsHiZEnabled(bool *out);
Result SetHiZEnabled(bool en);
Result GetInputCurrentLimit(int *out_ma);
Result SetInputCurrentLimit(int ma);
Result SetInputVoltageLimit(int mv);
Result SetBoostModeCurrentLimit(int ma);
Result GetChargerStatus(bq24193::ChargerStatus *out);
Result ResetWatchdogTimer();
Result SetWatchdogTimerSetting(int seconds);
Result GetBatteryCompensation(int *out_mo);
Result SetBatteryCompensation(int mo);
Result GetVoltageClamp(int *out_mv);
Result SetVoltageClamp(int mv);
};
}

View File

@@ -1,377 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "../../../powctl_device_management.hpp"
#include "powctl_retry_helper.hpp"
#include "powctl_charger_driver.hpp"
#include "powctl_bq24193_driver.hpp"
namespace ams::powctl::impl::board::nintendo::nx {
namespace {
constinit util::optional<ChargerDevice> g_charger_device;
constinit Bq24193Driver g_bq24193_driver;
ALWAYS_INLINE Bq24193Driver &GetBq24193Driver() {
return g_bq24193_driver;
}
}
ChargerDevice::ChargerDevice(bool ev) : m_gpio_pad_session(), m_watchdog_timer_enabled(false), m_watchdog_timer_timeout(0), m_use_event_handler(ev), m_event_handler() {
if (m_use_event_handler) {
/* Create the system event. */
os::CreateSystemEvent(std::addressof(m_system_event), os::EventClearMode_ManualClear, true);
/* Create the handler. */
m_event_handler.emplace(this);
/* Register the event handler. */
powctl::impl::RegisterInterruptHandler(std::addressof(*m_event_handler));
}
}
/* Generic API. */
void ChargerDriver::InitializeDriver() {
/* Initialize Bq24193Driver */
GetBq24193Driver().Initialize();
/* Initialize gpio library. */
gpio::Initialize();
/* Create charger device. */
g_charger_device.emplace(this->IsEventHandlerEnabled());
/* Open the device's gpio session. */
R_ABORT_UNLESS(gpio::OpenSession(g_charger_device->GetPadSession(), gpio::DeviceCode_BattChgEnableN));
/* Configure the gpio session as output. */
gpio::SetDirection(g_charger_device->GetPadSession(), gpio::Direction_Output);
/* Register our device. */
this->RegisterDevice(std::addressof(*g_charger_device));
/* Register the charger device's code. */
R_ABORT_UNLESS(powctl::impl::RegisterDeviceCode(powctl::DeviceCode_Bq24193, std::addressof(*g_charger_device)));
}
void ChargerDriver::FinalizeDriver() {
/* Unregister the charger device code. */
powctl::impl::UnregisterDeviceCode(powctl::DeviceCode_Bq24193);
/* Unregister our device. */
this->UnregisterDevice(std::addressof(*g_charger_device));
/* Close the device's gpio session. */
gpio::CloseSession(g_charger_device->GetPadSession());
/* Destroy the charger device. */
g_charger_device = util::nullopt;
/* Finalize gpio library. */
gpio::Finalize();
/* Finalize Bq24193Driver. */
GetBq24193Driver().Finalize();
}
Result ChargerDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Check that we support event handlers. */
R_UNLESS(this->IsEventHandlerEnabled(), powctl::ResultNotAvailable());
*out = device->SafeCastTo<ChargerDevice>().GetSystemEvent();
R_SUCCEED();
}
Result ChargerDriver::SetDeviceInterruptEnabled(IDevice *device, bool enable) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* Set the interrupt enable. */
device->SafeCastTo<ChargerDevice>().SetInterruptEnabled(enable);
R_SUCCEED();
}
Result ChargerDriver::GetDeviceErrorStatus(u32 *out, IDevice *device) {
/* TODO */
AMS_UNUSED(out, device);
AMS_ABORT();
}
Result ChargerDriver::SetDeviceErrorStatus(IDevice *device, u32 status) {
/* TODO */
AMS_UNUSED(device, status);
AMS_ABORT();
}
/* Charger API. */
Result ChargerDriver::GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
/* NOTE: Nintendo doesn't hold the mutex while doing the gpio:: call here, for some reason. */
/* Check if we're not charging. */
if (gpio::GetValue(device->SafeCastTo<ChargerDevice>().GetPadSession()) == gpio::GpioValue_High) {
*out = ChargeCurrentState_NotCharging;
} else {
/* Get force 20 percent charge state. */
bool force_20_percent = false;
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().GetForce20PercentChargeCurrent(std::addressof(force_20_percent)));
/* Set output appropriately. */
if (force_20_percent) {
*out = ChargeCurrentState_ChargingForce20Percent;
} else {
*out = ChargeCurrentState_Charging;
}
}
R_SUCCEED();
}
Result ChargerDriver::SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
std::scoped_lock lk(this->GetMutex());
switch (state) {
case ChargeCurrentState_NotCharging:
gpio::SetValue(device->SafeCastTo<ChargerDevice>().GetPadSession(), gpio::GpioValue_High);
break;
case ChargeCurrentState_ChargingForce20Percent:
case ChargeCurrentState_Charging:
gpio::SetValue(device->SafeCastTo<ChargerDevice>().GetPadSession(), gpio::GpioValue_Low);
AMS_POWCTL_DRIVER_R_TRY_WITH_RETRY(GetBq24193Driver().SetForce20PercentChargeCurrent(state == ChargeCurrentState_ChargingForce20Percent));
break;
case ChargeCurrentState_Unknown:
R_THROW(powctl::ResultInvalidArgument());
}
R_SUCCEED();
}
Result ChargerDriver::GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().GetFastChargeCurrentLimit(out_ma));
R_SUCCEED();
}
Result ChargerDriver::SetChargerFastChargeCurrentLimit(IDevice *device, int ma) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetFastChargeCurrentLimit(ma));
R_SUCCEED();
}
Result ChargerDriver::GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().GetChargeVoltageLimit(out_mv));
R_SUCCEED();
}
Result ChargerDriver::SetChargerChargeVoltageLimit(IDevice *device, int mv) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetChargeVoltageLimit(mv));
R_SUCCEED();
}
Result ChargerDriver::SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
bq24193::ChargerConfiguration bq_cfg;
switch (cfg) {
case ChargerConfiguration_ChargeDisable: bq_cfg = bq24193::ChargerConfiguration_ChargeDisable; break;
case ChargerConfiguration_ChargeBattery: bq_cfg = bq24193::ChargerConfiguration_ChargeBattery; break;
case ChargerConfiguration_Otg: bq_cfg = bq24193::ChargerConfiguration_Otg; break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetChargerConfiguration(bq_cfg));
R_SUCCEED();
}
Result ChargerDriver::IsChargerHiZEnabled(bool *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().IsHiZEnabled(out));
R_SUCCEED();
}
Result ChargerDriver::SetChargerHiZEnabled(IDevice *device, bool en) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetHiZEnabled(en));
R_SUCCEED();
}
Result ChargerDriver::GetChargerInputCurrentLimit(int *out_ma, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().GetInputCurrentLimit(out_ma));
R_SUCCEED();
}
Result ChargerDriver::SetChargerInputCurrentLimit(IDevice *device, int ma) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetInputCurrentLimit(ma));
R_SUCCEED();
}
Result ChargerDriver::SetChargerInputVoltageLimit(IDevice *device, int mv) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetInputVoltageLimit(mv));
R_SUCCEED();
}
Result ChargerDriver::SetChargerBoostModeCurrentLimit(IDevice *device, int ma) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetBoostModeCurrentLimit(ma));
R_SUCCEED();
}
Result ChargerDriver::GetChargerChargerStatus(ChargerStatus *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
bq24193::ChargerStatus bq_status = static_cast<bq24193::ChargerStatus>(~0);
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().GetChargerStatus(std::addressof(bq_status)));
switch (bq_status) {
case bq24193::ChargerStatus_NotCharging:
*out = ChargerStatus_NotCharging;
break;
case bq24193::ChargerStatus_PreCharge:
case bq24193::ChargerStatus_FastCharging:
*out = ChargerStatus_Charging;
break;
case bq24193::ChargerStatus_ChargeTerminationDone:
*out = ChargerStatus_ChargeTerminationDone;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
R_SUCCEED();
}
Result ChargerDriver::IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
*out = device->SafeCastTo<ChargerDevice>().IsWatchdogTimerEnabled();
R_SUCCEED();
}
Result ChargerDriver::SetChargerWatchdogTimerEnabled(IDevice *device, bool en) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
auto &charger_device = device->SafeCastTo<ChargerDevice>();
if (en) {
std::scoped_lock lk(this->GetMutex());
AMS_POWCTL_DRIVER_R_TRY_WITH_RETRY(GetBq24193Driver().ResetWatchdogTimer());
AMS_POWCTL_DRIVER_R_TRY_WITH_RETRY(GetBq24193Driver().SetWatchdogTimerSetting(charger_device.GetWatchdogTimerTimeout().GetSeconds()));
} else {
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetWatchdogTimerSetting(0));
}
charger_device.SetWatchdogTimerEnabled(en);
R_SUCCEED();
}
Result ChargerDriver::SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
device->SafeCastTo<ChargerDevice>().SetWatchdogTimerTimeout(timeout);
R_SUCCEED();
}
Result ChargerDriver::ResetChargerWatchdogTimer(IDevice *device) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().ResetWatchdogTimer());
R_SUCCEED();
}
Result ChargerDriver::GetChargerBatteryCompensation(int *out_mo, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_mo != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().GetBatteryCompensation(out_mo));
R_SUCCEED();
}
Result ChargerDriver::SetChargerBatteryCompensation(IDevice *device, int mo) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetBatteryCompensation(mo));
R_SUCCEED();
}
Result ChargerDriver::GetChargerVoltageClamp(int *out_mv, IDevice *device) {
/* Validate arguments. */
R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument());
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().GetVoltageClamp(out_mv));
R_SUCCEED();
}
Result ChargerDriver::SetChargerVoltageClamp(IDevice *device, int mv) {
/* Validate arguments. */
R_UNLESS(device != nullptr, powctl::ResultInvalidArgument());
AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(GetBq24193Driver().SetVoltageClamp(mv));
R_SUCCEED();
}
}

View File

@@ -1,160 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../../../powctl_i_power_control_driver.hpp"
#include "powctl_interrupt_event_handler.hpp"
namespace ams::powctl::impl::board::nintendo::nx {
class ChargerDevice : public powctl::impl::IDevice {
NON_COPYABLE(ChargerDevice);
NON_MOVEABLE(ChargerDevice);
AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo::nx::ChargerDevice, ::ams::powctl::impl::IDevice);
private:
gpio::GpioPadSession m_gpio_pad_session;
bool m_watchdog_timer_enabled;
TimeSpan m_watchdog_timer_timeout;
bool m_use_event_handler;
util::optional<ChargerInterruptEventHandler> m_event_handler;
os::SystemEventType m_system_event;
public:
ChargerDevice(bool ev);
bool IsWatchdogTimerEnabled() const { return m_watchdog_timer_enabled; }
void SetWatchdogTimerEnabled(bool en) { m_watchdog_timer_enabled = en; }
TimeSpan GetWatchdogTimerTimeout() const { return m_watchdog_timer_timeout; }
void SetWatchdogTimerTimeout(TimeSpan ts) { m_watchdog_timer_timeout = ts; }
gpio::GpioPadSession *GetPadSession() { return std::addressof(m_gpio_pad_session); }
os::SystemEventType *GetSystemEvent() { return std::addressof(m_system_event); }
void SetInterruptEnabled(bool en) {
if (m_use_event_handler) {
m_event_handler->SetInterruptEnabled(en);
}
}
};
class ChargerDriver : public IPowerControlDriver {
NON_COPYABLE(ChargerDriver);
NON_MOVEABLE(ChargerDriver);
AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo::nx::ChargerDriver, ::ams::powctl::impl::IPowerControlDriver);
public:
ChargerDriver(bool ev) : IPowerControlDriver(ev) { /* ... */ }
/* Generic API. */
virtual void InitializeDriver() override;
virtual void FinalizeDriver() override;
virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) override;
virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) override;
virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) override;
virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) override;
/* Charger API. */
virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) override;
virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) override;
virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) override;
virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) override;
virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) override;
virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) override;
virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) override;
virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) override;
virtual Result SetChargerHiZEnabled(IDevice *device, bool en) override;
virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) override;
virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) override;
virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) override;
virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) override;
virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) override;
virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) override;
virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) override;
virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) override;
virtual Result ResetChargerWatchdogTimer(IDevice *device) override;
virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) override;
virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) override;
virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) override;
virtual Result SetChargerVoltageClamp(IDevice *device, int mv) override;
/* Unsupported Battery API. */
virtual Result GetBatteryChargePercentage(float *out_percent, IDevice *device) override { AMS_UNUSED(out_percent, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryVoltageFuelGaugePercentage(float *out_percent, IDevice *device) override { AMS_UNUSED(out_percent, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) override { AMS_UNUSED(out_mah, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) override { AMS_UNUSED(out_mah, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryChargePercentageMinimumAlertThreshold(IDevice *device, float percentage) override { AMS_UNUSED(device, percentage); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryChargePercentageMaximumAlertThreshold(IDevice *device, float percentage) override { AMS_UNUSED(device, percentage); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryVoltageFuelGaugePercentageMinimumAlertThreshold(IDevice *device, float percentage) override { AMS_UNUSED(device, percentage); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryVoltageFuelGaugePercentageMaximumAlertThreshold(IDevice *device, float percentage) override { AMS_UNUSED(device, percentage); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryFullChargeThreshold(IDevice *device, float percentage) override { AMS_UNUSED(device, percentage); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) override { AMS_UNUSED(out_ma, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) override { AMS_UNUSED(out_ma, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) override { AMS_UNUSED(dst, out_size, device, dst_size); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) override { AMS_UNUSED(device, src, src_size); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) override { AMS_UNUSED(device, en); R_THROW(powctl::ResultNotSupported()); }
virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) override { AMS_UNUSED(device, en); R_THROW(powctl::ResultNotSupported()); }
virtual Result IsBatteryPresent(bool *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryCycles(int *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryCycles(IDevice *device, int cycles) override { AMS_UNUSED(device, cycles); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryAge(float *out_percent, IDevice *device) override { AMS_UNUSED(out_percent, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryTemperature(float *out_c, IDevice *device) override { AMS_UNUSED(out_c, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) override { AMS_UNUSED(out_c, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) override { AMS_UNUSED(device, c); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) override { AMS_UNUSED(device, c); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryVCell(int *out_mv, IDevice *device) override { AMS_UNUSED(out_mv, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) override { AMS_UNUSED(out_mv, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) override { AMS_UNUSED(out, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) override { AMS_UNUSED(device, mv); R_THROW(powctl::ResultNotSupported()); }
virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) override { AMS_UNUSED(out_mv, device); R_THROW(powctl::ResultNotSupported()); }
virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) override { AMS_UNUSED(device, mv); R_THROW(powctl::ResultNotSupported()); }
};
}

View File

@@ -1,33 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "powctl_interrupt_event_handler.hpp"
namespace ams::powctl::impl::board::nintendo::nx {
void ChargerInterruptEventHandler::SignalEvent(IDevice *device) {
/* TODO */
AMS_UNUSED(device);
AMS_ABORT();
}
void BatteryInterruptEventHandler::SignalEvent(IDevice *device) {
/* TODO */
AMS_UNUSED(device);
AMS_ABORT();
}
}

View File

@@ -1,104 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "../../../powctl_i_power_control_driver.hpp"
namespace ams::powctl::impl::board::nintendo::nx {
template<typename Derived>
class InterruptEventHandler : public ddsf::IEventHandler {
private:
IDevice *m_device;
gpio::GpioPadSession m_gpio_session;
os::SystemEventType m_gpio_system_event;
os::SdkMutex m_mutex;
public:
InterruptEventHandler(IDevice *dv) : IEventHandler(), m_device(dv), m_mutex() {
/* Initialize the gpio session. */
Derived::Initialize(std::addressof(m_gpio_session), std::addressof(m_gpio_system_event));
/* Initialize ourselves as an event handler. */
IEventHandler::Initialize(std::addressof(m_gpio_system_event));
}
os::SystemEventType *GetSystemEvent() {
return std::addressof(m_gpio_system_event);
}
void SetInterruptEnabled(bool en) {
std::scoped_lock lk(m_mutex);
gpio::SetInterruptEnable(std::addressof(m_gpio_session), en);
}
virtual void HandleEvent() override final {
/* Acquire exclusive access to ourselves. */
std::scoped_lock lk(m_mutex);
/* Clear our interrupt status. */
gpio::ClearInterruptStatus(std::addressof(m_gpio_session));
/* Clear our system event. */
os::ClearSystemEvent(std::addressof(m_gpio_system_event));
/* Signal the event. */
static_cast<Derived *>(this)->SignalEvent(m_device);
}
};
class ChargerInterruptEventHandler : public InterruptEventHandler<ChargerInterruptEventHandler> {
friend class InterruptEventHandler<ChargerInterruptEventHandler>;
private:
static void Initialize(gpio::GpioPadSession *session, os::SystemEventType *event) {
/* Open the gpio session. */
R_ABORT_UNLESS(gpio::OpenSession(session, gpio::DeviceCode_Bq24190Irq));
/* Configure the gpio session. */
gpio::SetDirection(session, gpio::Direction_Input);
gpio::SetInterruptMode(session, gpio::InterruptMode_FallingEdge);
gpio::SetInterruptEnable(session, true);
/* Bind the interrupt event. */
R_ABORT_UNLESS(gpio::BindInterrupt(event, session));
}
void SignalEvent(IDevice *device);
public:
ChargerInterruptEventHandler(IDevice *dv) : InterruptEventHandler<ChargerInterruptEventHandler>(dv) { /* ... */ }
};
class BatteryInterruptEventHandler : public InterruptEventHandler<BatteryInterruptEventHandler> {
friend class InterruptEventHandler<BatteryInterruptEventHandler>;
private:
static void Initialize(gpio::GpioPadSession *session, os::SystemEventType *event) {
/* Open the gpio session. */
R_ABORT_UNLESS(gpio::OpenSession(session, gpio::DeviceCode_BattMgicIrq));
/* Configure the gpio session. */
gpio::SetDirection(session, gpio::Direction_Input);
gpio::SetInterruptMode(session, gpio::InterruptMode_LowLevel);
/* Bind the interrupt event. */
R_ABORT_UNLESS(gpio::BindInterrupt(event, session));
}
void SignalEvent(IDevice *device);
public:
BatteryInterruptEventHandler(IDevice *dv) : InterruptEventHandler<BatteryInterruptEventHandler>(dv) { /* ... */ }
};
}

View File

@@ -1,200 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
/* 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,
};

View File

@@ -1,758 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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 FullCap0 = 0x35;
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));
R_SUCCEED();
}
ALWAYS_INLINE Result ReadRegister(const i2c::I2cSession &session, u8 address, u16 *out) {
R_RETURN(i2c::ReadSingleRegister(session, address, out));
}
ALWAYS_INLINE Result WriteRegister(const i2c::I2cSession &session, u8 address, u16 val) {
R_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)) { /* ... */ }
R_SUCCEED();
}
double CoerceToDouble(u64 value) {
static_assert(sizeof(value) == sizeof(double));
double d;
__builtin_memcpy(std::addressof(d), std::addressof(value), sizeof(d));
return d;
}
double ExponentiateTwoToPower(s16 exponent, double scale) {
if (exponent >= 1024) {
exponent = exponent - 1023;
scale = scale * 8.98846567e307;
if (exponent >= 1024) {
exponent = std::min<s16>(exponent, 2046) - 1023;
scale = scale * 8.98846567e307;
}
} else if (exponent <= -1023) {
exponent = exponent + 969;
scale = scale * 2.00416836e-292;
if (exponent <= -1023) {
exponent = std::max<s16>(exponent, -1991) + 969;
scale = scale * 2.00416836e-292;
}
}
return scale * CoerceToDouble(static_cast<u64>(exponent + 1023) << 52);
}
}
Result Max17050Driver::InitializeSession(const char *battery_vendor, u8 battery_version) {
/* Get the custom parameters. */
const auto &params = 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(m_i2c_session, max17050::Config, 0x7210));
/* Write initial filter config. */
R_TRY(WriteRegister(m_i2c_session, max17050::FilterCfg, 0x8784));
/* Write relax config. */
R_TRY(WriteRegister(m_i2c_session, max17050::RelaxCfg, params.relaxcfg));
/* Write initial learn config. */
R_TRY(WriteRegister(m_i2c_session, max17050::LearnCfg, 0x2603));
/* Write fullsocthr. */
R_TRY(WriteRegister(m_i2c_session, max17050::FullSocThr, params.fullsocthr));
/* Write iavgempty. */
R_TRY(WriteRegister(m_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(m_i2c_session, max17050::RComp0, params.rcomp0)) { /* ... */ }
/* Write and validate tempco */
while (!WriteValidateRegister(m_i2c_session, max17050::TempCo, params.tempco)) { /* ... */ }
/* Write ichgterm. */
R_TRY(WriteRegister(m_i2c_session, max17050::IChgTerm, params.ichgterm));
/* Write tgain. */
R_TRY(WriteRegister(m_i2c_session, max17050::TGain, params.tgain));
/* Write toff. */
R_TRY(WriteRegister(m_i2c_session, max17050::TOff, params.toff));
/* Write and validate vempty. */
while (!WriteValidateRegister(m_i2c_session, max17050::VEmpty, params.vempty)) { /* ... */ }
/* Write and validate qresidual. */
while (!WriteValidateRegister(m_i2c_session, max17050::QResidual00, params.qresidual00)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::QResidual10, params.qresidual10)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::QResidual20, params.qresidual20)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::QResidual30, params.qresidual30)) { /* ... */ }
/* Write capacity parameters. */
while (!WriteValidateRegister(m_i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ }
R_TRY(WriteRegister(m_i2c_session, max17050::DesignCap, params.vffullcap));
while (!WriteValidateRegister(m_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(m_i2c_session, max17050::SocVf, std::addressof(vfsoc)));
R_TRY(this->UnlockVoltageFuelGauge());
while (!WriteValidateRegister(m_i2c_session, max17050::SocVf0, vfsoc)) { /* ... */ }
R_TRY(ReadRegister(m_i2c_session, max17050::Qh, std::addressof(qh)));
R_TRY(WriteRegister(m_i2c_session, max17050::Qh0, qh));
R_TRY(this->LockVoltageFuelGauge());
}
/* Reset cycles. */
while (!WriteValidateRegister(m_i2c_session, max17050::Cycles, 0x0060)) { /* ... */ }
/* Load new capacity parameters. */
const u16 remcap = static_cast<u16>((vfsoc * params.vffullcap) / 0x6400);
const u16 repcap = static_cast<u16>(remcap * (params.fullcap / params.vffullcap));
const u16 dpacc = 0x0C80;
const u16 dqacc = params.vffullcap / 0x10;
while (!WriteValidateRegister(m_i2c_session, max17050::RemCapMix, remcap)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::RemCapRep, repcap)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::DPAcc, dpacc)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::DQAcc, dqacc)) { /* ... */ }
/* Write capacity parameters. */
while (!WriteValidateRegister(m_i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ }
R_TRY(WriteRegister(m_i2c_session, max17050::DesignCap, params.vffullcap));
while (!WriteValidateRegister(m_i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ }
/* Write soc rep. */
R_TRY(WriteRegister(m_i2c_session, max17050::SocRep, vfsoc));
/* Clear power on reset. */
R_TRY(ReadWriteValidateRegister(m_i2c_session, max17050::Status, 0x0002, 0x0000));
/* Set cgain. */
R_TRY(WriteRegister(m_i2c_session, max17050::CGain, 0x7FFF));
R_SUCCEED();
}
Result Max17050Driver::SetMaximumShutdownTimerThreshold() {
R_RETURN(WriteRegister(m_i2c_session, max17050::ShdnTimer, 0xE000));
}
Result Max17050Driver::SetAlertByChargePercentage() {
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::MiscCfg, 0x0003, 0x0000));
}
Result Max17050Driver::SetAlertByVoltageFuelGaugePercentage() {
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::MiscCfg, 0x0003, 0x0003));
}
bool Max17050Driver::IsPowerOnReset() {
/* Get the register. */
u16 val;
R_ABORT_UNLESS(ReadRegister(m_i2c_session, max17050::Status, std::addressof(val)));
/* Extract the value. */
return (val & 0x0002) != 0;
}
Result Max17050Driver::LockVoltageFuelGauge() {
R_RETURN(WriteRegister(m_i2c_session, max17050::SocVfAccess, 0x0000));
}
Result Max17050Driver::UnlockVoltageFuelGauge() {
R_RETURN(WriteRegister(m_i2c_session, max17050::SocVfAccess, 0x0080));
}
Result Max17050Driver::LockModelTable() {
R_TRY(WriteRegister(m_i2c_session, max17050::ModelAccess0, 0x0000));
R_TRY(WriteRegister(m_i2c_session, max17050::ModelAccess1, 0x0000));
R_SUCCEED();
}
Result Max17050Driver::UnlockModelTable() {
R_TRY(WriteRegister(m_i2c_session, max17050::ModelAccess0, 0x0059));
R_TRY(WriteRegister(m_i2c_session, max17050::ModelAccess1, 0x00C4));
R_SUCCEED();
}
bool Max17050Driver::IsModelTableLocked() {
for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) {
u16 val;
R_ABORT_UNLESS(ReadRegister(m_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(m_i2c_session, max17050::ModelChrTblStart + i, model_table[i]));
}
R_SUCCEED();
}
bool Max17050Driver::IsModelTableSet(const u16 *model_table) {
for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) {
u16 val;
R_ABORT_UNLESS(ReadRegister(m_i2c_session, max17050::ModelChrTblStart + i, std::addressof(val)));
if (val != model_table[i]) {
return false;
}
}
return true;
}
Result Max17050Driver::ReadInternalState() {
R_TRY(ReadRegister(m_i2c_session, max17050::RComp0, std::addressof(m_internal_state.rcomp0)));
R_TRY(ReadRegister(m_i2c_session, max17050::TempCo, std::addressof(m_internal_state.tempco)));
R_TRY(ReadRegister(m_i2c_session, max17050::FullCap, std::addressof(m_internal_state.fullcap)));
R_TRY(ReadRegister(m_i2c_session, max17050::Cycles, std::addressof(m_internal_state.cycles)));
R_TRY(ReadRegister(m_i2c_session, max17050::FullCapNom, std::addressof(m_internal_state.fullcapnom)));
R_TRY(ReadRegister(m_i2c_session, max17050::IAvgEmpty, std::addressof(m_internal_state.iavgempty)));
R_TRY(ReadRegister(m_i2c_session, max17050::QResidual00, std::addressof(m_internal_state.qresidual00)));
R_TRY(ReadRegister(m_i2c_session, max17050::QResidual10, std::addressof(m_internal_state.qresidual10)));
R_TRY(ReadRegister(m_i2c_session, max17050::QResidual20, std::addressof(m_internal_state.qresidual20)));
R_TRY(ReadRegister(m_i2c_session, max17050::QResidual30, std::addressof(m_internal_state.qresidual30)));
R_SUCCEED();
}
Result Max17050Driver::WriteInternalState() {
while (!WriteValidateRegister(m_i2c_session, max17050::RComp0, m_internal_state.rcomp0)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::TempCo, m_internal_state.tempco)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::FullCapNom, m_internal_state.fullcapnom)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::IAvgEmpty, m_internal_state.iavgempty)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::QResidual00, m_internal_state.qresidual00)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::QResidual10, m_internal_state.qresidual10)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::QResidual20, m_internal_state.qresidual20)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::QResidual30, m_internal_state.qresidual30)) { /* ... */ }
os::SleepThread(TimeSpan::FromMilliSeconds(350));
u16 fullcap0, socmix;
R_TRY(ReadRegister(m_i2c_session, max17050::FullCap0, std::addressof(fullcap0)));
R_TRY(ReadRegister(m_i2c_session, max17050::SocMix, std::addressof(socmix)));
while (!WriteValidateRegister(m_i2c_session, max17050::RemCapMix, static_cast<u16>((fullcap0 * socmix) / 0x6400))) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::FullCap, m_internal_state.fullcap)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::DPAcc, 0x0C80)) { /* ... */ }
while (!WriteValidateRegister(m_i2c_session, max17050::DQAcc, m_internal_state.fullcapnom / 0x10)) { /* ... */ }
os::SleepThread(TimeSpan::FromMilliSeconds(350));
while (!WriteValidateRegister(m_i2c_session, max17050::Cycles, m_internal_state.cycles)) { /* ... */ }
if (m_internal_state.cycles >= 0x100) {
while (!WriteValidateRegister(m_i2c_session, max17050::LearnCfg, 0x2673)) { /* ... */ }
}
R_SUCCEED();
}
Result Max17050Driver::GetChargePercentage(double *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::SocRep, std::addressof(val)));
/* Set output. */
*out = static_cast<double>(val) * 0.00390625;
R_SUCCEED();
}
Result Max17050Driver::GetVoltageFuelGaugePercentage(double *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::SocVf, std::addressof(val)));
/* Set output. */
*out = static_cast<double>(val) * 0.00390625;
R_SUCCEED();
}
Result Max17050Driver::GetFullCapacity(double *out, double sense_resistor) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(sense_resistor > 0.0);
/* Read the values. */
u16 cgain, fullcap;
R_TRY(ReadRegister(m_i2c_session, max17050::CGain, std::addressof(cgain)));
R_TRY(ReadRegister(m_i2c_session, max17050::FullCap, std::addressof(fullcap)));
/* Set output. */
*out = ((static_cast<double>(fullcap) * 0.005) / sense_resistor) / (static_cast<double>(cgain) * 0.0000610351562);
R_SUCCEED();
}
Result Max17050Driver::GetRemainingCapacity(double *out, double sense_resistor) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(sense_resistor > 0.0);
/* Read the values. */
u16 cgain, remcap;
R_TRY(ReadRegister(m_i2c_session, max17050::CGain, std::addressof(cgain)));
R_TRY(ReadRegister(m_i2c_session, max17050::RemCapRep, std::addressof(remcap)));
/* Set output. */
*out = ((static_cast<double>(remcap) * 0.005) / sense_resistor) / (static_cast<double>(cgain) * 0.0000610351562);
R_SUCCEED();
}
Result Max17050Driver::SetChargePercentageMinimumAlertThreshold(int percentage) {
R_TRY(this->SetAlertByChargePercentage());
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::SocAlrtThreshold, 0x00FF, static_cast<u8>(percentage)));
}
Result Max17050Driver::SetChargePercentageMaximumAlertThreshold(int percentage) {
R_TRY(this->SetAlertByChargePercentage());
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::SocAlrtThreshold, 0xFF00, static_cast<u16>(static_cast<u8>(percentage)) << 8));
}
Result Max17050Driver::SetVoltageFuelGaugePercentageMinimumAlertThreshold(int percentage) {
R_TRY(this->SetAlertByVoltageFuelGaugePercentage());
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::SocAlrtThreshold, 0x00FF, static_cast<u8>(percentage)));
}
Result Max17050Driver::SetVoltageFuelGaugePercentageMaximumAlertThreshold(int percentage) {
R_TRY(this->SetAlertByVoltageFuelGaugePercentage());
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::SocAlrtThreshold, 0xFF00, static_cast<u16>(static_cast<u8>(percentage)) << 8));
}
Result Max17050Driver::SetFullChargeThreshold(double percentage) {
/* Convert percentage from double to signed fixed-point with 8 fractional bits. */
const u16 val = static_cast<u16>(static_cast<s16>(percentage * (1 << 8)));
/* Set the threshold. */
R_RETURN(WriteRegister(m_i2c_session, max17050::FullSocThr, val));
}
Result Max17050Driver::GetAverageCurrent(double *out, double sense_resistor) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(sense_resistor > 0.0);
/* Read the values. */
u16 cgain, coff, avg_current;
R_TRY(ReadRegister(m_i2c_session, max17050::CGain, std::addressof(cgain)));
R_TRY(ReadRegister(m_i2c_session, max17050::COff, std::addressof(coff)));
R_TRY(ReadRegister(m_i2c_session, max17050::AverageCurrent, std::addressof(avg_current)));
/* Set output. */
*out = (((static_cast<double>(avg_current) - (static_cast<double>(coff) + static_cast<double>(coff))) / (static_cast<double>(cgain) * 0.0000610351562)) * 1.5625) / (sense_resistor * 1000.0);
R_SUCCEED();
}
Result Max17050Driver::GetCurrent(double *out, double sense_resistor) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
AMS_ABORT_UNLESS(sense_resistor > 0.0);
/* Read the values. */
u16 cgain, coff, current;
R_TRY(ReadRegister(m_i2c_session, max17050::CGain, std::addressof(cgain)));
R_TRY(ReadRegister(m_i2c_session, max17050::COff, std::addressof(coff)));
R_TRY(ReadRegister(m_i2c_session, max17050::Current, std::addressof(current)));
/* Set output. */
*out = (((static_cast<double>(current) - (static_cast<double>(coff) + static_cast<double>(coff))) / (static_cast<double>(cgain) * 0.0000610351562)) * 1.5625) / (sense_resistor * 1000.0);
R_SUCCEED();
}
Result Max17050Driver::GetNeedToRestoreParameters(bool *out) {
/* Get the register. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::MiscCfg, std::addressof(val)));
/* Extract the value. */
*out = (val & 0x8000) != 0;
R_SUCCEED();
}
Result Max17050Driver::SetNeedToRestoreParameters(bool en) {
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::MiscCfg, 0x8000, en ? 0x8000 : 0));
}
Result Max17050Driver::IsI2cShutdownEnabled(bool *out) {
/* Get the register. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::Config, std::addressof(val)));
/* Extract the value. */
*out = (val & 0x0040) != 0;
R_SUCCEED();
}
Result Max17050Driver::SetI2cShutdownEnabled(bool en) {
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::Config, 0x0040, en ? 0x0040 : 0));
}
Result Max17050Driver::GetStatus(u16 *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
R_RETURN(ReadRegister(m_i2c_session, max17050::Status, out));
}
Result Max17050Driver::GetCycles(u16 *out) {
/* Get the register. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::Cycles, std::addressof(val)));
/* Extract the value. */
*out = std::max<u16>(val, 0x60) - 0x60;
R_SUCCEED();
}
Result Max17050Driver::ResetCycles() {
R_RETURN(WriteRegister(m_i2c_session, max17050::Cycles, 0x0060));
}
Result Max17050Driver::GetAge(double *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::Age, std::addressof(val)));
/* Set output. */
*out = static_cast<double>(val) * 0.00390625;
R_SUCCEED();
}
Result Max17050Driver::GetTemperature(double *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::Temperature, std::addressof(val)));
/* Set output. */
*out = static_cast<double>(val) * 0.00390625;
R_SUCCEED();
}
Result Max17050Driver::GetMaximumTemperature(u8 *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::MaxMinTemp, std::addressof(val)));
/* Set output. */
*out = static_cast<u8>(val >> 8);
R_SUCCEED();
}
Result Max17050Driver::SetTemperatureMinimumAlertThreshold(int c) {
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::TAlrtThreshold, 0x00FF, static_cast<u8>(c)));
}
Result Max17050Driver::SetTemperatureMaximumAlertThreshold(int c) {
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::TAlrtThreshold, 0xFF00, static_cast<u16>(static_cast<u8>(c)) << 8));
}
Result Max17050Driver::GetVCell(int *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::VCell, std::addressof(val)));
/* Set output. */
*out = (625 * (val >> 3)) / 1000;
R_SUCCEED();
}
Result Max17050Driver::GetAverageVCell(int *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::AverageVCell, std::addressof(val)));
/* Set output. */
*out = (625 * (val >> 3)) / 1000;
R_SUCCEED();
}
Result Max17050Driver::GetAverageVCellTime(double *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::FilterCfg, std::addressof(val)));
/* Set output. */
*out = 175.8 * ExponentiateTwoToPower(6 + ((val >> 4) & 7), 1.0);
R_SUCCEED();
}
Result Max17050Driver::GetOpenCircuitVoltage(int *out) {
/* Validate parameters. */
AMS_ABORT_UNLESS(out != nullptr);
/* Read the value. */
u16 val;
R_TRY(ReadRegister(m_i2c_session, max17050::VFocV, std::addressof(val)));
/* Set output. */
*out = (1250 * (val >> 4)) / 1000;
R_SUCCEED();
}
Result Max17050Driver::SetVoltageMinimumAlertThreshold(int mv) {
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::VAlrtThreshold, 0x00FF, static_cast<u8>(util::DivideUp(mv, 20))));
}
Result Max17050Driver::SetVoltageMaximumAlertThreshold(int mv) {
R_RETURN(ReadWriteRegister(m_i2c_session, max17050::VAlrtThreshold, 0xFF00, static_cast<u16>(static_cast<u8>(mv / 20)) << 8));
}
}

View File

@@ -1,150 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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 {
private:
os::SdkMutex m_mutex;
int m_init_count;
i2c::I2cSession m_i2c_session;
max17050::InternalState m_internal_state;
private:
Result InitializeSession(const char *battery_vendor, u8 battery_version);
Result SetMaximumShutdownTimerThreshold();
Result SetAlertByChargePercentage();
Result SetAlertByVoltageFuelGaugePercentage();
bool IsPowerOnReset();
Result LockVoltageFuelGauge();
Result UnlockVoltageFuelGauge();
Result LockModelTable();
Result UnlockModelTable();
bool IsModelTableLocked();
Result SetModelTable(const u16 *model_table);
bool IsModelTableSet(const u16 *model_table);
public:
constexpr Max17050Driver() : m_mutex(), m_init_count(0), m_i2c_session(), m_internal_state() {
/* ... */
}
void Initialize(const char *battery_vendor, u8 battery_version) {
std::scoped_lock lk(m_mutex);
if ((m_init_count++) == 0) {
/* Initialize i2c library. */
i2c::InitializeEmpty();
/* Open session. */
R_ABORT_UNLESS(i2c::OpenSession(std::addressof(m_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(m_mutex);
if ((--m_init_count) == 0) {
/* Close session. */
i2c::CloseSession(m_i2c_session);
/* Finalize i2c library. */
i2c::Finalize();
}
}
Result ReadInternalState();
Result WriteInternalState();
void GetInternalState(max17050::InternalState *dst) {
*dst = m_internal_state;
}
void SetInternalState(const max17050::InternalState &src) {
m_internal_state = src;
}
Result GetChargePercentage(double *out);
Result GetVoltageFuelGaugePercentage(double *out);
Result GetFullCapacity(double *out, double sense_resistor);
Result GetRemainingCapacity(double *out, double sense_resistor);
Result SetChargePercentageMinimumAlertThreshold(int percentage);
Result SetChargePercentageMaximumAlertThreshold(int percentage);
Result SetVoltageFuelGaugePercentageMinimumAlertThreshold(int percentage);
Result SetVoltageFuelGaugePercentageMaximumAlertThreshold(int percentage);
Result SetFullChargeThreshold(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);
};
}

View File

@@ -1,42 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::powctl::impl {
constexpr inline const TimeSpan PowerControlRetryTimeout = TimeSpan::FromSeconds(10);
constexpr inline const TimeSpan PowerControlRetryInterval = TimeSpan::FromMilliSeconds(20);
#define AMS_POWCTL_DRIVER_R_TRY_WITH_RETRY(__EXPR__) \
({ \
TimeSpan __powctl_retry_current_time = 0; \
while (true) { \
const Result __powctl_retry_result = ( __EXPR__ ); \
if (R_SUCCEEDED(__powctl_retry_result)) { \
break; \
} \
\
__powctl_retry_current_time += PowerControlRetryInterval; \
R_UNLESS(__powctl_retry_current_time < PowerControlRetryTimeout, __powctl_retry_result); \
\
os::SleepThread(PowerControlRetryInterval); \
} \
})
#define AMS_POWCTL_DRIVER_LOCKED_R_TRY_WITH_RETRY(__EXPR__) AMS_POWCTL_DRIVER_R_TRY_WITH_RETRY( ({ std::scoped_lock lk(this->GetMutex()); ( __EXPR__ ); }) )
}

View File

@@ -1,152 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "powctl_device_management.hpp"
namespace ams::powctl::impl {
namespace {
constinit os::ThreadType g_interrupt_thread = {};
constexpr inline size_t InterruptThreadStackSize = os::MemoryPageSize;
alignas(os::MemoryPageSize) u8 g_interrupt_thread_stack[InterruptThreadStackSize];
IPowerControlDriver::List &GetDriverList() {
AMS_FUNCTION_LOCAL_STATIC_CONSTINIT(IPowerControlDriver::List, s_driver_list);
return s_driver_list;
}
ddsf::EventHandlerManager &GetInterruptHandlerManager() {
AMS_FUNCTION_LOCAL_STATIC(ddsf::EventHandlerManager, s_interrupt_handler_manager);
return s_interrupt_handler_manager;
}
ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() {
class DeviceCodeEntryManagerWithUnitHeap {
private:
u8 m_heap_memory[2_KB];
sf::UnitHeapMemoryResource m_memory_resource;
util::TypedStorage<ddsf::DeviceCodeEntryManager> m_manager;
public:
DeviceCodeEntryManagerWithUnitHeap() {
/* Initialize the memory resource. */
m_memory_resource.Attach(lmem::CreateUnitHeap(m_heap_memory, sizeof(m_heap_memory), sizeof(ddsf::DeviceCodeEntryHolder), lmem::CreateOption_ThreadSafe));
/* Construct the entry manager. */
util::ConstructAt(m_manager, std::addressof(m_memory_resource));
}
ALWAYS_INLINE operator ddsf::DeviceCodeEntryManager &() {
return util::GetReference(m_manager);
}
};
AMS_FUNCTION_LOCAL_STATIC(DeviceCodeEntryManagerWithUnitHeap, s_device_code_entry_manager_holder);
return s_device_code_entry_manager_holder;
}
void InterruptThreadFunction(void *arg) {
AMS_UNUSED(arg);
GetInterruptHandlerManager().LoopAuto();
}
}
void InitializeDrivers() {
/* Ensure the event handler manager is initialized. */
GetInterruptHandlerManager().Initialize();
/* Initialize all registered drivers. */
for (auto &driver : GetDriverList()) {
driver.SafeCastTo<IPowerControlDriver>().InitializeDriver();
}
/* Create the interrupt thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_interrupt_thread), InterruptThreadFunction, nullptr, g_interrupt_thread_stack, InterruptThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(powctl, InterruptHandler)));
os::SetThreadNamePointer(std::addressof(g_interrupt_thread), AMS_GET_SYSTEM_THREAD_NAME(powctl, InterruptHandler));
os::StartThread(std::addressof(g_interrupt_thread));
/* Wait for the interrupt thread to enter the loop. */
GetInterruptHandlerManager().WaitLoopEnter();
}
void FinalizeDrivers() {
/* Request the interrupt thread stop. */
GetInterruptHandlerManager().RequestStop();
os::WaitThread(std::addressof(g_interrupt_thread));
os::DestroyThread(std::addressof(g_interrupt_thread));
/* Reset all device code entries. */
GetDeviceCodeEntryManager().Reset();
/* Finalize all registered drivers. */
for (auto &driver : GetDriverList()) {
driver.SafeCastTo<IPowerControlDriver>().FinalizeDriver();
}
/* Finalize the interrupt handler manager. */
GetInterruptHandlerManager().Finalize();
}
void RegisterDriver(IPowerControlDriver *driver) {
AMS_ASSERT(driver != nullptr);
GetDriverList().push_back(*driver);
}
void UnregisterDriver(IPowerControlDriver *driver) {
AMS_ASSERT(driver != nullptr);
if (driver->IsLinkedToList()) {
auto &list = GetDriverList();
list.erase(list.iterator_to(*driver));
}
}
Result RegisterDeviceCode(DeviceCode device_code, IDevice *device) {
AMS_ASSERT(device != nullptr);
R_TRY(GetDeviceCodeEntryManager().Add(device_code, device));
R_SUCCEED();
}
bool UnregisterDeviceCode(DeviceCode device_code) {
return GetDeviceCodeEntryManager().Remove(device_code);
}
void RegisterInterruptHandler(ddsf::IEventHandler *handler) {
AMS_ASSERT(handler != nullptr);
GetInterruptHandlerManager().RegisterHandler(handler);
}
void UnregisterInterruptHandler(ddsf::IEventHandler *handler) {
AMS_ASSERT(handler != nullptr);
GetInterruptHandlerManager().UnregisterHandler(handler);
}
Result FindDevice(powctl::impl::IDevice **out, DeviceCode device_code) {
/* Validate output. */
AMS_ASSERT(out != nullptr);
/* Find the device. */
ddsf::IDevice *device;
R_TRY(GetDeviceCodeEntryManager().FindDevice(std::addressof(device), device_code));
/* Set output. */
*out = device->SafeCastToPointer<powctl::impl::IDevice>();
R_SUCCEED();
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "powctl_i_power_control_driver.hpp"
namespace ams::powctl::impl {
void InitializeDrivers();
void FinalizeDrivers();
void RegisterDriver(IPowerControlDriver *driver);
void UnregisterDriver(IPowerControlDriver *driver);
Result RegisterDeviceCode(DeviceCode device_code, IDevice *device);
bool UnregisterDeviceCode(DeviceCode device_code);
void RegisterInterruptHandler(ddsf::IEventHandler *handler);
void UnregisterInterruptHandler(ddsf::IEventHandler *handler);
Result FindDevice(IDevice **out, DeviceCode device_code);
}

View File

@@ -1,145 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::powctl::impl {
class IDevice : public ::ams::ddsf::IDevice {
NON_COPYABLE(IDevice);
NON_MOVEABLE(IDevice);
AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::IDevice, ::ams::ddsf::IDevice);
public:
IDevice() : ddsf::IDevice(false) { /* ... */ }
virtual ~IDevice() { /* ... */ }
};
class IPowerControlDriver : public ::ams::ddsf::IDriver {
NON_COPYABLE(IPowerControlDriver);
NON_MOVEABLE(IPowerControlDriver);
AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::IPowerControlDriver, ::ams::ddsf::IDriver);
private:
bool m_event_handler_enabled;
os::SdkMutex m_mutex;
protected:
constexpr bool IsEventHandlerEnabled() const {
return m_event_handler_enabled;
}
ALWAYS_INLINE os::SdkMutex &GetMutex() {
return m_mutex;
}
public:
IPowerControlDriver(bool ev) : IDriver(), m_event_handler_enabled(ev), m_mutex() { /* ... */ }
virtual ~IPowerControlDriver() { /* ... */ }
virtual void InitializeDriver() = 0;
virtual void FinalizeDriver() = 0;
virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) = 0;
virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) = 0;
/* TODO: Eventually implement proper error status enum? */
virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) = 0;
virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) = 0;
virtual Result GetBatteryChargePercentage(float *out_percent, IDevice *device) = 0;
virtual Result GetBatteryVoltageFuelGaugePercentage(float *out_percent, IDevice *device) = 0;
virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) = 0;
virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) = 0;
virtual Result SetBatteryChargePercentageMinimumAlertThreshold(IDevice *device, float percentage) = 0;
virtual Result SetBatteryChargePercentageMaximumAlertThreshold(IDevice *device, float percentage) = 0;
virtual Result SetBatteryVoltageFuelGaugePercentageMinimumAlertThreshold(IDevice *device, float percentage) = 0;
virtual Result SetBatteryVoltageFuelGaugePercentageMaximumAlertThreshold(IDevice *device, float percentage) = 0;
virtual Result SetBatteryFullChargeThreshold(IDevice *device, float percentage) = 0;
virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) = 0;
virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) = 0;
virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) = 0;
virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) = 0;
virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) = 0;
virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) = 0;
virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) = 0;
virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) = 0;
virtual Result SetChargerHiZEnabled(IDevice *device, bool en) = 0;
virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) = 0;
virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) = 0;
virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) = 0;
virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) = 0;
virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) = 0;
virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) = 0;
virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) = 0;
virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) = 0;
virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) = 0;
virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) = 0;
virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) = 0;
virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) = 0;
virtual Result IsBatteryPresent(bool *out, IDevice *device) = 0;
virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) = 0;
virtual Result GetBatteryCycles(int *out, IDevice *device) = 0;
virtual Result SetBatteryCycles(IDevice *device, int cycles) = 0;
virtual Result GetBatteryAge(float *out_percent, IDevice *device) = 0;
virtual Result GetBatteryTemperature(float *out_c, IDevice *device) = 0;
virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) = 0;
virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) = 0;
virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) = 0;
virtual Result GetBatteryVCell(int *out_mv, IDevice *device) = 0;
virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) = 0;
virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) = 0;
virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) = 0;
virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) = 0;
virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) = 0;
virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) = 0;
virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) = 0;
virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) = 0;
virtual Result ResetChargerWatchdogTimer(IDevice *device) = 0;
virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) = 0;
virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) = 0;
virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) = 0;
virtual Result SetChargerVoltageClamp(IDevice *device, int mv) = 0;
};
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
#include "powctl_i_power_control_driver.hpp"
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include "board/nintendo/nx/powctl_board_impl.hpp"
namespace ams::powctl::impl::board {
using namespace ams::powctl::impl::board::nintendo::nx;
}
#else
// TODO #error "Unknown board for ams::powctl::impl"
namespace ams::powctl::impl::board {
inline void Initialize(bool) {
AMS_ABORT("TODO");
}
inline void Finalize() {
AMS_ABORT("TODO");
}
}
#endif