boot: finish implementing CheckBatteryCharge

This commit is contained in:
Michael Scire 2019-05-06 23:08:28 -07:00
parent 7c36a827da
commit 3cc79f4e11
5 changed files with 270 additions and 5 deletions

View File

@ -307,3 +307,50 @@ Result BatteryDriver::IsBatteryRemoved(bool *out) {
*out = (val & 0x0008) == 0x0008;
return ResultSuccess;
}
Result BatteryDriver::GetTemperature(double *out) {
u16 val = 0;
Result rc = this->Read(Max17050Temperature, &val);
if (R_FAILED(rc)) {
return rc;
}
*out = static_cast<double>(val) * double(0.00390625);
return ResultSuccess;
}
Result BatteryDriver::GetAverageVCell(u32 *out) {
u16 val = 0;
Result rc = this->Read(Max17050AverageVCell, &val);
if (R_FAILED(rc)) {
return rc;
}
*out = (625 * u32(val >> 3)) / 1000;
return ResultSuccess;
}
Result BatteryDriver::GetSocRep(double *out) {
u16 val = 0;
Result rc = this->Read(Max17050SocRep, &val);
if (R_FAILED(rc)) {
return rc;
}
*out = static_cast<double>(val) * double(0.00390625);
return ResultSuccess;
}
Result BatteryDriver::GetBatteryPercentage(size_t *out) {
double raw_charge;
Result rc = this->GetSocRep(&raw_charge);
if (R_FAILED(rc)) {
return rc;
}
int converted_percentage = (((raw_charge - 3.93359375) * 98.0) / 94.2304688) + 2.0;
if (converted_percentage < 1) {
*out = 1;
} else if (converted_percentage > 100) {
*out = 100;
} else {
*out = static_cast<size_t>(converted_percentage);
}
return ResultSuccess;
}

View File

@ -54,4 +54,8 @@ class BatteryDriver {
public:
Result InitializeBatteryParameters();
Result IsBatteryRemoved(bool *out);
Result GetTemperature(double *out);
Result GetAverageVCell(u32 *out);
Result GetSocRep(double *out);
Result GetBatteryPercentage(size_t *out);
};

View File

