mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-06-03 08:08:39 -04:00
boot: refactor battery checking to use new powctl apis
This commit is contained in:
parent
485304bd17
commit
708f5bf1fb
44 changed files with 1426 additions and 1554 deletions
|
@ -1,299 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_battery_driver.hpp"
|
||||
#include "boot_calibration.hpp"
|
||||
#include "boot_i2c_utils.hpp"
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
/* Include configuration into anonymous namespace. */
|
||||
namespace {
|
||||
|
||||
#include "boot_battery_parameters.inc"
|
||||
|
||||
const Max17050Parameters *GetBatteryParameters() {
|
||||
const u32 battery_version = GetBatteryVersion();
|
||||
const u32 battery_vendor = GetBatteryVendor();
|
||||
|
||||
if (battery_version == 2) {
|
||||
if (battery_vendor == 'M') {
|
||||
return &Max17050Params2M;
|
||||
} else {
|
||||
return &Max17050Params2;
|
||||
}
|
||||
} else if (battery_version == 1) {
|
||||
return &Max17050Params1;
|
||||
} else {
|
||||
switch (battery_vendor) {
|
||||
case 'M':
|
||||
return &Max17050ParamsM;
|
||||
case 'R':
|
||||
return &Max17050ParamsR;
|
||||
case 'A':
|
||||
default:
|
||||
return &Max17050ParamsA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result BatteryDriver::Read(u8 addr, u16 *out) {
|
||||
return ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
Result BatteryDriver::Write(u8 addr, u16 val) {
|
||||
return WriteI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(&val), sizeof(val), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
Result BatteryDriver::ReadWrite(u8 addr, u16 mask, u16 val) {
|
||||
u16 cur_val;
|
||||
R_TRY(this->Read(addr, &cur_val));
|
||||
|
||||
const u16 new_val = (cur_val & ~mask) | val;
|
||||
R_TRY(this->Write(addr, new_val));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
bool BatteryDriver::WriteValidate(u8 addr, u16 val) {
|
||||
/* Nintendo doesn't seem to check errors when doing this? */
|
||||
/* It's probably okay, since the value does get validated. */
|
||||
/* That said, we will validate the read to avoid uninit data problems. */
|
||||
this->Write(addr, val);
|
||||
svcSleepThread(3'000'000ul);
|
||||
|
||||
u16 new_val;
|
||||
return R_SUCCEEDED(this->Read(addr, &new_val)) && new_val == val;
|
||||
}
|
||||
|
||||
bool BatteryDriver::IsPowerOnReset() {
|
||||
/* N doesn't check result... */
|
||||
u16 val = 0;
|
||||
this->Read(Max17050Status, &val);
|
||||
return (val & 0x0002) == 0x0002;
|
||||
}
|
||||
|
||||
Result BatteryDriver::LockVfSoc() {
|
||||
return this->Write(Max17050SocVfAccess, 0x0000);
|
||||
}
|
||||
|
||||
Result BatteryDriver::UnlockVfSoc() {
|
||||
return this->Write(Max17050SocVfAccess, 0x0080);
|
||||
}
|
||||
|
||||
Result BatteryDriver::LockModelTable() {
|
||||
R_TRY(this->Write(Max17050ModelAccess0, 0x0000));
|
||||
R_TRY(this->Write(Max17050ModelAccess1, 0x0000));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BatteryDriver::UnlockModelTable() {
|
||||
R_TRY(this->Write(Max17050ModelAccess0, 0x0059));
|
||||
R_TRY(this->Write(Max17050ModelAccess1, 0x00C4));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BatteryDriver::SetModelTable(const u16 *model_table) {
|
||||
for (size_t i = 0; i < Max17050ModelChrTblSize; i++) {
|
||||
R_TRY(this->Write(Max17050ModelChrTblStart + i, model_table[i]));
|
||||
}
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
bool BatteryDriver::IsModelTableLocked() {
|
||||
bool locked = true;
|
||||
|
||||
u16 cur_val = 0;
|
||||
for (size_t i = 0; i < Max17050ModelChrTblSize; i++) {
|
||||
this->Read(Max17050ModelChrTblStart + i, &cur_val);
|
||||
locked &= (cur_val == 0);
|
||||
}
|
||||
|
||||
return locked;
|
||||
}
|
||||
|
||||
bool BatteryDriver::IsModelTableSet(const u16 *model_table) {
|
||||
bool set = true;
|
||||
|
||||
u16 cur_val = 0;
|
||||
for (size_t i = 0; i < Max17050ModelChrTblSize; i++) {
|
||||
this->Read(Max17050ModelChrTblStart + i, &cur_val);
|
||||
set &= (cur_val == model_table[i]);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
Result BatteryDriver::InitializeBatteryParameters() {
|
||||
const Max17050Parameters *params = GetBatteryParameters();
|
||||
|
||||
if (IsPowerOnReset()) {
|
||||
/* Do initial config. */
|
||||
R_TRY(this->ReadWrite(Max17050MiscCfg, 0x8000, 0x8000));
|
||||
|
||||
svcSleepThread(500'000'000ul);
|
||||
|
||||
R_TRY(this->Write(Max17050Config, 0x7210));
|
||||
R_TRY(this->Write(Max17050FilterCfg, 0x8784));
|
||||
R_TRY(this->Write(Max17050RelaxCfg, params->relaxcfg));
|
||||
R_TRY(this->Write(Max17050LearnCfg, 0x2603));
|
||||
R_TRY(this->Write(Max17050FullSocThr, params->fullsocthr));
|
||||
R_TRY(this->Write(Max17050IAvgEmpty, 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 model table. */
|
||||
size_t lock_i = 0;
|
||||
while (true) {
|
||||
lock_i++;
|
||||
R_TRY(this->LockModelTable());
|
||||
|
||||
if (this->IsModelTableLocked()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (lock_i >= 8) {
|
||||
/* This is regarded as guaranteed success. */
|
||||
return ResultSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
/* Write custom parameters. */
|
||||
while (!this->WriteValidate(Max17050RComp0, params->rcomp0)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050TempCo, params->tempco)) { /* ... */ }
|
||||
|
||||
R_TRY(this->Write(Max17050IChgTerm, params->ichgterm));
|
||||
R_TRY(this->Write(Max17050TGain, params->tgain));
|
||||
R_TRY(this->Write(Max17050TOff, params->toff));
|
||||
|
||||
while (!this->WriteValidate(Max17050VEmpty, params->vempty)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050QResidual00, params->qresidual00)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050QResidual10, params->qresidual10)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050QResidual20, params->qresidual20)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050QResidual30, params->qresidual30)) { /* ... */ }
|
||||
|
||||
|
||||
/* Write full capacity parameters. */
|
||||
while (!this->WriteValidate(Max17050FullCap, params->fullcap)) { /* ... */ }
|
||||
R_TRY(this->Write(Max17050DesignCap, params->vffullcap));
|
||||
while (!this->WriteValidate(Max17050FullCapNom, params->vffullcap)) { /* ... */ }
|
||||
|
||||
svcSleepThread(350'000'000ul);
|
||||
|
||||
/* Write VFSOC to VFSOC 0. */
|
||||
u16 vfsoc, qh;
|
||||
{
|
||||
R_TRY(this->Read(Max17050SocVf, &vfsoc));
|
||||
R_TRY(this->UnlockVfSoc());
|
||||
R_TRY(this->Write(Max17050SocVf0, vfsoc));
|
||||
R_TRY(this->Read(Max17050Qh, &qh));
|
||||
R_TRY(this->Write(Max17050Qh0, qh));
|
||||
R_TRY(this->LockVfSoc());
|
||||
}
|
||||
|
||||
/* Write cycles. */
|
||||
while (!this->WriteValidate(Max17050Cycles, 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 dqacc = params->vffullcap / 0x10;
|
||||
while (!this->WriteValidate(Max17050RemCapMix, remcap)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050RemCapRep, repcap)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050DPAcc, 0x0C80)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050DQAcc, dqacc)) { /* ... */ }
|
||||
while (!this->WriteValidate(Max17050FullCap, params->fullcap)) { /* ... */ }
|
||||
R_TRY(this->Write(Max17050DesignCap, params->vffullcap));
|
||||
while (!this->WriteValidate(Max17050FullCapNom, params->vffullcap)) { /* ... */ }
|
||||
R_TRY(this->Write(Max17050SocRep, vfsoc));
|
||||
|
||||
/* Finish initialization. */
|
||||
{
|
||||
u16 status;
|
||||
R_TRY(this->Read(Max17050Status, &status));
|
||||
while (!this->WriteValidate(Max17050Status, status & 0xFFFD)) { /* ... */ }
|
||||
}
|
||||
R_TRY(this->Write(Max17050CGain, 0x7FFF));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BatteryDriver::IsBatteryRemoved(bool *out) {
|
||||
/* N doesn't check result, but we will. */
|
||||
u16 val = 0;
|
||||
R_TRY(this->Read(Max17050Status, &val));
|
||||
*out = (val & 0x0008) == 0x0008;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BatteryDriver::GetTemperature(double *out) {
|
||||
u16 val = 0;
|
||||
R_TRY(this->Read(Max17050Temperature, &val));
|
||||
*out = static_cast<double>(val) * double(0.00390625);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BatteryDriver::GetAverageVCell(u32 *out) {
|
||||
u16 val = 0;
|
||||
R_TRY(this->Read(Max17050AverageVCell, &val));
|
||||
*out = (625 * u32(val >> 3)) / 1000;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BatteryDriver::GetSocRep(double *out) {
|
||||
u16 val = 0;
|
||||
R_TRY(this->Read(Max17050SocRep, &val));
|
||||
*out = static_cast<double>(val) * double(0.00390625);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BatteryDriver::GetBatteryPercentage(size_t *out) {
|
||||
double raw_charge;
|
||||
R_TRY(this->GetSocRep(&raw_charge));
|
||||
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();
|
||||
}
|
||||
|
||||
Result BatteryDriver::SetShutdownTimer() {
|
||||
return this->Write(Max17050ShdnTimer, 0xE000);
|
||||
}
|
||||
|
||||
Result BatteryDriver::GetShutdownEnabled(bool *out) {
|
||||
u16 val = 0;
|
||||
R_TRY(this->Read(Max17050Config, &val));
|
||||
*out = (val & 0x0040) != 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BatteryDriver::SetShutdownEnabled(bool enabled) {
|
||||
return this->ReadWrite(Max17050Config, 0x0040, enabled ? 0x0040 : 0x0000);
|
||||
}
|
||||
|
||||
}
|
|
@ -14,45 +14,60 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
class BatteryDriver {
|
||||
private:
|
||||
i2c::driver::I2cSession i2c_session;
|
||||
powctl::Session battery_session;
|
||||
public:
|
||||
BatteryDriver() {
|
||||
R_ABORT_UNLESS(i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max17050));
|
||||
BatteryDriver() : battery_session() {
|
||||
R_ABORT_UNLESS(powctl::OpenSession(std::addressof(this->battery_session), powctl::DeviceCode_Max17050, ddsf::AccessMode_ReadWrite));
|
||||
}
|
||||
|
||||
~BatteryDriver() {
|
||||
i2c::driver::CloseSession(this->i2c_session);
|
||||
powctl::CloseSession(this->battery_session);
|
||||
}
|
||||
private:
|
||||
Result Read(u8 addr, u16 *out_data);
|
||||
Result Write(u8 addr, u16 val);
|
||||
Result ReadWrite(u8 addr, u16 mask, u16 val);
|
||||
bool WriteValidate(u8 addr, u16 val);
|
||||
|
||||
bool IsPowerOnReset();
|
||||
Result LockVfSoc();
|
||||
Result UnlockVfSoc();
|
||||
Result LockModelTable();
|
||||
Result UnlockModelTable();
|
||||
bool IsModelTableLocked();
|
||||
Result SetModelTable(const u16 *model_table);
|
||||
bool IsModelTableSet(const u16 *model_table);
|
||||
|
||||
public:
|
||||
Result InitializeBatteryParameters();
|
||||
Result IsBatteryRemoved(bool *out);
|
||||
Result GetTemperature(double *out);
|
||||
Result GetAverageVCell(u32 *out);
|
||||
Result GetSocRep(double *out);
|
||||
Result GetBatteryPercentage(size_t *out);
|
||||
Result SetShutdownTimer();
|
||||
Result GetShutdownEnabled(bool *out);
|
||||
Result SetShutdownEnabled(bool enabled);
|
||||
Result IsBatteryRemoved(bool *out) {
|
||||
bool present;
|
||||
R_TRY(powctl::IsBatteryPresent(std::addressof(present), this->battery_session));
|
||||
|
||||
return present == false;
|
||||
}
|
||||
|
||||
Result GetSocRep(float *out) {
|
||||
return powctl::GetBatterySocRep(out, this->battery_session);
|
||||
}
|
||||
|
||||
Result GetAverageVCell(int *out) {
|
||||
return powctl::GetBatteryAverageVCell(out, this->battery_session);
|
||||
}
|
||||
|
||||
Result GetOpenCircuitVoltage(int *out) {
|
||||
return powctl::GetBatteryOpenCircuitVoltage(out, this->battery_session);
|
||||
}
|
||||
|
||||
Result GetAverageCurrent(int *out) {
|
||||
return powctl::GetBatteryAverageCurrent(out, this->battery_session);
|
||||
}
|
||||
|
||||
Result GetCurrent(int *out) {
|
||||
return powctl::GetBatteryCurrent(out, this->battery_session);
|
||||
}
|
||||
|
||||
Result GetTemperature(float *out) {
|
||||
return powctl::GetBatteryTemperature(out, this->battery_session);
|
||||
}
|
||||
|
||||
Result IsI2cShutdownEnabled(bool *out) {
|
||||
return powctl::IsBatteryI2cShutdownEnabled(out, this->battery_session);
|
||||
}
|
||||
|
||||
Result SetI2cShutdownEnabled(bool en) {
|
||||
return powctl::SetBatteryI2cShutdownEnabled(this->battery_session, en);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@ namespace ams::boot {
|
|||
namespace {
|
||||
|
||||
/* Pull in icon definitions. */
|
||||
#include "boot_battery_icon_low.inc"
|
||||
#include "boot_battery_icon_charging.inc"
|
||||
#include "boot_battery_icon_charging_red.inc"
|
||||
#include "boot_battery_icon_low.inc"
|
||||
#include "boot_battery_icon_charging.inc"
|
||||
#include "boot_battery_icon_charging_red.inc"
|
||||
|
||||
/* Helpers. */
|
||||
void FillBatteryMeter(u32 *icon, const size_t icon_w, const size_t icon_h, const size_t meter_x, const size_t meter_y, const size_t meter_w, const size_t meter_h, const size_t fill_w) {
|
||||
|
@ -54,12 +54,12 @@ namespace ams::boot {
|
|||
{
|
||||
/* Low battery icon is shown for 5 seconds. */
|
||||
ShowDisplay(LowBatteryX, LowBatteryY, LowBatteryW, LowBatteryH, LowBattery);
|
||||
svcSleepThread(5'000'000'000ul);
|
||||
os::SleepThread(TimeSpan::FromSeconds(5));
|
||||
}
|
||||
FinalizeDisplay();
|
||||
}
|
||||
|
||||
void StartShowChargingIcon(size_t battery_percentage, bool wait) {
|
||||
void StartShowChargingIcon(int battery_percentage, bool wait) {
|
||||
const bool is_red = battery_percentage <= 15;
|
||||
|
||||
const size_t IconX = is_red ? ChargingRedBatteryX : ChargingBatteryX;
|
||||
|
@ -70,7 +70,7 @@ namespace ams::boot {
|
|||
const size_t IconMeterY = is_red ? ChargingRedBatteryMeterY : ChargingBatteryMeterY;
|
||||
const size_t IconMeterW = is_red ? ChargingRedBatteryMeterW : ChargingBatteryMeterW;
|
||||
const size_t IconMeterH = is_red ? ChargingRedBatteryMeterH : ChargingBatteryMeterH;
|
||||
const size_t MeterFillW = static_cast<size_t>(IconMeterW * (1.0 - (0.0404 + 0.0096 * battery_percentage)) + 0.5);
|
||||
const size_t MeterFillW = static_cast<size_t>(IconMeterW * (1.0 - (0.0404 + 0.0096 * static_cast<double>(battery_percentage))) + 0.5);
|
||||
|
||||
/* Create stack buffer, copy icon into it, draw fill meter, draw. */
|
||||
{
|
||||
|
@ -84,7 +84,7 @@ namespace ams::boot {
|
|||
|
||||
/* Wait for 2 seconds if we're supposed to. */
|
||||
if (wait) {
|
||||
svcSleepThread(2'000'000'000ul);
|
||||
os::SleepThread(TimeSpan::FromSeconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,12 +15,21 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_display.hpp"
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
/* Battery Display utilities. */
|
||||
void ShowLowBatteryIcon();
|
||||
void StartShowChargingIcon(size_t battery_percentage, bool wait);
|
||||
void StartShowChargingIcon(int battery_percentage, bool wait);
|
||||
void EndShowChargingIcon();
|
||||
|
||||
inline void StartShowChargingIcon(int battery_percentage) {
|
||||
return StartShowChargingIcon(battery_percentage, true);
|
||||
}
|
||||
|
||||
inline void StartShowLowBatteryChargingIcon() {
|
||||
return StartShowChargingIcon(1, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
constexpr u8 Max17050Status = 0x00;
|
||||
constexpr u8 Max17050VAlrtThreshold = 0x01;
|
||||
constexpr u8 Max17050TAlrtThreshold = 0x02;
|
||||
constexpr u8 Max17050SocAlrtThreshold = 0x03;
|
||||
constexpr u8 Max17050AtRate = 0x04;
|
||||
constexpr u8 Max17050RemCapRep = 0x05;
|
||||
constexpr u8 Max17050SocRep = 0x06;
|
||||
constexpr u8 Max17050Age = 0x07;
|
||||
constexpr u8 Max17050Temperature = 0x08;
|
||||
constexpr u8 Max17050VCell = 0x09;
|
||||
constexpr u8 Max17050Current = 0x0A;
|
||||
constexpr u8 Max17050AverageCurrent = 0x0B;
|
||||
|
||||
constexpr u8 Max17050SocMix = 0x0D;
|
||||
constexpr u8 Max17050SocAv = 0x0E;
|
||||
constexpr u8 Max17050RemCapMix = 0x0F;
|
||||
constexpr u8 Max17050FullCap = 0x10;
|
||||
constexpr u8 Max17050Tte = 0x11;
|
||||
constexpr u8 Max17050QResidual00 = 0x12;
|
||||
constexpr u8 Max17050FullSocThr = 0x13;
|
||||
|
||||
|
||||
constexpr u8 Max17050AverageTemp = 0x16;
|
||||
constexpr u8 Max17050Cycles = 0x17;
|
||||
constexpr u8 Max17050DesignCap = 0x18;
|
||||
constexpr u8 Max17050AverageVCell = 0x19;
|
||||
constexpr u8 Max17050MaxMinTemp = 0x1A;
|
||||
constexpr u8 Max17050MaxMinVoltage = 0x1B;
|
||||
constexpr u8 Max17050MaxMinCurrent = 0x1C;
|
||||
constexpr u8 Max17050Config = 0x1D;
|
||||
constexpr u8 Max17050IChgTerm = 0x1E;
|
||||
constexpr u8 Max17050RemCapAv = 0x1F;
|
||||
|
||||
constexpr u8 Max17050Version = 0x21;
|
||||
constexpr u8 Max17050QResidual10 = 0x22;
|
||||
constexpr u8 Max17050FullCapNom = 0x23;
|
||||
constexpr u8 Max17050TempNom = 0x24;
|
||||
constexpr u8 Max17050TempLim = 0x25;
|
||||
|
||||
constexpr u8 Max17050Ain = 0x27;
|
||||
constexpr u8 Max17050LearnCfg = 0x28;
|
||||
constexpr u8 Max17050FilterCfg = 0x29;
|
||||
constexpr u8 Max17050RelaxCfg = 0x2A;
|
||||
constexpr u8 Max17050MiscCfg = 0x2B;
|
||||
constexpr u8 Max17050TGain = 0x2C;
|
||||
constexpr u8 Max17050TOff = 0x2D;
|
||||
constexpr u8 Max17050CGain = 0x2E;
|
||||
constexpr u8 Max17050COff = 0x2F;
|
||||
|
||||
|
||||
constexpr u8 Max17050QResidual20 = 0x32;
|
||||
|
||||
|
||||
|
||||
constexpr u8 Max17050IAvgEmpty = 0x36;
|
||||
constexpr u8 Max17050FCtc = 0x37;
|
||||
constexpr u8 Max17050RComp0 = 0x38;
|
||||
constexpr u8 Max17050TempCo = 0x39;
|
||||
constexpr u8 Max17050VEmpty = 0x3A;
|
||||
|
||||
|
||||
constexpr u8 Max17050FStat = 0x3D;
|
||||
constexpr u8 Max17050Timer = 0x3E;
|
||||
constexpr u8 Max17050ShdnTimer = 0x3F;
|
||||
|
||||
|
||||
constexpr u8 Max17050QResidual30 = 0x42;
|
||||
|
||||
|
||||
constexpr u8 Max17050DQAcc = 0x45;
|
||||
constexpr u8 Max17050DPAcc = 0x46;
|
||||
|
||||
constexpr u8 Max17050SocVf0 = 0x48;
|
||||
|
||||
constexpr u8 Max17050Qh0 = 0x4C;
|
||||
constexpr u8 Max17050Qh = 0x4D;
|
||||
|
||||
constexpr u8 Max17050SocVfAccess = 0x60;
|
||||
|
||||
constexpr u8 Max17050ModelAccess0 = 0x62;
|
||||
constexpr u8 Max17050ModelAccess1 = 0x63;
|
||||
|
||||
constexpr u8 Max17050ModelChrTblStart = 0x80;
|
||||
constexpr u8 Max17050ModelChrTblEnd = 0xB0;
|
||||
|
||||
|
||||
constexpr u8 Max17050VFocV = 0xFB;
|
||||
constexpr u8 Max17050SocVf = 0xFF;
|
||||
|
||||
constexpr size_t Max17050ModelChrTblSize = Max17050ModelChrTblEnd - Max17050ModelChrTblStart;
|
||||
|
||||
struct Max17050Parameters {
|
||||
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[Max17050ModelChrTblSize];
|
||||
u16 fullsocthr;
|
||||
u16 iavgempty;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Max17050Parameters) == 0x7E, "Max17050Parameters definition!");
|
||||
|
||||
constexpr Max17050Parameters Max17050ParamsA = {
|
||||
0x203B, /* relaxcfg */
|
||||
0x0053, /* rcomp0 */
|
||||
0x1C22, /* tempco */
|
||||
0x0333, /* ichgterm */
|
||||
0xE1F6, /* tgain */
|
||||
0x2BF2, /* toff */
|
||||
0xA05F, /* vempty */
|
||||
0x5786, /* qresidual00 */
|
||||
0x3184, /* qresidual10 */
|
||||
0x1E00, /* qresidual20 */
|
||||
0x1602, /* qresidual30 */
|
||||
0x2476, /* fullcap */
|
||||
0x2476, /* vffullcap */
|
||||
{ /* 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,
|
||||
},
|
||||
0x5F00, /* fullsocthr */
|
||||
0x1D2A /* iavgempty */
|
||||
};
|
||||
|
||||
constexpr Max17050Parameters Max17050ParamsM = {
|
||||
0x203B, /* relaxcfg */
|
||||
0x0085, /* rcomp0 */
|
||||
0x1625, /* tempco */
|
||||
0x0333, /* ichgterm */
|
||||
0xE1F6, /* tgain */
|
||||
0x2BF2, /* toff */
|
||||
0xA05F, /* vempty */
|
||||
0x3100, /* qresidual00 */
|
||||
0x1B00, /* qresidual10 */
|
||||
0x1000, /* qresidual20 */
|
||||
0x0C81, /* qresidual30 */
|
||||
0x227A, /* fullcap */
|
||||
0x227A, /* vffullcap */
|
||||
{ /* 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,
|
||||
},
|
||||
0x5F00, /* fullsocthr */
|
||||
0x1D2A /* iavgempty */
|
||||
};
|
||||
|
||||
constexpr Max17050Parameters Max17050ParamsR = {
|
||||
0x203B, /* relaxcfg */
|
||||
0x0048, /* rcomp0 */
|
||||
0x2034, /* tempco */
|
||||
0x0333, /* ichgterm */
|
||||
0xE1F6, /* tgain */
|
||||
0x2BF2, /* toff */
|
||||
0xA05F, /* vempty */
|
||||
0x5A00, /* qresidual00 */
|
||||
0x3B00, /* qresidual10 */
|
||||
0x0F80, /* qresidual20 */
|
||||
0x0B02, /* qresidual30 */
|
||||
0x2466, /* fullcap */
|
||||
0x2466, /* vffullcap */
|
||||
{ /* 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,
|
||||
},
|
||||
0x5F00, /* fullsocthr */
|
||||
0x1D2A /* iavgempty */
|
||||
};
|
||||
|
||||
constexpr Max17050Parameters Max17050Params1 = {
|
||||
0x203B, /* relaxcfg */
|
||||
0x0040, /* rcomp0 */
|
||||
0x1624, /* tempco */
|
||||
0x0333, /* ichgterm */
|
||||
0xE1F6, /* tgain */
|
||||
0x2BF2, /* toff */
|
||||
0xA05F, /* vempty */
|
||||
0x4690, /* qresidual00 */
|
||||
0x2605, /* qresidual10 */
|
||||
0x1605, /* qresidual20 */
|
||||
0x0F05, /* qresidual30 */
|
||||
0x1AE4, /* fullcap */
|
||||
0x1AE4, /* vffullcap */
|
||||
{ /* 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,
|
||||
},
|
||||
0x5F00, /* fullsocthr */
|
||||
0x1584 /* iavgempty */
|
||||
};
|
||||
|
||||
constexpr Max17050Parameters Max17050Params2 = {
|
||||
0x203B, /* relaxcfg */
|
||||
0x004A, /* rcomp0 */
|
||||
0x1D23, /* tempco */
|
||||
0x0333, /* ichgterm */
|
||||
0xE1F6, /* tgain */
|
||||
0x2BF2, /* toff */
|
||||
0xA05F, /* vempty */
|
||||
0x4000, /* qresidual00 */
|
||||
0x1E80, /* qresidual10 */
|
||||
0x0D83, /* qresidual20 */
|
||||
0x0783, /* qresidual30 */
|
||||
0x1C20, /* fullcap */
|
||||
0x1C20, /* vffullcap */
|
||||
{ /* 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,
|
||||
},
|
||||
0x5500, /* fullsocthr */
|
||||
0x1680 /* iavgempty */
|
||||
};
|
||||
|
||||
constexpr Max17050Parameters Max17050Params2M = {
|
||||
0x203B, /* relaxcfg */
|
||||
0x0049, /* rcomp0 */
|
||||
0x222A, /* tempco */
|
||||
0x0333, /* ichgterm */
|
||||
0xE1F6, /* tgain */
|
||||
0x2BF2, /* toff */
|
||||
0xA05F, /* vempty */
|
||||
0x4F00, /* qresidual00 */
|
||||
0x2680, /* qresidual10 */
|
||||
0x1205, /* qresidual20 */
|
||||
0x0C87, /* qresidual30 */
|
||||
0x1C68, /* fullcap */
|
||||
0x1C68, /* vffullcap */
|
||||
{ /* 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,
|
||||
},
|
||||
0x5500, /* fullsocthr */
|
||||
0x16B9 /* iavgempty */
|
||||
};
|
|
@ -23,29 +23,29 @@ namespace ams::boot {
|
|||
namespace {
|
||||
|
||||
/* Globals. */
|
||||
u32 g_boot_reason = 0;
|
||||
bool g_detected_boot_reason = false;
|
||||
constinit spl::BootReason g_boot_reason = spl::BootReason_Unknown;
|
||||
constinit bool g_detected_boot_reason = false;
|
||||
|
||||
/* Helpers. */
|
||||
u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
|
||||
spl::BootReason DetectBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
|
||||
if (power_intr & 0x08) {
|
||||
return 2;
|
||||
return spl::BootReason_OnKey;
|
||||
}
|
||||
if (rtc_intr & 0x02) {
|
||||
return 3;
|
||||
return spl::BootReason_RtcAlarm1;
|
||||
}
|
||||
if (power_intr & 0x80) {
|
||||
return 1;
|
||||
return spl::BootReason_AcOk;
|
||||
}
|
||||
if (rtc_intr & 0x04) {
|
||||
if (nv_erc != 0x80 && !spl::IsRecoveryBoot()) {
|
||||
return 4;
|
||||
return spl::BootReason_RtcAlarm2;
|
||||
}
|
||||
}
|
||||
if ((nv_erc & 0x40) && ac_ok) {
|
||||
return 1;
|
||||
return spl::BootReason_AcOk;
|
||||
}
|
||||
return 0;
|
||||
return spl::BootReason_Unknown;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,37 +60,38 @@ namespace ams::boot {
|
|||
/* Get values from PMIC. */
|
||||
{
|
||||
PmicDriver pmic_driver;
|
||||
R_ABORT_UNLESS(pmic_driver.GetPowerIntr(&power_intr));
|
||||
R_ABORT_UNLESS(pmic_driver.GetNvErc(&nv_erc));
|
||||
R_ABORT_UNLESS(pmic_driver.GetAcOk(&ac_ok));
|
||||
R_ABORT_UNLESS(pmic_driver.GetOnOffIrq(std::addressof(power_intr)));
|
||||
R_ABORT_UNLESS(pmic_driver.GetNvErc(std::addressof(nv_erc)));
|
||||
R_ABORT_UNLESS(pmic_driver.GetAcOk(std::addressof(ac_ok)));
|
||||
}
|
||||
|
||||
/* Get values from RTC. */
|
||||
{
|
||||
RtcDriver rtc_driver;
|
||||
R_ABORT_UNLESS(rtc_driver.GetRtcIntr(&rtc_intr));
|
||||
R_ABORT_UNLESS(rtc_driver.GetRtcIntrM(&rtc_intr_m));
|
||||
R_ABORT_UNLESS(rtc_driver.GetRtcIntr(std::addressof(rtc_intr)));
|
||||
R_ABORT_UNLESS(rtc_driver.GetRtcIntrM(std::addressof(rtc_intr_m)));
|
||||
}
|
||||
|
||||
/* Set global derived boot reason. */
|
||||
g_boot_reason = MakeBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok);
|
||||
g_boot_reason = DetectBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok);
|
||||
|
||||
/* Set boot reason for SPL. */
|
||||
if (hos::GetVersion() >= hos::Version_3_0_0) {
|
||||
/* Create the boot reason value. */
|
||||
spl::BootReasonValue boot_reason_value = {};
|
||||
|
||||
boot_reason_value.power_intr = power_intr;
|
||||
boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m;
|
||||
boot_reason_value.nv_erc = nv_erc;
|
||||
boot_reason_value.boot_reason = g_boot_reason;
|
||||
|
||||
/* Set the boot reason value. */
|
||||
R_ABORT_UNLESS(spl::SetBootReason(boot_reason_value));
|
||||
}
|
||||
|
||||
g_detected_boot_reason = true;
|
||||
}
|
||||
|
||||
u32 GetBootReason() {
|
||||
spl::BootReason GetBootReason() {
|
||||
AMS_ABORT_UNLESS(g_detected_boot_reason);
|
||||
return g_boot_reason;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,6 @@ namespace ams::boot {
|
|||
|
||||
/* Boot Reason utilities. */
|
||||
void DetectBootReason();
|
||||
u32 GetBootReason();
|
||||
spl::BootReason GetBootReason();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::boot::bq24193 {
|
||||
|
||||
constexpr u8 InputSourceControl = 0x00;
|
||||
constexpr u8 PowerOnConfiguration = 0x01;
|
||||
constexpr u8 ChargeCurrentControl = 0x02;
|
||||
constexpr u8 PreChargeTerminationCurrentControl = 0x03;
|
||||
constexpr u8 ChargeVoltageControl = 0x04;
|
||||
constexpr u8 ChargeTerminationTimerControl = 0x05;
|
||||
constexpr u8 IrCompensationThermalRegulationControl = 0x06;
|
||||
constexpr u8 MiscOperationControl = 0x07;
|
||||
constexpr u8 SystemStatus = 0x08;
|
||||
constexpr u8 Fault = 0x09;
|
||||
constexpr u8 VendorPartRevisionStatus = 0x0A;
|
||||
|
||||
enum ChargerConfiguration : u8 {
|
||||
ChargerConfiguration_ChargeDisable = (0 << 4),
|
||||
ChargerConfiguration_ChargeBattery = (1 << 4),
|
||||
ChargerConfiguration_Otg = (2 << 4),
|
||||
};
|
||||
|
||||
constexpr u32 ChargeVoltageLimitMin = 3504;
|
||||
constexpr u32 ChargeVoltageLimitMax = 4208;
|
||||
|
||||
inline u8 EncodeChargeVoltageLimit(u32 voltage) {
|
||||
AMS_ABORT_UNLESS(voltage >= ChargeVoltageLimitMin);
|
||||
AMS_ABORT_UNLESS(voltage <= ChargeVoltageLimitMax);
|
||||
|
||||
voltage -= ChargeVoltageLimitMin;
|
||||
voltage >>= 4;
|
||||
return static_cast<u8>(voltage << 2);
|
||||
}
|
||||
|
||||
inline u32 DecodeChargeVoltageLimit(u8 reg) {
|
||||
return ChargeVoltageLimitMin + (static_cast<u32>(reg & 0xFC) << 2);
|
||||
}
|
||||
|
||||
constexpr u32 FastChargeCurrentLimitMin = 512;
|
||||
constexpr u32 FastChargeCurrentLimitMax = 4544;
|
||||
|
||||
inline u8 EncodeFastChargeCurrentLimit(u32 current) {
|
||||
AMS_ABORT_UNLESS(current >= FastChargeCurrentLimitMin);
|
||||
AMS_ABORT_UNLESS(current <= FastChargeCurrentLimitMax);
|
||||
|
||||
current -= FastChargeCurrentLimitMin;
|
||||
current >>= 6;
|
||||
return static_cast<u8>(current << 2);
|
||||
}
|
||||
|
||||
inline u32 DecodeFastChargeCurrentLimit(u8 reg) {
|
||||
return FastChargeCurrentLimitMin + (static_cast<u32>(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,
|
||||
};
|
||||
|
||||
constexpr u32 PreChargeCurrentLimitMin = 128;
|
||||
constexpr u32 PreChargeCurrentLimitMax = 2048;
|
||||
|
||||
inline u8 EncodePreChargeCurrentLimit(u32 current) {
|
||||
AMS_ABORT_UNLESS(current >= PreChargeCurrentLimitMin);
|
||||
AMS_ABORT_UNLESS(current <= PreChargeCurrentLimitMax);
|
||||
|
||||
current -= PreChargeCurrentLimitMin;
|
||||
current >>= 7;
|
||||
return static_cast<u8>(current << 4);
|
||||
}
|
||||
|
||||
inline u32 DecodePreChargeCurrentLimit(u8 reg) {
|
||||
return PreChargeCurrentLimitMin + (static_cast<u32>(reg & 0xF0) << 3);
|
||||
}
|
||||
|
||||
constexpr u32 TerminationCurrentLimitMin = 128;
|
||||
constexpr u32 TerminationCurrentLimitMax = 2048;
|
||||
|
||||
inline u8 EncodeTerminationCurrentLimit(u32 current) {
|
||||
AMS_ABORT_UNLESS(current >= TerminationCurrentLimitMin);
|
||||
AMS_ABORT_UNLESS(current <= TerminationCurrentLimitMax);
|
||||
|
||||
current -= TerminationCurrentLimitMin;
|
||||
current >>= 7;
|
||||
return static_cast<u8>(current);
|
||||
}
|
||||
|
||||
inline u32 DecodeTerminationCurrentLimit(u8 reg) {
|
||||
return TerminationCurrentLimitMin + (static_cast<u32>(reg & 0xF) << 7);
|
||||
}
|
||||
|
||||
constexpr u32 MinimumSystemVoltageLimitMin = 3000;
|
||||
constexpr u32 MinimumSystemVoltageLimitMax = 3700;
|
||||
|
||||
inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
|
||||
AMS_ABORT_UNLESS(voltage >= MinimumSystemVoltageLimitMin);
|
||||
AMS_ABORT_UNLESS(voltage <= MinimumSystemVoltageLimitMax);
|
||||
|
||||
voltage -= MinimumSystemVoltageLimitMin;
|
||||
voltage /= 100;
|
||||
return static_cast<u8>(voltage << 1);
|
||||
}
|
||||
|
||||
inline u32 DecodeMinimumSystemVoltageLimit(u8 reg) {
|
||||
return MinimumSystemVoltageLimitMin + (static_cast<u32>(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,
|
||||
};
|
||||
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_calibration.hpp"
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience definitions. */
|
||||
constexpr size_t BatteryLotOffset = 0x2CE0;
|
||||
constexpr size_t BatteryLotSize = 0x20;
|
||||
constexpr size_t BatteryVersionOffset = 0x4310;
|
||||
constexpr size_t BatteryVersionSize = 0x10;
|
||||
|
||||
constexpr u32 DefaultBatteryVendor = static_cast<u32>('A');
|
||||
constexpr u32 DefaultBatteryVersion = 0;
|
||||
|
||||
/* Helpers. */
|
||||
constexpr u16 GetCrc16(const void *data, size_t size) {
|
||||
constexpr u16 s_crc_table[0x10] = {
|
||||
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
|
||||
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
|
||||
};
|
||||
|
||||
AMS_ABORT_UNLESS(data != nullptr);
|
||||
|
||||
u16 crc16 = 0x55AA;
|
||||
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[data_u8[i] & 0xF]);
|
||||
crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[(data_u8[i] >> 4) & 0xF]);
|
||||
}
|
||||
return crc16;
|
||||
}
|
||||
|
||||
Result ValidateCalibrationCrc16(const void *data, size_t size) {
|
||||
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
|
||||
const bool crc_valid = GetCrc16(data, size - sizeof(u16)) == *(reinterpret_cast<const u16 *>(&data_u8[size - sizeof(u16)]));
|
||||
R_UNLESS(crc_valid, cal::ResultCalibrationDataCrcError());
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetBatteryVendorImpl(u32 *vendor) {
|
||||
FsStorage s;
|
||||
R_TRY(fsOpenBisStorage(&s, FsBisPartitionId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(&s); };
|
||||
|
||||
u8 battery_lot[BatteryLotSize];
|
||||
R_TRY(fsStorageRead(&s, BatteryLotOffset, battery_lot, sizeof(battery_lot)));
|
||||
|
||||
R_TRY(ValidateCalibrationCrc16(battery_lot, sizeof(battery_lot)));
|
||||
|
||||
*vendor = battery_lot[7];
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetBatteryVersionImpl(u32 *version) {
|
||||
FsStorage s;
|
||||
R_TRY(fsOpenBisStorage(&s, FsBisPartitionId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(&s); };
|
||||
|
||||
u8 battery_version[BatteryVersionSize];
|
||||
R_TRY(fsStorageRead(&s, BatteryVersionOffset, battery_version, sizeof(battery_version)));
|
||||
|
||||
R_TRY(ValidateCalibrationCrc16(battery_version, sizeof(battery_version)));
|
||||
|
||||
*version = battery_version[0];
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u32 GetBatteryVendor() {
|
||||
u32 vendor;
|
||||
if (R_FAILED(GetBatteryVendorImpl(&vendor))) {
|
||||
return DefaultBatteryVendor;
|
||||
}
|
||||
return vendor;
|
||||
}
|
||||
|
||||
u32 GetBatteryVersion() {
|
||||
u32 version;
|
||||
if (R_FAILED(GetBatteryVersionImpl(&version))) {
|
||||
return DefaultBatteryVersion;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
/* Calibration utilities. */
|
||||
u32 GetBatteryVersion();
|
||||
u32 GetBatteryVendor();
|
||||
|
||||
}
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_change_voltage.hpp"
|
||||
#include "boot_pmc_wrapper.hpp"
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
|
@ -36,8 +35,8 @@ namespace ams::boot {
|
|||
|
||||
void ChangeGpioVoltageTo1_8v() {
|
||||
/* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */
|
||||
WritePmcRegister(PmcPwrDet, VoltageChangeMask, VoltageChangeMask);
|
||||
WritePmcRegister(PmcPwrDetVal, 0, VoltageChangeMask);
|
||||
dd::ReadModifyWriteIoRegister(PmcPwrDet, VoltageChangeMask, VoltageChangeMask);
|
||||
dd::ReadModifyWriteIoRegister(PmcPwrDetVal, 0, VoltageChangeMask);
|
||||
|
||||
/* Sleep for 100 us. */
|
||||
svcSleepThread(100'000ul);
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_charger_driver.hpp"
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
Result ChargerDriver::Read(u8 addr, u8 *out) {
|
||||
return ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
Result ChargerDriver::Write(u8 addr, u8 val) {
|
||||
return WriteI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(&val), sizeof(val), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
Result ChargerDriver::ReadWrite(u8 addr, u8 mask, u8 val) {
|
||||
u8 cur_val;
|
||||
R_TRY(this->Read(addr, &cur_val));
|
||||
|
||||
const u8 new_val = (cur_val & ~mask) | val;
|
||||
R_TRY(this->Write(addr, new_val));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ChargerDriver::Initialize() {
|
||||
return this->Initialize(true);
|
||||
}
|
||||
|
||||
Result ChargerDriver::Initialize(bool set_input_current_limit) {
|
||||
if (set_input_current_limit) {
|
||||
R_TRY(this->SetInputCurrentLimit(bq24193::InputCurrentLimit_500mA));
|
||||
}
|
||||
|
||||
R_TRY(this->SetChargeVoltageLimit(4208));
|
||||
R_TRY(this->SetFastChargeCurrentLimit(512));
|
||||
R_TRY(this->SetForce20PercentChargeCurrent(false));
|
||||
R_TRY(this->SetPreChargeCurrentLimit(128));
|
||||
R_TRY(this->SetTerminationCurrentLimit(128));
|
||||
R_TRY(this->SetMinimumSystemVoltageLimit(3000));
|
||||
R_TRY(this->SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting_Disabled));
|
||||
R_TRY(this->SetChargingSafetyTimerEnabled(false));
|
||||
R_TRY(this->ResetWatchdogTimer());
|
||||
R_TRY(this->SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit_500mA));
|
||||
R_TRY(this->SetHiZEnabled(false));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetChargeEnabled(bool enabled) {
|
||||
/* TODO */
|
||||
AMS_ABORT();
|
||||
/* boot::gpio::SetValue(GpioPadName_Bq24193Charger, enabled ? GpioValue_Low : GpioValue_High); */
|
||||
return this->SetChargerConfiguration(bq24193::ChargerConfiguration_ChargeBattery);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetChargerConfiguration(bq24193::ChargerConfiguration config) {
|
||||
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x30, config);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetChargeVoltageLimit(u32 voltage) {
|
||||
return this->ReadWrite(bq24193::ChargeVoltageControl, 0xFC, bq24193::EncodeChargeVoltageLimit(voltage));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetFastChargeCurrentLimit(u32 current) {
|
||||
return this->ReadWrite(bq24193::ChargeCurrentControl, 0xFC, bq24193::EncodeFastChargeCurrentLimit(current));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetInputCurrentLimit(bq24193::InputCurrentLimit current) {
|
||||
return this->ReadWrite(bq24193::InputSourceControl, 0x07, current);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetForce20PercentChargeCurrent(bool force) {
|
||||
return this->ReadWrite(bq24193::ChargeCurrentControl, 0x01, force ? 1 : 0);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetPreChargeCurrentLimit(u32 current) {
|
||||
return this->ReadWrite(bq24193::PreChargeTerminationCurrentControl, 0xF0, bq24193::EncodePreChargeCurrentLimit(current));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetTerminationCurrentLimit(u32 current) {
|
||||
return this->ReadWrite(bq24193::PreChargeTerminationCurrentControl, 0x0F, bq24193::EncodeTerminationCurrentLimit(current));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetMinimumSystemVoltageLimit(u32 voltage) {
|
||||
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x0E, bq24193::EncodeMinimumSystemVoltageLimit(voltage));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting setting) {
|
||||
return this->ReadWrite(bq24193::ChargeTerminationTimerControl, 0x30, setting);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetChargingSafetyTimerEnabled(bool enabled) {
|
||||
return this->ReadWrite(bq24193::ChargeTerminationTimerControl, 0x08, enabled ? 0x08 : 0);
|
||||
}
|
||||
|
||||
Result ChargerDriver::ResetWatchdogTimer() {
|
||||
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x40, 0x40);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit current) {
|
||||
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x01, current);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetHiZEnabled(bool enabled) {
|
||||
return this->ReadWrite(bq24193::InputSourceControl, 0x80, enabled ? 0x80 : 0);
|
||||
}
|
||||
|
||||
Result ChargerDriver::GetInputCurrentLimit(bq24193::InputCurrentLimit *out) {
|
||||
u8 limit;
|
||||
R_TRY(this->Read(bq24193::InputSourceControl, &limit));
|
||||
*out = static_cast<bq24193::InputCurrentLimit>(limit);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result ChargerDriver::GetChargeVoltageLimit(u32 *out) {
|
||||
u8 reg;
|
||||
R_TRY(this->Read(bq24193::ChargeVoltageControl, ®));
|
||||
*out = bq24193::DecodeChargeVoltageLimit(reg);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
|
@ -14,52 +14,111 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "boot_bq24193_charger.hpp"
|
||||
#include "boot_i2c_utils.hpp"
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
enum ChargerStatus {
|
||||
ChargerStatus_Unknown = 0,
|
||||
ChargerStatus_Charging = 1,
|
||||
ChargerStatus_NotCharging = 2,
|
||||
ChargerStatus_ChargeTerminationDone = 3,
|
||||
};
|
||||
|
||||
class ChargerDriver {
|
||||
private:
|
||||
static constexpr u32 GpioPadName_Bq24193Charger = 0xA;
|
||||
private:
|
||||
i2c::driver::I2cSession i2c_session;
|
||||
powctl::Session charger_session;
|
||||
public:
|
||||
ChargerDriver() {
|
||||
R_ABORT_UNLESS(i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Bq24193));
|
||||
|
||||
//boot::gpio::Configure(GpioPadName_Bq24193Charger);
|
||||
//boot::gpio::SetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output);
|
||||
ChargerDriver() : charger_session() {
|
||||
R_ABORT_UNLESS(powctl::OpenSession(std::addressof(this->charger_session), powctl::DeviceCode_Bq24193, ddsf::AccessMode_ReadWrite));
|
||||
}
|
||||
|
||||
~ChargerDriver() {
|
||||
i2c::driver::CloseSession(this->i2c_session);
|
||||
powctl::CloseSession(this->charger_session);
|
||||
}
|
||||
private:
|
||||
Result Read(u8 addr, u8 *out_data);
|
||||
Result Write(u8 addr, u8 val);
|
||||
Result ReadWrite(u8 addr, u8 mask, u8 val);
|
||||
|
||||
Result SetInputCurrentLimit(bq24193::InputCurrentLimit current);
|
||||
Result SetForce20PercentChargeCurrent(bool force);
|
||||
Result SetPreChargeCurrentLimit(u32 current);
|
||||
Result SetTerminationCurrentLimit(u32 current);
|
||||
Result SetMinimumSystemVoltageLimit(u32 voltage);
|
||||
Result SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting setting);
|
||||
Result SetChargingSafetyTimerEnabled(bool enabled);
|
||||
Result ResetWatchdogTimer();
|
||||
Result SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit current);
|
||||
Result SetHiZEnabled(bool enabled);
|
||||
Result Initialize(bool set_input_current_limit) {
|
||||
/* Set input current limit to 500 ma. */
|
||||
if (set_input_current_limit) {
|
||||
R_TRY(powctl::SetChargerInputCurrentLimit(this->charger_session, 500));
|
||||
}
|
||||
|
||||
public:
|
||||
Result Initialize();
|
||||
Result Initialize(bool set_input_current_limit);
|
||||
Result SetChargeVoltageLimit(u32 voltage);
|
||||
Result SetFastChargeCurrentLimit(u32 current);
|
||||
Result SetChargeEnabled(bool enabled);
|
||||
Result SetChargerConfiguration(bq24193::ChargerConfiguration config);
|
||||
Result GetInputCurrentLimit(bq24193::InputCurrentLimit *out);
|
||||
Result GetChargeVoltageLimit(u32 *out);
|
||||
/* Set input voltage limit to 500 mv. */
|
||||
R_TRY(powctl::SetChargerInputVoltageLimit(this->charger_session, 500));
|
||||
|
||||
/* Disable hi-z mode. */
|
||||
R_TRY(powctl::SetChargerHiZEnabled(this->charger_session, false));
|
||||
|
||||
/* Set configuration to charge battery. */
|
||||
R_TRY(powctl::SetChargerChargerConfiguration(this->charger_session, powctl::ChargerConfiguration_ChargeBattery));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetChargeCurrentState(powctl::ChargeCurrentState *out) {
|
||||
return powctl::GetChargerChargeCurrentState(out, this->charger_session);
|
||||
}
|
||||
|
||||
Result SetChargeCurrentState(powctl::ChargeCurrentState state) {
|
||||
return powctl::SetChargerChargeCurrentState(this->charger_session, state);
|
||||
}
|
||||
|
||||
Result GetInputCurrentLimit(int *out) {
|
||||
return powctl::GetChargerInputCurrentLimit(out, this->charger_session);
|
||||
}
|
||||
|
||||
Result SetChargerConfiguration(powctl::ChargerConfiguration cfg) {
|
||||
return powctl::SetChargerChargerConfiguration(this->charger_session, cfg);
|
||||
}
|
||||
|
||||
Result GetFastChargeCurrentLimit(int *out) {
|
||||
return powctl::GetChargerFastChargeCurrentLimit(out, this->charger_session);
|
||||
}
|
||||
|
||||
Result SetFastChargeCurrentLimit(int limit) {
|
||||
return powctl::SetChargerFastChargeCurrentLimit(this->charger_session, limit);
|
||||
}
|
||||
|
||||
Result GetChargeVoltageLimit(int *out) {
|
||||
return powctl::GetChargerChargeVoltageLimit(out, this->charger_session);
|
||||
}
|
||||
|
||||
Result SetChargeVoltageLimit(int limit) {
|
||||
return powctl::SetChargerChargeVoltageLimit(this->charger_session, limit);
|
||||
}
|
||||
|
||||
Result GetChargerStatus(boot::ChargerStatus *out) {
|
||||
/* Default to unknown. */
|
||||
*out = ChargerStatus_Unknown;
|
||||
|
||||
/* Get the powctl status. */
|
||||
powctl::ChargerStatus powctl_status;
|
||||
R_TRY(powctl::GetChargerChargerStatus(std::addressof(powctl_status), this->charger_session));
|
||||
|
||||
switch (powctl_status) {
|
||||
case powctl::ChargerStatus_Charging: *out = boot::ChargerStatus_Charging; break;
|
||||
case powctl::ChargerStatus_NotCharging: *out = boot::ChargerStatus_NotCharging; break;
|
||||
case powctl::ChargerStatus_ChargeTerminationDone: *out = boot::ChargerStatus_ChargeTerminationDone; break;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GetBatteryCompensation(int *out) {
|
||||
return powctl::GetChargerBatteryCompensation(out, this->charger_session);
|
||||
}
|
||||
|
||||
Result SetBatteryCompensation(int v) {
|
||||
return powctl::SetChargerBatteryCompensation(this->charger_session, v);
|
||||
}
|
||||
|
||||
Result GetVoltageClamp(int *out) {
|
||||
return powctl::GetChargerVoltageClamp(out, this->charger_session);
|
||||
}
|
||||
|
||||
Result SetVoltageClamp(int v) {
|
||||
return powctl::SetChargerVoltageClamp(this->charger_session, v);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -14,19 +14,25 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_battery_driver.hpp"
|
||||
#include "boot_check_battery.hpp"
|
||||
#include "boot_battery_icons.hpp"
|
||||
#include "boot_boot_reason.hpp"
|
||||
#include "boot_calibration.hpp"
|
||||
#include "boot_charger_driver.hpp"
|
||||
#include "boot_check_battery.hpp"
|
||||
#include "boot_pmic_driver.hpp"
|
||||
#include "boot_battery_driver.hpp"
|
||||
#include "boot_charger_driver.hpp"
|
||||
#include "boot_power_utils.hpp"
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Value definitions. */
|
||||
constexpr inline double BatteryLevelThresholdForBoot = 3.0;
|
||||
constexpr inline double BatteryLevelThresholdForFullCharge = 99.0;
|
||||
|
||||
constexpr inline int BatteryVoltageThresholdConnected = 4000;
|
||||
constexpr inline int BatteryVoltageThresholdDisconnected = 3650;
|
||||
|
||||
/* Types. */
|
||||
enum class CheckBatteryResult {
|
||||
Success,
|
||||
|
@ -34,251 +40,488 @@ namespace ams::boot {
|
|||
Reboot,
|
||||
};
|
||||
|
||||
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;
|
||||
class BatteryChecker {
|
||||
private:
|
||||
boot::ChargerDriver &charger_driver;
|
||||
boot::BatteryDriver &battery_driver;
|
||||
const powctl::driver::impl::ChargeParameters &charge_parameters;
|
||||
powctl::driver::impl::ChargeArbiter charge_arbiter;
|
||||
powctl::ChargeCurrentState charge_current_state;
|
||||
int fast_charge_current_limit;
|
||||
int charge_voltage_limit;
|
||||
int battery_compensation;
|
||||
int voltage_clamp;
|
||||
TimeSpan charging_done_interval;
|
||||
bool has_start_time;
|
||||
TimeSpan start_time;
|
||||
private:
|
||||
bool IsChargeDone();
|
||||
void UpdateChargeDoneCurrent();
|
||||
void ApplyArbiterRule();
|
||||
void PrintBatteryStatus(float raw_charge, int voltage, int voltage_threshold);
|
||||
CheckBatteryResult LoopCheckBattery(bool reboot_on_power_button_press, bool return_on_enough_battery, bool shutdown_on_full_battery, bool show_display, bool show_charging_display);
|
||||
|
||||
void UpdateStartTime() {
|
||||
/* Update start time. */
|
||||
this->start_time = os::ConvertToTimeSpan(os::GetSystemTick());
|
||||
this->has_start_time = true;
|
||||
}
|
||||
public:
|
||||
BatteryChecker(boot::ChargerDriver &cd, boot::BatteryDriver &bd, const powctl::driver::impl::ChargeParameters &cp, int cvl) : charger_driver(cd), battery_driver(bd), charge_parameters(cp), charge_arbiter(cp.rules, cp.num_rules, cvl), charging_done_interval(TimeSpan::FromSeconds(2)), has_start_time(false) {
|
||||
/* Get parameters from charger. */
|
||||
if (R_FAILED(this->charger_driver.GetChargeCurrentState(std::addressof(this->charge_current_state)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
if (R_FAILED(this->charger_driver.GetFastChargeCurrentLimit(std::addressof(this->fast_charge_current_limit)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
if (R_FAILED(this->charger_driver.GetChargeVoltageLimit(std::addressof(this->charge_voltage_limit)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
if (R_FAILED(this->charger_driver.GetBatteryCompensation(std::addressof(this->battery_compensation)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
if (R_FAILED(this->charger_driver.GetVoltageClamp(std::addressof(this->voltage_clamp)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
CheckBatteryResult LoopCheckBattery(spl::BootReason boot_reason) {
|
||||
if (boot_reason == spl::BootReason_RtcAlarm2) {
|
||||
/* RTC Alarm 2 boot (QuasiOff) */
|
||||
return this->LoopCheckBattery(true, false, true, false, false);
|
||||
} else if (boot_reason == spl::BootReason_AcOk) {
|
||||
/* ACOK boot */
|
||||
return this->LoopCheckBattery(true, true, false, true, true);
|
||||
} else {
|
||||
/* Normal boot */
|
||||
return this->LoopCheckBattery(false, true, false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateCharger();
|
||||
};
|
||||
|
||||
/* Battery parameters. */
|
||||
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,
|
||||
};
|
||||
void BatteryChecker::PrintBatteryStatus(float raw_charge, int voltage, int voltage_threshold) {
|
||||
/* TODO: Print charge/voltage/threshold. */
|
||||
AMS_UNUSED(raw_charge, voltage, voltage_threshold);
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
constexpr const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_version) {
|
||||
switch (battery_version) {
|
||||
case 0:
|
||||
return &BatteryChargeParameters0;
|
||||
case 1:
|
||||
return &BatteryChargeParameters1;
|
||||
case 2:
|
||||
return &BatteryChargeParameters2;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
/* Get various battery metrics. */
|
||||
int avg_current, current, open_circuit_voltage;
|
||||
float temp;
|
||||
if (R_FAILED(this->battery_driver.GetAverageCurrent(std::addressof(avg_current)))) {
|
||||
return;
|
||||
}
|
||||
if (R_FAILED(this->battery_driver.GetCurrent(std::addressof(current)))) {
|
||||
return;
|
||||
}
|
||||
if (R_FAILED(this->battery_driver.GetTemperature(std::addressof(temp)))) {
|
||||
return;
|
||||
}
|
||||
if (R_FAILED(this->battery_driver.GetOpenCircuitVoltage(std::addressof(open_circuit_voltage)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Print the things we just got. */
|
||||
AMS_UNUSED(avg_current, current, temp, open_circuit_voltage);
|
||||
}
|
||||
|
||||
/* Helpers. */
|
||||
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 BatteryChecker::IsChargeDone() {
|
||||
/* Get the charger status. */
|
||||
boot::ChargerStatus charger_status;
|
||||
if (R_FAILED(this->charger_driver.GetChargerStatus(std::addressof(charger_status)))) {
|
||||
boot::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;
|
||||
/* If charge status isn't done, we're not done. */
|
||||
if (charger_status != boot::ChargerStatus_ChargeTerminationDone) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Return whether a done current of zero is acceptable. */
|
||||
return this->charge_arbiter.IsBatteryDoneCurrentAcceptable(0);
|
||||
}
|
||||
|
||||
void BatteryChecker::UpdateChargeDoneCurrent() {
|
||||
int done_current = 0;
|
||||
if (this->has_start_time && (os::ConvertToTimeSpan(os::GetSystemTick()) - this->start_time) >= this->charging_done_interval) {
|
||||
/* Get the current. */
|
||||
if (R_FAILED(this->battery_driver.GetCurrent(std::addressof(done_current)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
} 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);
|
||||
} else {
|
||||
/* Get the charger status. */
|
||||
boot::ChargerStatus charger_status;
|
||||
if (R_FAILED(this->charger_driver.GetChargerStatus(std::addressof(charger_status)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
|
||||
/* If the charger status isn't done, don't update. */
|
||||
if (charger_status != boot::ChargerStatus_ChargeTerminationDone) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
/* Update done current. */
|
||||
this->charge_arbiter.SetBatteryDoneCurrent(done_current);
|
||||
}
|
||||
|
||||
void BatteryChecker::UpdateCharger() {
|
||||
/* Get the battery temperature. */
|
||||
float temp;
|
||||
if (R_FAILED(this->battery_driver.GetTemperature(std::addressof(temp)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
|
||||
if (R_FAILED(charger_driver->SetChargeEnabled(enable_charge))) {
|
||||
pmic_driver->ShutdownSystem();
|
||||
/* Update the temperature level. */
|
||||
powctl::BatteryTemperatureLevel temp_level;
|
||||
if (temp < static_cast<float>(this->charge_parameters.temp_min)) {
|
||||
temp_level = powctl::BatteryTemperatureLevel::TooLow;
|
||||
} else if (temp < static_cast<float>(this->charge_parameters.temp_low)) {
|
||||
temp_level = powctl::BatteryTemperatureLevel::Low;
|
||||
} else if (temp < static_cast<float>(this->charge_parameters.temp_high)) {
|
||||
temp_level = powctl::BatteryTemperatureLevel::Medium;
|
||||
} else if (temp < static_cast<float>(this->charge_parameters.temp_max)) {
|
||||
temp_level = powctl::BatteryTemperatureLevel::High;
|
||||
} else {
|
||||
temp_level = powctl::BatteryTemperatureLevel::TooHigh;
|
||||
}
|
||||
if (R_FAILED(charger_driver->SetChargeVoltageLimit(charge_voltage_limit))) {
|
||||
pmic_driver->ShutdownSystem();
|
||||
this->charge_arbiter.SetBatteryTemperatureLevel(temp_level);
|
||||
|
||||
/* Update average voltage. */
|
||||
int avg_v_cell;
|
||||
if (R_FAILED(this->battery_driver.GetAverageVCell(std::addressof(avg_v_cell)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
if (R_FAILED(charger_driver->SetFastChargeCurrentLimit(fast_charge_current_limit))) {
|
||||
pmic_driver->ShutdownSystem();
|
||||
this->charge_arbiter.SetBatteryAverageVCell(avg_v_cell);
|
||||
|
||||
/* Update open circuit voltage. */
|
||||
int ocv;
|
||||
if (R_FAILED(this->battery_driver.GetOpenCircuitVoltage(std::addressof(ocv)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->charge_arbiter.SetBatteryOpenCircuitVoltage(ocv);
|
||||
|
||||
/* Update charge done current. */
|
||||
this->UpdateChargeDoneCurrent();
|
||||
|
||||
/* Update arbiter power state. */
|
||||
this->charge_arbiter.SetPowerState(powctl::PowerState::ShutdownChargeMain);
|
||||
|
||||
/* Apply the newly selected rule. */
|
||||
this->ApplyArbiterRule();
|
||||
}
|
||||
|
||||
void BatteryChecker::ApplyArbiterRule() {
|
||||
/* Get the selected rule. */
|
||||
const auto *rule = this->charge_arbiter.GetSelectedRule();
|
||||
AMS_ASSERT(rule != nullptr);
|
||||
|
||||
/* Check if we need to perform charger initialization. */
|
||||
const bool reinit_charger = rule->reinitialize_charger;
|
||||
const auto cur_charge_current_state = this->charge_current_state;
|
||||
|
||||
/* Set the charger to not charging while we make changes. */
|
||||
if (!reinit_charger || cur_charge_current_state != powctl::ChargeCurrentState_NotCharging) {
|
||||
if (R_FAILED(this->charger_driver.SetChargeCurrentState(powctl::ChargeCurrentState_NotCharging))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->charge_current_state = powctl::ChargeCurrentState_NotCharging;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* Process fast charge current limit when rule is smaller. */
|
||||
const auto rule_fast_charge_current_limit = rule->fast_charge_current_limit;
|
||||
const auto cur_fast_charge_current_limit = this->fast_charge_current_limit;
|
||||
if (rule_fast_charge_current_limit < cur_fast_charge_current_limit) {
|
||||
if (R_FAILED(this->charger_driver.SetFastChargeCurrentLimit(rule_fast_charge_current_limit))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->fast_charge_current_limit = rule_fast_charge_current_limit;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* Process charge voltage limit when rule is smaller. */
|
||||
const auto rule_charge_voltage_limit = std::min(rule->charge_voltage_limit, this->charge_arbiter.GetChargeVoltageLimit());
|
||||
const auto cur_charge_voltage_limit = this->charge_voltage_limit;
|
||||
if (rule_charge_voltage_limit < cur_charge_voltage_limit) {
|
||||
if (R_FAILED(this->charger_driver.SetChargeVoltageLimit(rule_charge_voltage_limit))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->charge_voltage_limit = rule_charge_voltage_limit;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* Process battery compensation when rule is smaller. */
|
||||
const auto rule_battery_compensation = rule->battery_compensation;
|
||||
const auto cur_battery_compensation = this->battery_compensation;
|
||||
if (rule_battery_compensation < cur_battery_compensation) {
|
||||
if (R_FAILED(this->charger_driver.SetBatteryCompensation(rule_battery_compensation))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->battery_compensation = rule_battery_compensation;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* Process voltage clamp when rule is smaller. */
|
||||
const auto rule_voltage_clamp = rule->voltage_clamp;
|
||||
const auto cur_voltage_clamp = this->voltage_clamp;
|
||||
if (rule_voltage_clamp < cur_voltage_clamp) {
|
||||
if (R_FAILED(this->charger_driver.SetVoltageClamp(rule_voltage_clamp))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->voltage_clamp = rule_voltage_clamp;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* Process voltage clamp when rule is larger. */
|
||||
if (rule_voltage_clamp > cur_voltage_clamp) {
|
||||
if (R_FAILED(this->charger_driver.SetVoltageClamp(rule_voltage_clamp))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->voltage_clamp = rule_voltage_clamp;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* Process battery compensation when rule is larger. */
|
||||
if (rule_battery_compensation > cur_battery_compensation) {
|
||||
if (R_FAILED(this->charger_driver.SetBatteryCompensation(rule_battery_compensation))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->battery_compensation = rule_battery_compensation;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* Process fast charge current limit when rule is larger. */
|
||||
if (rule_fast_charge_current_limit > cur_fast_charge_current_limit) {
|
||||
if (R_FAILED(this->charger_driver.SetFastChargeCurrentLimit(rule_fast_charge_current_limit))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->fast_charge_current_limit = rule_fast_charge_current_limit;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* Process charge voltage limit when rule is larger. */
|
||||
if (rule_charge_voltage_limit > cur_charge_voltage_limit) {
|
||||
if (R_FAILED(this->charger_driver.SetChargeVoltageLimit(rule_charge_voltage_limit))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->charge_voltage_limit = rule_charge_voltage_limit;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
|
||||
/* If we're not charging and we expect to reinitialize the charger, do so. */
|
||||
if (cur_charge_current_state != powctl::ChargeCurrentState_Charging && reinit_charger) {
|
||||
if (R_FAILED(this->charger_driver.SetChargeCurrentState(powctl::ChargeCurrentState_Charging))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
this->charge_current_state = powctl::ChargeCurrentState_Charging;
|
||||
|
||||
/* Update start time. */
|
||||
this->UpdateStartTime();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
CheckBatteryResult BatteryChecker::LoopCheckBattery(bool reboot_on_power_button_press, bool return_on_enough_battery, bool shutdown_on_full_battery, bool show_display, bool show_charging_display) {
|
||||
/* Ensure that if we show a charging icon, we stop showing it when we're done. */
|
||||
bool is_showing_charging_icon = false;
|
||||
ON_SCOPE_EXIT {
|
||||
if (is_showing_charging_icon) {
|
||||
EndShowChargingIcon();
|
||||
boot::EndShowChargingIcon();
|
||||
is_showing_charging_icon = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (can_show_charging_icon) {
|
||||
size_t battery_percentage;
|
||||
if (R_FAILED(battery_driver->GetBatteryPercentage(&battery_percentage))) {
|
||||
/* Show the charging display, if we should. */
|
||||
if (show_charging_display) {
|
||||
/* Get the raw battery charge. */
|
||||
float raw_battery_charge;
|
||||
if (R_FAILED(this->battery_driver.GetSocRep(std::addressof(raw_battery_charge)))) {
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
StartShowChargingIcon(battery_percentage, true);
|
||||
|
||||
/* Display the battery with the appropriate percentage. */
|
||||
const auto battery_charge = powctl::impl::ConvertBatteryChargePercentage(raw_battery_charge);
|
||||
boot::StartShowChargingIcon(battery_charge);
|
||||
is_showing_charging_icon = true;
|
||||
}
|
||||
|
||||
/* Loop, checking the battery status. */
|
||||
TimeSpan last_progress_time = TimeSpan(0);
|
||||
while (true) {
|
||||
double battery_charge;
|
||||
if (R_FAILED(battery_driver->GetSocRep(&battery_charge))) {
|
||||
/* Get the raw battery charge. */
|
||||
float raw_battery_charge;
|
||||
if (R_FAILED(this->battery_driver.GetSocRep(std::addressof(raw_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? */
|
||||
}
|
||||
|
||||
/* Get the average vcell. */
|
||||
int battery_voltage;
|
||||
if (R_FAILED(this->battery_driver.GetAverageVCell(std::addressof(battery_voltage)))) {
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
|
||||
/* Get whether we're connected to charger. */
|
||||
bool ac_ok;
|
||||
if (R_FAILED(pmic_driver->GetAcOk(&ac_ok))) {
|
||||
if (R_FAILED((boot::PmicDriver().GetAcOk(std::addressof(ac_ok))))) {
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
|
||||
u32 battery_voltage;
|
||||
if (R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
/* Decide on a battery voltage threshold. */
|
||||
const auto battery_voltage_threshold = ac_ok ? BatteryVoltageThresholdConnected : BatteryVoltageThresholdDisconnected;
|
||||
|
||||
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) {
|
||||
ShowLowBatteryIcon();
|
||||
/* Check if we should return. */
|
||||
if (return_on_enough_battery) {
|
||||
if (raw_battery_charge >= BatteryLevelThresholdForBoot || battery_voltage >= battery_voltage_threshold) {
|
||||
this->PrintBatteryStatus(raw_battery_charge, battery_voltage, battery_voltage_threshold);
|
||||
return CheckBatteryResult::Success;
|
||||
}
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
|
||||
if (reboot_on_power_button_pressed) {
|
||||
bool power_button_pressed;
|
||||
if (R_FAILED(pmic_driver->GetPowerButtonPressed(&power_button_pressed))) {
|
||||
/* Otherwise, check if we should shut down. */
|
||||
if (shutdown_on_full_battery) {
|
||||
if (raw_battery_charge >= BatteryLevelThresholdForFullCharge || this->IsChargeDone()) {
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform periodic printing. */
|
||||
constexpr TimeSpan PrintProgressInterval = TimeSpan::FromSeconds(10);
|
||||
const auto cur_time = os::ConvertToTimeSpan(os::GetSystemTick());
|
||||
if ((cur_time - last_progress_time) >= PrintProgressInterval) {
|
||||
last_progress_time = cur_time;
|
||||
this->PrintBatteryStatus(raw_battery_charge, battery_voltage, battery_voltage_threshold);
|
||||
}
|
||||
|
||||
/* If we've gotten to this point, we have insufficient battery to boot. If we aren't charging, show low battery and shutdown. */
|
||||
if (!ac_ok) {
|
||||
this->PrintBatteryStatus(raw_battery_charge, battery_voltage, battery_voltage_threshold);
|
||||
if (show_display && !is_showing_charging_icon) {
|
||||
boot::ShowLowBatteryIcon();
|
||||
}
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
|
||||
/* Check if we should reboot due to a power button press. */
|
||||
if (reboot_on_power_button_press) {
|
||||
/* Get the power button value. */
|
||||
bool power_button_pressed;
|
||||
if (R_FAILED((boot::PmicDriver().GetPowerButtonPressed(std::addressof(power_button_pressed))))) {
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
|
||||
/* Handle the press (or not). */
|
||||
if (power_button_pressed) {
|
||||
return CheckBatteryResult::Reboot;
|
||||
}
|
||||
}
|
||||
|
||||
if (can_show_battery_icon && !is_showing_charging_icon) {
|
||||
StartShowChargingIcon(1, false);
|
||||
/* If we got to this point, we should show the low-battery charging screen. */
|
||||
if (show_display && !is_showing_charging_icon) {
|
||||
boot::StartShowLowBatteryChargingIcon();
|
||||
is_showing_charging_icon = true;
|
||||
}
|
||||
|
||||
svcSleepThread(20'000'000ul);
|
||||
UpdateCharger(pmic_driver, charger_driver, battery_driver, params, charge_voltage_limit);
|
||||
/* Wait a bit before checking again. */
|
||||
constexpr auto BatteryChargeCheckInterval = TimeSpan::FromMilliSeconds(20);
|
||||
os::SleepThread(BatteryChargeCheckInterval);
|
||||
|
||||
/* Update the charger. */
|
||||
this->UpdateCharger();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CheckBatteryCharge() {
|
||||
PmicDriver pmic_driver;
|
||||
BatteryDriver battery_driver;
|
||||
ChargerDriver charger_driver;
|
||||
/* Open a sessions for the charger/battery. */
|
||||
boot::ChargerDriver charger_driver;
|
||||
boot::BatteryDriver battery_driver;
|
||||
|
||||
if (R_FAILED(battery_driver.InitializeBatteryParameters())) {
|
||||
pmic_driver.ShutdownSystem();
|
||||
}
|
||||
/* Check if the battery is removed. */
|
||||
{
|
||||
bool removed;
|
||||
if (R_FAILED(battery_driver.IsBatteryRemoved(&removed)) || removed) {
|
||||
pmic_driver.ShutdownSystem();
|
||||
bool removed = false;
|
||||
if (R_FAILED(battery_driver.IsBatteryRemoved(std::addressof(removed))) || removed) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
}
|
||||
|
||||
const u32 boot_reason = GetBootReason();
|
||||
bq24193::InputCurrentLimit input_current_limit;
|
||||
if (R_FAILED(charger_driver.Initialize(boot_reason != 4)) || R_FAILED(charger_driver.GetInputCurrentLimit(&input_current_limit))) {
|
||||
pmic_driver.ShutdownSystem();
|
||||
}
|
||||
/* Get the boot reason. */
|
||||
const auto boot_reason = boot::GetBootReason();
|
||||
|
||||
if (input_current_limit <= bq24193::InputCurrentLimit_150mA) {
|
||||
charger_driver.SetChargerConfiguration(bq24193::ChargerConfiguration_ChargeDisable);
|
||||
pmic_driver.ShutdownSystem();
|
||||
}
|
||||
/* Initialize the charger driver. */
|
||||
if (R_FAILED(charger_driver.Initialize(boot_reason != spl::BootReason_RtcAlarm2)))
|
||||
|
||||
const BatteryChargeParameters *params = GetBatteryChargeParameters(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();
|
||||
/* Check that the charger input limit is greater than 150 milli-amps. */
|
||||
{
|
||||
int input_current_limit_ma;
|
||||
if (R_FAILED(charger_driver.GetInputCurrentLimit(std::addressof(input_current_limit_ma)))) {
|
||||
boot::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);
|
||||
|
||||
if (input_current_limit_ma <= 150) {
|
||||
charger_driver.SetChargerConfiguration(powctl::ChargerConfiguration_ChargeDisable);
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the charge parameters. */
|
||||
const auto &charge_parameters = powctl::driver::impl::GetChargeParameters();
|
||||
|
||||
/* Get the charge voltage limit. */
|
||||
int charge_voltage_limit_mv;
|
||||
if (boot_reason != spl::BootReason_RtcAlarm2) {
|
||||
charge_voltage_limit_mv = charge_parameters.default_charge_voltage_limit;
|
||||
} 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);
|
||||
if (R_FAILED(charger_driver.GetChargeVoltageLimit(std::addressof(charge_voltage_limit_mv)))) {
|
||||
boot::ShutdownSystem();
|
||||
}
|
||||
}
|
||||
|
||||
switch (check_result) {
|
||||
/* Create and update a battery checker. */
|
||||
BatteryChecker battery_checker(charger_driver, battery_driver, charge_parameters, charge_voltage_limit_mv);
|
||||
battery_checker.UpdateCharger();
|
||||
|
||||
/* Set the display brightness to 25%. */
|
||||
boot::SetDisplayBrightness(25);
|
||||
|
||||
/* Check the battery. */
|
||||
const CheckBatteryResult check_result = battery_checker.LoopCheckBattery(boot_reason);
|
||||
|
||||
/* Set the display brightness to 100%. */
|
||||
boot::SetDisplayBrightness(100);
|
||||
|
||||
/* Handle the check result. */
|
||||
switch (check_result) {
|
||||
case CheckBatteryResult::Success:
|
||||
break;
|
||||
case CheckBatteryResult::Shutdown:
|
||||
pmic_driver.ShutdownSystem();
|
||||
boot::ShutdownSystem();
|
||||
break;
|
||||
case CheckBatteryResult::Reboot:
|
||||
RebootSystem();
|
||||
boot::RebootSystem();
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_clock_initial_configuration.hpp"
|
||||
#include "boot_pmc_wrapper.hpp"
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline dd::PhysicalAddress PmcBase = 0x7000E400;
|
||||
|
||||
/* Convenience definitions. */
|
||||
constexpr u32 InitialClockOutMask1x = 0x00C4;
|
||||
constexpr u32 InitialClockOutMask6x = 0xC4C4;
|
||||
|
@ -30,7 +31,7 @@ namespace ams::boot {
|
|||
void SetInitialClockConfiguration() {
|
||||
/* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */
|
||||
const u32 mask = hos::GetVersion() >= hos::Version_6_0_0 ? InitialClockOutMask6x : InitialClockOutMask1x;
|
||||
WritePmcRegister(PmcBase + APBDEV_PMC_CLK_OUT_CNTRL, mask, mask);
|
||||
dd::ReadModifyWriteIoRegister(PmcBase + APBDEV_PMC_CLK_OUT_CNTRL, mask, mask);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include <stratosphere.hpp>
|
||||
#include "boot_display.hpp"
|
||||
#include "boot_i2c_utils.hpp"
|
||||
#include "boot_pmc_wrapper.hpp"
|
||||
|
||||
#include "boot_registers_di.hpp"
|
||||
|
||||
|
@ -25,7 +24,7 @@ namespace ams::boot {
|
|||
/* Display configuration included into anonymous namespace. */
|
||||
namespace {
|
||||
|
||||
#include "boot_display_config.inc"
|
||||
#include "boot_display_config.inc"
|
||||
|
||||
}
|
||||
|
||||
|
@ -39,6 +38,8 @@ namespace ams::boot {
|
|||
constexpr size_t FrameBufferHeight = 1280;
|
||||
constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32);
|
||||
|
||||
constexpr inline dd::PhysicalAddress PmcBase = 0x7000E400;
|
||||
|
||||
constexpr uintptr_t Disp1Base = 0x54200000ul;
|
||||
constexpr uintptr_t DsiBase = 0x54300000ul;
|
||||
constexpr uintptr_t ClkRstBase = 0x60006000ul;
|
||||
|
@ -68,19 +69,20 @@ namespace ams::boot {
|
|||
/* Types. */
|
||||
|
||||
/* Globals. */
|
||||
bool g_is_display_intialized = false;
|
||||
u32 *g_frame_buffer = nullptr;
|
||||
spl::SocType g_soc_type = spl::SocType_Erista;
|
||||
u32 g_lcd_vendor = 0;
|
||||
Handle g_dc_das_hnd = INVALID_HANDLE;
|
||||
u8 g_frame_buffer_storage[DeviceAddressSpaceAlignSize + FrameBufferSize];
|
||||
constinit bool g_is_display_intialized = false;
|
||||
constinit u32 *g_frame_buffer = nullptr;
|
||||
constinit spl::SocType g_soc_type = spl::SocType_Erista;
|
||||
constinit u32 g_lcd_vendor = 0;
|
||||
constinit Handle g_dc_das_hnd = INVALID_HANDLE;
|
||||
constinit int g_display_brightness = 100;
|
||||
constinit u8 g_frame_buffer_storage[DeviceAddressSpaceAlignSize + FrameBufferSize];
|
||||
|
||||
uintptr_t g_disp1_regs = 0;
|
||||
uintptr_t g_dsi_regs = 0;
|
||||
uintptr_t g_clk_rst_regs = 0;
|
||||
uintptr_t g_gpio_regs = 0;
|
||||
uintptr_t g_apb_misc_regs = 0;
|
||||
uintptr_t g_mipi_cal_regs = 0;
|
||||
constinit uintptr_t g_disp1_regs = 0;
|
||||
constinit uintptr_t g_dsi_regs = 0;
|
||||
constinit uintptr_t g_clk_rst_regs = 0;
|
||||
constinit uintptr_t g_gpio_regs = 0;
|
||||
constinit uintptr_t g_apb_misc_regs = 0;
|
||||
constinit uintptr_t g_mipi_cal_regs = 0;
|
||||
|
||||
/* Helper functions. */
|
||||
void InitializeRegisterBaseAddresses() {
|
||||
|
@ -119,9 +121,9 @@ namespace ams::boot {
|
|||
}
|
||||
}
|
||||
|
||||
#define DO_REGISTER_WRITES(base_address, writes) DoRegisterWrites(base_address, writes, util::size(writes))
|
||||
#define DO_SOC_DEPENDENT_REGISTER_WRITES(base_address, writes) DoSocDependentRegisterWrites(base_address, writes##Erista, util::size(writes##Erista), writes##Mariko, util::size(writes##Mariko))
|
||||
#define DO_DSI_SLEEP_OR_REGISTER_WRITES(writes) DoDsiSleepOrRegisterWrites(writes, util::size(writes))
|
||||
#define DO_REGISTER_WRITES(base_address, writes) DoRegisterWrites(base_address, writes, util::size(writes))
|
||||
#define DO_SOC_DEPENDENT_REGISTER_WRITES(base_address, writes) DoSocDependentRegisterWrites(base_address, writes##Erista, util::size(writes##Erista), writes##Mariko, util::size(writes##Mariko))
|
||||
#define DO_DSI_SLEEP_OR_REGISTER_WRITES(writes) DoDsiSleepOrRegisterWrites(writes, util::size(writes))
|
||||
|
||||
void InitializeFrameBuffer() {
|
||||
if (g_frame_buffer != nullptr) {
|
||||
|
@ -217,8 +219,8 @@ namespace ams::boot {
|
|||
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP, 0xA);
|
||||
|
||||
/* DPD idle. */
|
||||
WritePmcRegister(PmcBase + APBDEV_PMC_IO_DPD_REQ, 0x40000000);
|
||||
WritePmcRegister(PmcBase + APBDEV_PMC_IO_DPD2_REQ, 0x40000000);
|
||||
dd::WriteIoRegister(PmcBase + APBDEV_PMC_IO_DPD_REQ, 0x40000000);
|
||||
dd::WriteIoRegister(PmcBase + APBDEV_PMC_IO_DPD2_REQ, 0x40000000);
|
||||
|
||||
/* Configure LCD pinmux tristate + passthrough. */
|
||||
reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_NFC_EN, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE)));
|
||||
|
@ -502,4 +504,8 @@ namespace ams::boot {
|
|||
g_is_display_intialized = false;
|
||||
}
|
||||
|
||||
void SetDisplayBrightness(int percentage) {
|
||||
g_display_brightness = percentage;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,4 +23,6 @@ namespace ams::boot {
|
|||
void ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img);
|
||||
void FinalizeDisplay();
|
||||
|
||||
void SetDisplayBrightness(int percentage);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_pmc_wrapper.hpp"
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience definitions. */
|
||||
constexpr dd::PhysicalAddress PmcPhysStart = 0x7000E400;
|
||||
constexpr dd::PhysicalAddress PmcPhysLast = 0x7000EFFF;
|
||||
|
||||
/* Helpers. */
|
||||
bool IsValidPmcAddress(dd::PhysicalAddress phys_addr) {
|
||||
return util::IsAligned(phys_addr, alignof(u32)) && PmcPhysStart <= phys_addr && phys_addr <= PmcPhysLast;
|
||||
}
|
||||
|
||||
inline u32 ReadWriteRegisterImpl(dd::PhysicalAddress phys_addr, u32 value, u32 mask) {
|
||||
u32 out_value;
|
||||
R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::AtmosphereReadWriteRegister(phys_addr, mask, value, &out_value)));
|
||||
return out_value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u32 ReadPmcRegister(dd::PhysicalAddress phys_addr) {
|
||||
AMS_ABORT_UNLESS(IsValidPmcAddress(phys_addr));
|
||||
return ReadWriteRegisterImpl(phys_addr, 0, 0);
|
||||
}
|
||||
|
||||
void WritePmcRegister(dd::PhysicalAddress phys_addr, u32 value, u32 mask) {
|
||||
AMS_ABORT_UNLESS(IsValidPmcAddress(phys_addr));
|
||||
ReadWriteRegisterImpl(phys_addr, value, mask);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::boot {
|
||||
|
||||
constexpr inline dd::PhysicalAddress PmcBase = 0x7000E400;
|
||||
|
||||
/* PMC Access Utilities. */
|
||||
u32 ReadPmcRegister(dd::PhysicalAddress phys_addr);
|
||||
void WritePmcRegister(dd::PhysicalAddress phys_addr, u32 value, u32 mask = std::numeric_limits<u32>::max());
|
||||
|
||||
}
|
|
@ -20,90 +20,87 @@
|
|||
namespace ams::boot {
|
||||
|
||||
void PmicDriver::ShutdownSystem() {
|
||||
R_ABORT_UNLESS(this->ShutdownSystem(false));
|
||||
this->ShutdownSystem(false);
|
||||
}
|
||||
|
||||
void PmicDriver::RebootSystem() {
|
||||
R_ABORT_UNLESS(this->ShutdownSystem(true));
|
||||
this->ShutdownSystem(true);
|
||||
}
|
||||
|
||||
Result PmicDriver::GetAcOk(bool *out) {
|
||||
u8 power_status;
|
||||
R_TRY(this->GetPowerStatus(&power_status));
|
||||
R_TRY(this->GetPowerStatus(std::addressof(power_status)));
|
||||
*out = (power_status & 0x02) != 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result PmicDriver::GetPowerIntr(u8 *out) {
|
||||
Result PmicDriver::GetOnOffIrq(u8 *out) {
|
||||
const u8 addr = 0x0B;
|
||||
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
|
||||
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), std::addressof(addr), sizeof(addr));
|
||||
}
|
||||
|
||||
Result PmicDriver::GetPowerStatus(u8 *out) {
|
||||
const u8 addr = 0x15;
|
||||
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
|
||||
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), std::addressof(addr), sizeof(addr));
|
||||
}
|
||||
|
||||
Result PmicDriver::GetNvErc(u8 *out) {
|
||||
const u8 addr = 0x0C;
|
||||
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
|
||||
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), std::addressof(addr), sizeof(addr));
|
||||
}
|
||||
|
||||
Result PmicDriver::GetPowerButtonPressed(bool *out) {
|
||||
u8 power_intr;
|
||||
R_TRY(this->GetPowerIntr(&power_intr));
|
||||
*out = (power_intr & 0x08) != 0;
|
||||
u8 on_off_irq;
|
||||
R_TRY(this->GetOnOffIrq(std::addressof(on_off_irq)));
|
||||
*out = (on_off_irq & 0x08) != 0;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result PmicDriver::ShutdownSystem(bool reboot) {
|
||||
void PmicDriver::ShutdownSystem(bool reboot) {
|
||||
const u8 on_off_1_addr = 0x41;
|
||||
const u8 on_off_2_addr = 0x42;
|
||||
|
||||
/* Get value, set or clear software reset mask. */
|
||||
u8 on_off_2_val = 0;
|
||||
R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr)));
|
||||
R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, std::addressof(on_off_2_val), sizeof(on_off_2_val), std::addressof(on_off_2_addr), sizeof(on_off_2_addr)));
|
||||
if (reboot) {
|
||||
on_off_2_val |= 0x80;
|
||||
} else {
|
||||
on_off_2_val &= ~0x80;
|
||||
}
|
||||
R_ABORT_UNLESS(WriteI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr)));
|
||||
R_ABORT_UNLESS(WriteI2cRegister(this->i2c_session, std::addressof(on_off_2_val), sizeof(on_off_2_val), std::addressof(on_off_2_addr), sizeof(on_off_2_addr)));
|
||||
|
||||
/* Get value, set software reset mask. */
|
||||
u8 on_off_1_val = 0;
|
||||
R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr)));
|
||||
R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, std::addressof(on_off_1_val), sizeof(on_off_1_val), std::addressof(on_off_1_addr), sizeof(on_off_1_addr)));
|
||||
on_off_1_val |= 0x80;
|
||||
|
||||
/* Finalize the battery on non-Calcio. */
|
||||
if (spl::GetHardwareType() != spl::HardwareType::Calcio) {
|
||||
BatteryDriver battery_driver;
|
||||
this->FinalizeBattery(&battery_driver);
|
||||
this->FinalizeBattery(battery_driver);
|
||||
}
|
||||
|
||||
/* Actually write the value to trigger shutdown/reset. */
|
||||
R_ABORT_UNLESS(WriteI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr)));
|
||||
R_ABORT_UNLESS(WriteI2cRegister(this->i2c_session, std::addressof(on_off_1_val), sizeof(on_off_1_val), std::addressof(on_off_1_addr), sizeof(on_off_1_addr)));
|
||||
|
||||
/* Allow up to 5 seconds for shutdown/reboot to take place. */
|
||||
svcSleepThread(5'000'000'000ul);
|
||||
AMS_ABORT_UNLESS(false);
|
||||
os::SleepThread(TimeSpan::FromSeconds(5));
|
||||
AMS_ABORT("Shutdown failed");
|
||||
}
|
||||
|
||||
void PmicDriver::FinalizeBattery(BatteryDriver *battery_driver) {
|
||||
/* Set shutdown timer. */
|
||||
battery_driver->SetShutdownTimer();
|
||||
|
||||
void PmicDriver::FinalizeBattery(BatteryDriver &battery_driver) {
|
||||
/* Get whether shutdown is enabled. */
|
||||
bool shutdown_enabled;
|
||||
if (R_FAILED(battery_driver->GetShutdownEnabled(&shutdown_enabled))) {
|
||||
if (R_FAILED(battery_driver.IsI2cShutdownEnabled(std::addressof(shutdown_enabled)))) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* On Hoag, we don't want to use the desired shutdown value when battery charged. */
|
||||
bool use_desired_shutdown = true;
|
||||
if (spl::GetHardwareType() == spl::HardwareType::Hoag) {
|
||||
double battery_charge;
|
||||
if (R_FAILED(battery_driver->GetSocRep(&battery_charge)) || battery_charge >= 80.0) {
|
||||
float battery_charge_raw;
|
||||
if (R_FAILED(battery_driver.GetSocRep(std::addressof(battery_charge_raw))) || battery_charge_raw >= 80.0) {
|
||||
use_desired_shutdown = false;
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +116,7 @@ namespace ams::boot {
|
|||
desired_shutdown_enabled &= use_desired_shutdown;
|
||||
|
||||
if (shutdown_enabled != desired_shutdown_enabled) {
|
||||
battery_driver->SetShutdownEnabled(desired_shutdown_enabled);
|
||||
battery_driver.SetI2cShutdownEnabled(desired_shutdown_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,13 +32,13 @@ namespace ams::boot {
|
|||
}
|
||||
private:
|
||||
Result GetPowerStatus(u8 *out);
|
||||
Result ShutdownSystem(bool reboot);
|
||||
void FinalizeBattery(BatteryDriver *battery_driver);
|
||||
void ShutdownSystem(bool reboot);
|
||||
void FinalizeBattery(BatteryDriver &battery_driver);
|
||||
public:
|
||||
void ShutdownSystem();
|
||||
void RebootSystem();
|
||||
Result GetAcOk(bool *out);
|
||||
Result GetPowerIntr(u8 *out);
|
||||
Result GetOnOffIrq(u8 *out);
|
||||
Result GetNvErc(u8 *out);
|
||||
Result GetPowerButtonPressed(bool *out);
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_power_utils.hpp"
|
||||
#include "boot_pmic_driver.hpp"
|
||||
#include "fusee-primary_bin.h"
|
||||
|
||||
namespace ams::boot {
|
||||
|
@ -65,7 +66,16 @@ namespace ams::boot {
|
|||
}
|
||||
|
||||
void RebootSystem() {
|
||||
DoRebootToPayload(nullptr);
|
||||
if (spl::GetSocType() == spl::SocType_Erista) {
|
||||
DoRebootToPayload(nullptr);
|
||||
} else {
|
||||
/* On Mariko, we can't reboot to payload, so we should just do a reboot. */
|
||||
PmicDriver().RebootSystem();
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownSystem() {
|
||||
PmicDriver().ShutdownSystem();
|
||||
}
|
||||
|
||||
void SetInitialRebootPayload() {
|
||||
|
|
|
@ -29,8 +29,8 @@ namespace ams::boot {
|
|||
}
|
||||
|
||||
void ShowSplashScreen() {
|
||||
const u32 boot_reason = GetBootReason();
|
||||
if (boot_reason == 1 || boot_reason == 4) {
|
||||
const auto boot_reason = GetBootReason();
|
||||
if (boot_reason == spl::BootReason_AcOk || boot_reason == spl::BootReason_RtcAlarm2) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ namespace ams::boot {
|
|||
{
|
||||
/* Splash screen is shown for 2 seconds. */
|
||||
ShowDisplay(SplashScreenX, SplashScreenY, SplashScreenW, SplashScreenH, SplashScreen);
|
||||
svcSleepThread(2'000'000'000ul);
|
||||
os::SleepThread(TimeSpan::FromSeconds(2));
|
||||
}
|
||||
FinalizeDisplay();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue