diff --git a/stratosphere/boot/source/boot_battery_driver.cpp b/stratosphere/boot/source/boot_battery_driver.cpp
index 92008927f..8c9d607c5 100644
--- a/stratosphere/boot/source/boot_battery_driver.cpp
+++ b/stratosphere/boot/source/boot_battery_driver.cpp
@@ -296,3 +296,14 @@ Result BatteryDriver::InitializeBatteryParameters() {
return ResultSuccess;
}
+
+Result BatteryDriver::IsBatteryRemoved(bool *out) {
+ /* N doesn't check result, but we will. */
+ u16 val = 0;
+ Result rc = this->Read(Max17050Status, &val);
+ if (R_FAILED(rc)) {
+ return rc;
+ }
+ *out = (val & 0x0008) == 0x0008;
+ return ResultSuccess;
+}
diff --git a/stratosphere/boot/source/boot_battery_driver.hpp b/stratosphere/boot/source/boot_battery_driver.hpp
index 2ffbc1238..f12c81e46 100644
--- a/stratosphere/boot/source/boot_battery_driver.hpp
+++ b/stratosphere/boot/source/boot_battery_driver.hpp
@@ -53,4 +53,5 @@ class BatteryDriver {
public:
Result InitializeBatteryParameters();
+ Result IsBatteryRemoved(bool *out);
};
diff --git a/stratosphere/boot/source/boot_bq24193_charger.hpp b/stratosphere/boot/source/boot_bq24193_charger.hpp
new file mode 100644
index 000000000..ad961e072
--- /dev/null
+++ b/stratosphere/boot/source/boot_bq24193_charger.hpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+static constexpr u8 Bq24193InputSourceControl = 0x00;
+static constexpr u8 Bq24193PowerOnConfiguration = 0x01;
+static constexpr u8 Bq24193ChargeCurrentControl = 0x02;
+static constexpr u8 Bq24193PreChargeTerminationCurrentControl = 0x03;
+static constexpr u8 Bq24193ChargeVoltageControl = 0x04;
+static constexpr u8 Bq24193ChargeTerminationTimerControl = 0x05;
+static constexpr u8 Bq24193IrCompensationThermalRegulationControl = 0x06;
+static constexpr u8 Bq24193MiscOperationControl = 0x07;
+static constexpr u8 Bq24193SystemStatus = 0x08;
+static constexpr u8 Bq24193Fault = 0x09;
+static constexpr u8 Bq24193VendorPartRevisionStatus = 0x0A;
+
+enum ChargerConfiguration : u8 {
+ ChargerConfiguration_ChargeDisable = (0 << 4),
+ ChargerConfiguration_ChargeBattery = (1 << 4),
+ ChargerConfiguration_Otg = (2 << 4),
+};
+
+static constexpr u32 ChargeVoltageLimitMin = 3504;
+static constexpr u32 ChargeVoltageLimitMax = 4208;
+
+static inline u8 EncodeChargeVoltageLimit(u32 voltage) {
+ if (voltage < ChargeVoltageLimitMin || voltage > ChargeVoltageLimitMax) {
+ std::abort();
+ }
+ voltage -= ChargeVoltageLimitMin;
+ voltage >>= 4;
+ return static_cast(voltage << 2);
+}
+
+static inline u32 DecodeChargeVoltageLimit(u8 reg) {
+ return ChargeVoltageLimitMin + (static_cast(reg & 0xFC) << 2);
+}
+
+static constexpr u32 FastChargeCurrentLimitMin = 512;
+static constexpr u32 FastChargeCurrentLimitMax = 4544;
+
+static inline u8 EncodeFastChargeCurrentLimit(u32 current) {
+ if (current < FastChargeCurrentLimitMin || current > FastChargeCurrentLimitMax) {
+ std::abort();
+ }
+ current -= FastChargeCurrentLimitMin;
+ current >>= 6;
+ return static_cast(current << 2);
+}
+
+static inline u32 DecodeFastChargeCurrentLimit(u8 reg) {
+ return FastChargeCurrentLimitMin + (static_cast(reg & 0xFC) << 4);
+}
+
+enum InputCurrentLimit : u8 {
+ InputCurrentLimit_100mA = 0,
+ InputCurrentLimit_150mA = 1,
+ InputCurrentLimit_500mA = 2,
+ InputCurrentLimit_900mA = 3,
+ InputCurrentLimit_1200mA = 4,
+ InputCurrentLimit_1500mA = 5,
+ InputCurrentLimit_2000mA = 6,
+ InputCurrentLimit_3000mA = 7,
+};
+
+static constexpr u32 PreChargeCurrentLimitMin = 128;
+static constexpr u32 PreChargeCurrentLimitMax = 2048;
+
+static inline u8 EncodePreChargeCurrentLimit(u32 current) {
+ if (current < PreChargeCurrentLimitMin || current > PreChargeCurrentLimitMax) {
+ std::abort();
+ }
+ current -= PreChargeCurrentLimitMin;
+ current >>= 7;
+ return static_cast(current << 4);
+}
+
+static inline u32 DecodePreChargeCurrentLimit(u8 reg) {
+ return PreChargeCurrentLimitMin + (static_cast(reg & 0xF0) << 3);
+}
+
+static constexpr u32 TerminationCurrentLimitMin = 128;
+static constexpr u32 TerminationCurrentLimitMax = 2048;
+
+static inline u8 EncodeTerminationCurrentLimit(u32 current) {
+ if (current < TerminationCurrentLimitMin || current > TerminationCurrentLimitMax) {
+ std::abort();
+ }
+ current -= TerminationCurrentLimitMin;
+ current >>= 7;
+ return static_cast(current);
+}
+
+static inline u32 DecodeTerminationCurrentLimit(u8 reg) {
+ return TerminationCurrentLimitMin + (static_cast(reg & 0xF) << 7);
+}
+
+static constexpr u32 MinimumSystemVoltageLimitMin = 3000;
+static constexpr u32 MinimumSystemVoltageLimitMax = 3700;
+
+static inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
+ if (voltage < MinimumSystemVoltageLimitMin || voltage > MinimumSystemVoltageLimitMax) {
+ std::abort();
+ }
+ voltage -= MinimumSystemVoltageLimitMin;
+ voltage /= 100;
+ return static_cast(voltage << 1);
+}
+
+static inline u32 DecodeMinimumSystemVoltageLimit(u8 reg) {
+ return MinimumSystemVoltageLimitMin + (static_cast(reg & 0x0E) * 50);
+}
+
+enum WatchdogTimerSetting : u8 {
+ WatchdogTimerSetting_Disabled = (0 << 4),
+ WatchdogTimerSetting_40s = (1 << 4),
+ WatchdogTimerSetting_80s = (2 << 4),
+ WatchdogTimerSetting_160s = (3 << 4),
+};
+
+enum BoostModeCurrentLimit : u8 {
+ BoostModeCurrentLimit_500mA = 0,
+ BoostModeCurrentLimit_1300mA = 1,
+};
\ No newline at end of file
diff --git a/stratosphere/boot/source/boot_charger_driver.cpp b/stratosphere/boot/source/boot_charger_driver.cpp
new file mode 100644
index 000000000..472ffbe5b
--- /dev/null
+++ b/stratosphere/boot/source/boot_charger_driver.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include "boot_charger_driver.hpp"
+
+Result ChargerDriver::Read(u8 addr, u8 *out) {
+ return Boot::ReadI2cRegister(this->i2c_session, reinterpret_cast(out), sizeof(*out), &addr, sizeof(addr));
+}
+
+Result ChargerDriver::Write(u8 addr, u8 val) {
+ return Boot::WriteI2cRegister(this->i2c_session, reinterpret_cast(&val), sizeof(val), &addr, sizeof(addr));
+}
+
+Result ChargerDriver::ReadWrite(u8 addr, u8 mask, u8 val) {
+ Result rc;
+ u8 cur_val;
+ if (R_FAILED((rc = this->Read(addr, &cur_val)))) {
+ return rc;
+ }
+
+ const u8 new_val = (cur_val & ~mask) | val;
+ if (R_FAILED((rc = this->Write(addr, new_val)))) {
+ return rc;
+ }
+ return ResultSuccess;
+}
+
+Result ChargerDriver::Initialize() {
+ return this->Initialize(true);
+}
+
+Result ChargerDriver::Initialize(bool set_input_current_limit) {
+ Result rc;
+ if (set_input_current_limit) {
+ if (R_FAILED((rc = this->SetInputCurrentLimit(InputCurrentLimit_500mA)))) {
+ return rc;
+ }
+ }
+
+ if (R_FAILED((rc = this->SetChargeVoltageLimit(4208)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetFastChargeCurrentLimit(512)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetForce20PercentChargeCurrent(false)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetPreChargeCurrentLimit(128)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetTerminationCurrentLimit(128)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetMinimumSystemVoltageLimit(3000)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetWatchdogTimerSetting(WatchdogTimerSetting_Disabled)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetChargingSafetyTimerEnabled(false)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->ResetWatchdogTimer()))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetBoostModeCurrentLimit(BoostModeCurrentLimit_500mA)))) {
+ return rc;
+ }
+
+ if (R_FAILED((rc = this->SetHiZEnabled(false)))) {
+ return rc;
+ }
+
+ return ResultSuccess;
+}
+
+Result ChargerDriver::SetChargeEnabled(bool enabled) {
+ Boot::GpioSetValue(GpioPadName_Bq24193Charger, enabled ? GpioValue_Low : GpioValue_High);
+ return this->SetChargerConfiguration(ChargerConfiguration_ChargeBattery);
+}
+
+Result ChargerDriver::SetChargerConfiguration(ChargerConfiguration config) {
+ return this->ReadWrite(Bq24193PowerOnConfiguration, 0x30, config);
+}
+
+Result ChargerDriver::SetChargeVoltageLimit(u32 voltage) {
+ return this->ReadWrite(Bq24193ChargeVoltageControl, 0xFC, EncodeChargeVoltageLimit(voltage));
+}
+
+Result ChargerDriver::SetFastChargeCurrentLimit(u32 current) {
+ return this->ReadWrite(Bq24193ChargeCurrentControl, 0xFC, EncodeFastChargeCurrentLimit(current));
+}
+
+Result ChargerDriver::SetInputCurrentLimit(InputCurrentLimit current) {
+ return this->ReadWrite(Bq24193InputSourceControl, 0x07, current);
+}
+
+Result ChargerDriver::SetForce20PercentChargeCurrent(bool force) {
+ return this->ReadWrite(Bq24193ChargeCurrentControl, 0x01, force ? 1 : 0);
+}
+
+Result ChargerDriver::SetPreChargeCurrentLimit(u32 current) {
+ return this->ReadWrite(Bq24193PreChargeTerminationCurrentControl, 0xF0, EncodePreChargeCurrentLimit(current));
+}
+
+Result ChargerDriver::SetTerminationCurrentLimit(u32 current) {
+ return this->ReadWrite(Bq24193PreChargeTerminationCurrentControl, 0x0F, EncodeTerminationCurrentLimit(current));
+}
+
+Result ChargerDriver::SetMinimumSystemVoltageLimit(u32 voltage) {
+ return this->ReadWrite(Bq24193PowerOnConfiguration, 0x0E, EncodeMinimumSystemVoltageLimit(voltage));
+}
+
+Result ChargerDriver::SetWatchdogTimerSetting(WatchdogTimerSetting setting) {
+ return this->ReadWrite(Bq24193ChargeTerminationTimerControl, 0x30, setting);
+}
+
+Result ChargerDriver::SetChargingSafetyTimerEnabled(bool enabled) {
+ return this->ReadWrite(Bq24193ChargeTerminationTimerControl, 0x08, enabled ? 0x08 : 0);
+}
+
+Result ChargerDriver::ResetWatchdogTimer() {
+ return this->ReadWrite(Bq24193PowerOnConfiguration, 0x40, 0x40);
+}
+
+Result ChargerDriver::SetBoostModeCurrentLimit(BoostModeCurrentLimit current) {
+ return this->ReadWrite(Bq24193PowerOnConfiguration, 0x01, current);
+}
+
+Result ChargerDriver::SetHiZEnabled(bool enabled) {
+ return this->ReadWrite(Bq24193InputSourceControl, 0x80, enabled ? 0x80 : 0);
+}
+
+Result ChargerDriver::GetInputCurrentLimit(InputCurrentLimit *out) {
+ u8 limit;
+ Result rc = this->Read(Bq24193InputSourceControl, &limit);
+ if (R_FAILED(rc)) {
+ return rc;
+ }
+ *out = static_cast(limit);
+ return ResultSuccess;
+}
+
+Result ChargerDriver::GetChargeVoltageLimit(u32 *out) {
+ u8 reg;
+ Result rc = this->Read(Bq24193ChargeVoltageControl, ®);
+ if (R_FAILED(rc)) {
+ return rc;
+ }
+ *out = DecodeChargeVoltageLimit(reg);
+ return ResultSuccess;
+}
diff --git a/stratosphere/boot/source/boot_charger_driver.hpp b/stratosphere/boot/source/boot_charger_driver.hpp
new file mode 100644
index 000000000..01885b7b3
--- /dev/null
+++ b/stratosphere/boot/source/boot_charger_driver.hpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include
+
+#include "i2c_driver/i2c_api.hpp"
+#include "boot_functions.hpp"
+#include "boot_bq24193_charger.hpp"
+
+class ChargerDriver {
+ private:
+ static constexpr u32 GpioPadName_Bq24193Charger = 0xA;
+ private:
+ I2cSessionImpl i2c_session;
+ public:
+ ChargerDriver() {
+ I2cDriver::Initialize();
+ I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max17050);
+
+ Boot::GpioSetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output);
+ }
+
+ ~ChargerDriver() {
+ I2cDriver::CloseSession(this->i2c_session);
+ I2cDriver::Finalize();
+ }
+ private:
+ Result Read(u8 addr, u8 *out_data);
+ Result Write(u8 addr, u8 val);
+ Result ReadWrite(u8 addr, u8 mask, u8 val);
+
+ Result SetInputCurrentLimit(InputCurrentLimit current);
+ Result SetForce20PercentChargeCurrent(bool force);
+ Result SetPreChargeCurrentLimit(u32 current);
+ Result SetTerminationCurrentLimit(u32 current);
+ Result SetMinimumSystemVoltageLimit(u32 voltage);
+ Result SetWatchdogTimerSetting(WatchdogTimerSetting setting);
+ Result SetChargingSafetyTimerEnabled(bool enabled);
+ Result ResetWatchdogTimer();
+ Result SetBoostModeCurrentLimit(BoostModeCurrentLimit current);
+ Result SetHiZEnabled(bool enabled);
+
+ public:
+ Result Initialize();
+ Result Initialize(bool set_input_current_limit);
+ Result SetChargeVoltageLimit(u32 voltage);
+ Result SetFastChargeCurrentLimit(u32 current);
+ Result SetChargeEnabled(bool enabled);
+ Result SetChargerConfiguration(ChargerConfiguration config);
+ Result GetInputCurrentLimit(InputCurrentLimit *out);
+ Result GetChargeVoltageLimit(u32 *out);
+};
diff --git a/stratosphere/boot/source/boot_check_battery.cpp b/stratosphere/boot/source/boot_check_battery.cpp
new file mode 100644
index 000000000..4f966c596
--- /dev/null
+++ b/stratosphere/boot/source/boot_check_battery.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "boot_functions.hpp"
+#include "boot_pmic_driver.hpp"
+#include "boot_battery_driver.hpp"
+#include "boot_charger_driver.hpp"
+
+enum CheckBatteryResult {
+ CheckBatteryResult_Success = 0,
+ CheckBatteryResult_Shutdown = 1,
+ CheckBatteryResult_Reboot = 2,
+};
+
+void Boot::CheckBatteryCharge() {
+ PmicDriver pmic_driver;
+ BatteryDriver battery_driver;
+ ChargerDriver charger_driver;
+
+ if (R_FAILED(battery_driver.InitializeBatteryParameters())) {
+ pmic_driver.ShutdownSystem();
+ }
+ {
+ bool removed;
+ if (R_FAILED(battery_driver.IsBatteryRemoved(&removed)) || removed) {
+ pmic_driver.ShutdownSystem();
+ }
+ }
+
+ const u32 boot_reason = Boot::GetBootReason();
+ InputCurrentLimit input_current_limit;
+ if (R_FAILED(charger_driver.Initialize(boot_reason != 4)) || R_FAILED(charger_driver.GetInputCurrentLimit(&input_current_limit))) {
+ pmic_driver.ShutdownSystem();
+ }
+
+ if (input_current_limit <= InputCurrentLimit_150mA) {
+ charger_driver.SetChargerConfiguration(ChargerConfiguration_ChargeDisable);
+ pmic_driver.ShutdownSystem();
+ }
+
+ const u32 battery_version = Boot::GetBatteryVersion();
+
+ /* TODO: UpdateCharger(); */
+ /* TODO: LoopCheckBattery(); */
+ CheckBatteryResult check_result = CheckBatteryResult_Success;
+
+ switch (check_result) {
+ case CheckBatteryResult_Success:
+ break;
+ case CheckBatteryResult_Shutdown:
+ pmic_driver.ShutdownSystem();
+ break;
+ case CheckBatteryResult_Reboot:
+ pmic_driver.RebootSystem();
+ break;
+ default:
+ std::abort();
+ }
+}
diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp
index c1746f687..2517ac17d 100644
--- a/stratosphere/boot/source/boot_main.cpp
+++ b/stratosphere/boot/source/boot_main.cpp
@@ -124,7 +124,7 @@ int main(int argc, char **argv)
Boot::ShowSplashScreen();
/* Check that the battery has enough to boot. */
- /* TODO: Boot::CheckBatteryCharge(); */
+ Boot::CheckBatteryCharge();
/* Configure pinmux + drive pads. */
Boot::ConfigurePinmux();