@ -25,6 +25,197 @@ enum CheckBatteryResult {
CheckBatteryResult_Reboot = 2,
};
struct BatteryChargeParameters {
u32 temp_min;
u32 temp_low;
u32 temp_high;
u32 temp_max;
u32 allow_high_temp_charge_max_voltage;
u32 charge_voltage_limit_default;
u32 charge_voltage_limit_high_temp;
u32 allow_fast_charge_min_temp;
u32 allow_fast_charge_min_voltage;
u32 fast_charge_current_limit_default;
u32 fast_charge_current_limit_low_temp;
u32 fast_charge_current_limit_low_voltage;
};
static constexpr BatteryChargeParameters BatteryChargeParameters0 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 60,
.allow_high_temp_charge_max_voltage = 4050,
.charge_voltage_limit_default = 4208,
.charge_voltage_limit_high_temp = 3952,
.allow_fast_charge_min_voltage = 3320,
.fast_charge_current_limit_default = 0x800,
.fast_charge_current_limit_low_temp = 0x300,
.fast_charge_current_limit_low_voltage = 0x200,
};
static constexpr BatteryChargeParameters BatteryChargeParameters1 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 59,
.allow_high_temp_charge_max_voltage = 3984,
.charge_voltage_limit_default = 4208,
.charge_voltage_limit_high_temp = 3984,
.allow_fast_charge_min_voltage = 0,
.fast_charge_current_limit_default = 0x600,
.fast_charge_current_limit_low_temp = 0x240,
.fast_charge_current_limit_low_voltage = 0x600,
};
static constexpr BatteryChargeParameters BatteryChargeParameters2 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 59,
.allow_high_temp_charge_max_voltage = 4080,
.charge_voltage_limit_default = 4320,
.charge_voltage_limit_high_temp = 4080,
.allow_fast_charge_min_voltage = 0,
.fast_charge_current_limit_default = 0x680,
.fast_charge_current_limit_low_temp = 0x280,
.fast_charge_current_limit_low_voltage = 0x680,
};
static const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_version) {
switch (battery_version) {
case 0:
return &BatteryChargeParameters0;
case 1:
return &BatteryChargeParameters1;
case 2:
return &BatteryChargeParameters2;
default:
std::abort();
}
}
static void UpdateCharger(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit) {
double temperature;
u32 battery_voltage;
if (R_FAILED(battery_driver->GetTemperature(&temperature)) || R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
pmic_driver->ShutdownSystem();
}
bool enable_charge = true;
if (temperature < double(params->temp_min)) {
enable_charge = false;
} else if (double(params->temp_high) <= temperature && temperature < double(params->temp_max)) {
if (battery_voltage < params->allow_high_temp_charge_max_voltage) {
charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp);
} else {
enable_charge = false;
}
} else if (double(params->temp_max) <= temperature) {
enable_charge = false;
if (battery_voltage < params->allow_high_temp_charge_max_voltage) {
charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp);
}
}
u32 fast_charge_current_limit = params->fast_charge_current_limit_default;
if (temperature < double(params->temp_low)) {
fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_temp);
}
if (battery_voltage < params->allow_fast_charge_min_voltage) {
fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_voltage);
}
if (R_FAILED(charger_driver->SetChargeEnabled(enable_charge))) {
pmic_driver->ShutdownSystem();
}
if (R_FAILED(charger_driver->SetChargeVoltageLimit(charge_voltage_limit))) {
pmic_driver->ShutdownSystem();
}
if (R_FAILED(charger_driver->SetFastChargeCurrentLimit(fast_charge_current_limit))) {
pmic_driver->ShutdownSystem();
}
}
static bool IsSufficientBattery(u32 battery_voltage, bool ac_ok) {
/* Nintendo has stuff for updating a static variable every 10 seconds here, but this seems, again, to be debug leftovers. */
const u32 required_voltage = ac_ok ? 4000 : 3650;
return battery_voltage >= required_voltage;
}
static CheckBatteryResult LoopCheckBattery(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit, bool reboot_on_power_button_pressed, bool succeed_on_sufficient_battery, bool shutdown_on_full_battery, bool can_show_battery_icon, bool can_show_charging_icon) {
bool is_showing_charging_icon = false;
ON_SCOPE_EXIT {
if (is_showing_charging_icon) {
Boot::EndShowChargingIcon();
}
};
if (can_show_charging_icon) {
size_t battery_percentage;
if (R_FAILED(battery_driver->GetBatteryPercentage(&battery_percentage))) {
return CheckBatteryResult_Shutdown;
}
Boot::StartShowChargingIcon(battery_percentage, true);
is_showing_charging_icon = true;
}
while (true) {
double battery_charge;
if (R_FAILED(battery_driver->GetSocRep(&battery_charge))) {
return CheckBatteryResult_Shutdown;
}
if (succeed_on_sufficient_battery && battery_charge >= 3.0) {
return CheckBatteryResult_Success;
} else if (shutdown_on_full_battery && battery_charge >= 99.0) {
return CheckBatteryResult_Shutdown;
} else {
/* Nintendo has logic for checking a value every 10 seconds. */
/* They never do anything with this value though, so it's probably just leftovers from debug? */
}
bool ac_ok;
if (R_FAILED(pmic_driver->GetAcOk(&ac_ok))) {
return CheckBatteryResult_Shutdown;
}
u32 battery_voltage;
if (R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
return CheckBatteryResult_Shutdown;
}
if (succeed_on_sufficient_battery && IsSufficientBattery(battery_voltage, ac_ok)) {
return CheckBatteryResult_Success;
}
if (!ac_ok) {
if (can_show_battery_icon && !is_showing_charging_icon) {
Boot::ShowLowBatteryIcon();
}
return CheckBatteryResult_Shutdown;
}
if (reboot_on_power_button_pressed) {
bool power_button_pressed;
if (R_FAILED(pmic_driver->GetPowerButtonPressed(&power_button_pressed))) {
return CheckBatteryResult_Shutdown;
}
if (power_button_pressed) {
return CheckBatteryResult_Reboot;
}
}
if (can_show_battery_icon && !is_showing_charging_icon) {
Boot::StartShowChargingIcon(1, false);
is_showing_charging_icon = true;
}
svcSleepThread(2'000'000'000ul);
UpdateCharger(pmic_driver, charger_driver, battery_driver, params, charge_voltage_limit);
}
}
void Boot::CheckBatteryCharge() {
PmicDriver pmic_driver;
BatteryDriver battery_driver;
@ -51,11 +242,23 @@ void Boot::CheckBatteryCharge() {
pmic_driver.ShutdownSystem();
}
const u32 battery_version = Boot::GetBatteryVersion();
/* TODO: UpdateCharger(); */
/* TODO: LoopCheckBattery(); */
CheckBatteryResult check_result = CheckBatteryResult_Success;
const BatteryChargeParameters *params = GetBatteryChargeParameters(Boot::GetBatteryVersion());
u32 charge_voltage_limit = params->charge_voltage_limit_default;
CheckBatteryResult check_result;
if (boot_reason == 4) {
if (R_FAILED(charger_driver.GetChargeVoltageLimit(&charge_voltage_limit))) {
pmic_driver.ShutdownSystem();
}
UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit);
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, false, true, false, false);
} else {
UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit);
if (boot_reason == 1) {
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, true, false, true, true);
} else {
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, false, true, false, true, false);
}
}
switch (check_result) {
case CheckBatteryResult_Success:

View File

@ -57,6 +57,16 @@ Result PmicDriver::GetNvErc(u8 *out) {
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetPowerButtonPressed(bool *out) {
u8 power_intr;
Result rc = this->GetPowerIntr(&power_intr);
if (R_FAILED(rc)) {
return rc;
}
*out = (power_intr & 0x08) != 0;
return ResultSuccess;
}
Result PmicDriver::ShutdownSystem(bool reboot) {
/* TODO: Implement this. */
std::abort();

View File

@ -42,4 +42,5 @@ class PmicDriver {
Result GetAcOk(bool *out);
Result GetPowerIntr(u8 *out);
Result GetNvErc(u8 *out);
Result GetPowerButtonPressed(bool *out);
};