boot: refactor battery checking to use new powctl apis

This commit is contained in:
Michael Scire 2020-11-08 04:16:50 -08:00 committed by SciresM
parent 485304bd17
commit 708f5bf1fb
44 changed files with 1426 additions and 1554 deletions

View file

@ -41,6 +41,7 @@
/* At this point, just include the rest alphabetically. */
/* TODO: Figure out optimal order. */
#include <stratosphere/boot2.hpp>
#include <stratosphere/cal.hpp>
#include <stratosphere/capsrv.hpp>
#include <stratosphere/cfg.hpp>
#include <stratosphere/clkrst.hpp>

View file

@ -0,0 +1,19 @@
/*
* 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/cal/cal_battery_api.hpp>

View file

@ -0,0 +1,24 @@
/*
* 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 <vapours.hpp>
namespace ams::cal {
Result GetBatteryVersion(u8 *out);
Result GetBatteryVendor(size_t *out_vendor_size, void *dst, size_t dst_size);
}

View file

@ -20,4 +20,7 @@
#include <stratosphere/powctl/powctl_session_api.hpp>
#include <stratosphere/powctl/powctl_battery_api.hpp>
#include <stratosphere/powctl/powctl_charger_api.hpp>
#include <stratosphere/powctl/impl/powctl_battery_charge_percentage.hpp>
#include <stratosphere/powctl/driver/powctl_driver_api.hpp>
#include <stratosphere/powctl/driver/impl/powctl_select_charger_parameters.hpp>
#include <stratosphere/powctl/driver/impl/powctl_charge_arbiter.hpp>

View file

@ -0,0 +1,161 @@
/*
* 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 <vapours.hpp>
#include <stratosphere/powctl/powctl_types.hpp>
#include <stratosphere/powctl/driver/impl/powctl_select_charger_parameters.hpp>
namespace ams::powctl::driver::impl {
class ChargeArbiter {
private:
const ChargeParametersRule *rules;
size_t num_rules;
int charge_voltage_limit;
BatteryTemperatureLevel temperature_level;
int avg_v_cell;
int open_circuit_voltage;
bool has_battery_done_current;
int battery_done_current;
PowerState power_state;
const ChargeParametersRule *selected_rule;
bool check_battery_done_current;
private:
static constexpr bool IsInRange(int value, int min, int max) {
if (!(min <= value)) {
return false;
}
if (max == std::numeric_limits<int>::max()) {
return value <= max;
} else {
return value < max;
}
}
bool IsAcceptablePowerState(const PowerState *acceptable, size_t num_acceptable) const {
for (size_t i = 0; i < num_acceptable; ++i) {
if (this->power_state == acceptable[i]) {
return true;
}
}
return false;
}
public:
ChargeArbiter(const ChargeParametersRule *r, size_t nr, int cvl)
: rules(r), num_rules(nr), charge_voltage_limit(cvl), temperature_level(BatteryTemperatureLevel::Medium),
avg_v_cell(4080), open_circuit_voltage(4001), has_battery_done_current(false), battery_done_current(0),
power_state(PowerState::FullAwake), selected_rule(nullptr), check_battery_done_current(false)
{
this->UpdateSelectedRule();
}
void SetBatteryTemperatureLevel(BatteryTemperatureLevel btl) {
this->temperature_level = btl;
this->UpdateSelectedRule();
}
void SetBatteryAverageVCell(int avg) {
this->avg_v_cell = avg;
this->UpdateSelectedRule();
}
void SetBatteryOpenCircuitVoltage(int ocv) {
this->open_circuit_voltage = ocv;
this->UpdateSelectedRule();
}
void SetBatteryDoneCurrent(int current) {
this->battery_done_current = current;
this->has_battery_done_current = true;
this->UpdateSelectedRule();
}
void SetPowerState(PowerState ps) {
this->power_state = ps;
this->UpdateSelectedRule();
}
int GetChargeVoltageLimit() const {
return this->charge_voltage_limit;
}
bool IsBatteryDoneCurrentAcceptable(int current) const {
const auto *rule = this->GetSelectedRule();
AMS_ASSERT(rule != nullptr);
return IsInRange(0, rule->min_battery_done_current, rule->max_battery_done_current);
}
const ChargeParametersRule *GetSelectedRule() const {
return this->selected_rule;
}
void UpdateSelectedRule() {
/* Try to find an entry that fits our current requirements. */
const ChargeParametersRule *best_rule = nullptr;
for (size_t i = 0; i < this->num_rules; ++i) {
/* Get the current rule. */
const ChargeParametersRule &cur_rule = this->rules[i];
/* Check the temperature level. */
if (this->temperature_level != cur_rule.temperature_level) {
continue;
}
/* Check that average voltage is in range. */
if (!IsInRange(this->avg_v_cell, cur_rule.min_avg_v_cell, cur_rule.max_avg_v_cell)) {
continue;
}
/* Check that open circuit voltage is in range. */
if (!IsInRange(this->open_circuit_voltage, cur_rule.min_open_circuit_voltage, cur_rule.max_open_circuit_voltage)) {
continue;
}
/* Check if our power state is acceptable. */
if (!this->IsAcceptablePowerState(cur_rule.acceptable_power_states, cur_rule.num_acceptable_power_states)) {
continue;
}
/* The limit is probably acceptable. */
if (this->selected_rule != std::addressof(cur_rule)) {
/* We're selecting a new rule. Check if our need to deal with battery current is acceptable. */
if (cur_rule.check_battery_current && this->check_battery_done_current) {
continue;
}
/* Set whether we need to check the battery done current. */
this->has_battery_done_current = false;
this->check_battery_done_current |= cur_rule.check_battery_current;
} else {
/* We're selecting the currently selected rule. Make sure the battery done current is acceptable if we have one. */
if (this->has_battery_done_current && !IsInRange(this->battery_done_current, cur_rule.min_battery_done_current, cur_rule.max_battery_done_current)) {
continue;
}
}
/* Select the current rule. */
best_rule = std::addressof(cur_rule);
break;
}
/* Update our selected rule. */
this->selected_rule = best_rule;
}
};
}

View file

@ -0,0 +1,64 @@
/*
* 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 <vapours.hpp>
#include <stratosphere/powctl/powctl_types.hpp>
namespace ams::powctl::driver::impl {
struct ChargeParametersRule {
BatteryTemperatureLevel temperature_level;
int min_avg_v_cell;
int max_avg_v_cell;
int min_open_circuit_voltage;
int max_open_circuit_voltage;
int min_battery_done_current;
int max_battery_done_current;
const PowerState *acceptable_power_states;
size_t num_acceptable_power_states;
bool check_battery_current;
bool reinitialize_charger;
int charge_voltage_limit;
int fast_charge_current_limit;
int battery_compensation;
int voltage_clamp;
};
struct UnknownParameterX {
int _00;
int _04;
double _08;
double _10;
};
struct ChargeParameters {
int temp_min;
int temp_low;
int temp_high;
int temp_max;
int low_voltage_fast_charge_current_limit;
int default_charge_voltage_limit;
const UnknownParameterX *unknown_x_table;
size_t x_table_size;
double _28;
double _30;
const ChargeParametersRule *rules;
size_t num_rules;
};
const ChargeParameters &GetChargeParameters();
}

View file

@ -0,0 +1,26 @@
/*
* 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 <vapours.hpp>
#include <stratosphere/powctl/powctl_types.hpp>
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
#include <stratosphere/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.hpp>
#else
#error "unknown board for powctl::driver::impl::ChargerParameters"
#endif

View file

@ -0,0 +1,65 @@
/*
* 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 <vapours.hpp>
#include <stratosphere/powctl/powctl_types.hpp>
namespace ams::powctl::impl {
constexpr inline const double MinRawDefaultPercentage = 3.0;
constexpr inline const double MaxRawDefaultPercentage = 99.0;
constexpr inline const double MinRawThresholdPercentage = 11.0;
constexpr inline const int MinDisplayPercentage = 1;
constexpr inline const int MaxDisplayPercentage = 100;
constexpr inline void CalculateMarginatedRawPercentage(double *out_marginated_min, double *out_marginated_max, double min, double max) {
/* Ensure minimum is in correct range. */
min = std::max(std::min(min, MinRawThresholdPercentage), MinRawDefaultPercentage);
/* Calculate the marginated values. */
constexpr const double MinMarginPercentage = 0.93359375;
constexpr const double MaxMarginPercentage = -0.83593750;
const auto margin_factor = (max - min) / (MaxRawDefaultPercentage - MinRawDefaultPercentage);
*out_marginated_min = min + MinMarginPercentage * margin_factor;
*out_marginated_max = max + MaxMarginPercentage * margin_factor;
}
constexpr inline int GetDisplayPercentage(double raw_percentage, double min, double max) {
/* Calculate the display percentage. */
constexpr const double BaseDisplayPercentage = 2.0;
const auto display_percentage = BaseDisplayPercentage + ((static_cast<double>(MaxDisplayPercentage - MinDisplayPercentage) * (raw_percentage - min)) / (max - min));
/* Clamp the display percentage within bounds. */
return std::max(std::min(static_cast<int>(display_percentage), MaxDisplayPercentage), MinDisplayPercentage);
}
constexpr inline int ConvertBatteryChargePercentage(double raw_percentage, double min, double max) {
/* Marginate the min/max. */
double marginated_min = 0.0, marginated_max = 0.0;
CalculateMarginatedRawPercentage(std::addressof(marginated_min), std::addressof(marginated_max), min, max);
/* Convert to display percentage. */
return GetDisplayPercentage(raw_percentage, marginated_min, marginated_max);
}
constexpr inline int ConvertBatteryChargePercentage(double raw_percentage) {
return ConvertBatteryChargePercentage(raw_percentage, MinRawDefaultPercentage, MaxRawDefaultPercentage);
}
}

View file

@ -25,15 +25,15 @@ namespace ams::powctl {
Result GetBatterySocVf(float *out_percent, Session &session);
Result GetBatteryFullCapacity(u32 *out_mah, Session &session);
Result GetBatteryRemainingCapacity(u32 *out_mah, Session &session);
Result GetBatteryFullCapacity(int *out_mah, Session &session);
Result GetBatteryRemainingCapacity(int *out_mah, Session &session);
Result SetBatteryPercentageMinimumAlertThreshold(Session &session, float percentage);
Result SetBatteryPercentageMaximumAlertThreshold(Session &session, float percentage);
Result SetBatteryPercentageFullThreshold(Session &session, float percentage);
Result GetBatteryAverageCurrent(u32 *out_ma, Session &session);
Result GetBatteryCurrent(u32 *out_ma, Session &session);
Result GetBatteryAverageCurrent(int *out_ma, Session &session);
Result GetBatteryCurrent(int *out_ma, Session &session);
Result GetBatteryInternalState(void *dst, size_t *out_size, Session &session, size_t dst_size);
Result SetBatteryInternalState(Session &session, const void *src, size_t src_size);
@ -44,10 +44,10 @@ namespace ams::powctl {
Result IsBatteryI2cShutdownEnabled(bool *out, Session &session);
Result SetBatteryI2cShutdownEnabled(Session &session, bool en);
Result IsBatteryRemoved(bool *out, Session &session);
Result IsBatteryPresent(bool *out, Session &session);
Result GetBatteryCycles(u32 *out, Session &session);
Result SetBatteryCycles(Session &session, u32 cycles);
Result GetBatteryCycles(int *out, Session &session);
Result SetBatteryCycles(Session &session, int cycles);
Result GetBatteryAge(float *out_percent, Session &session);
@ -57,14 +57,14 @@ namespace ams::powctl {
Result SetBatteryTemperatureMinimumAlertThreshold(Session &session, float c);
Result SetBatteryTemperatureMaximumAlertThreshold(Session &session, float c);
Result GetBatteryVCell(u32 *out_mv, Session &session);
Result GetBatteryAverageVCell(u32 *out_mv, Session &session);
Result GetBatteryVCell(int *out_mv, Session &session);
Result GetBatteryAverageVCell(int *out_mv, Session &session);
Result GetBatteryAverageVCellTime(TimeSpan *out, Session &session);
Result GetBatteryOpenCircuitVoltage(u32 *out_mv, Session &session);
Result GetBatteryOpenCircuitVoltage(int *out_mv, Session &session);
Result SetBatteryVoltageMinimumAlertThreshold(Session &session, u32 mv);
Result SetBatteryVoltageMaximumAlertThreshold(Session &session, u32 mv);
Result SetBatteryVoltageMinimumAlertThreshold(Session &session, int mv);
Result SetBatteryVoltageMaximumAlertThreshold(Session &session, int mv);
}

View file

@ -24,22 +24,22 @@ namespace ams::powctl {
Result GetChargerChargeCurrentState(ChargeCurrentState *out, Session &session);
Result SetChargerChargeCurrentState(Session &session, ChargeCurrentState state);
Result GetChargerFastChargeCurrentLimit(u32 *out_ma, Session &session);
Result SetChargerFastChargeCurrentLimit(Session &session, u32 ma);
Result GetChargerFastChargeCurrentLimit(int *out_ma, Session &session);
Result SetChargerFastChargeCurrentLimit(Session &session, int ma);
Result GetChargerChargeVoltageLimit(u32 *out_mv, Session &session);
Result SetChargerChargeVoltageLimit(Session &session, u32 mv);
Result GetChargerChargeVoltageLimit(int *out_mv, Session &session);
Result SetChargerChargeVoltageLimit(Session &session, int mv);
Result SetChargerChargerConfiguration(Session &session, ChargerConfiguration cfg);
Result IsChargerHiZEnabled(bool *out, Session &session);
Result SetChargerHiZEnabled(Session &session, bool en);
Result GetChargerInputCurrentLimit(u32 *out_ma, Session &session);
Result SetChargerInputCurrentLimit(Session &session, u32 ma);
Result GetChargerInputCurrentLimit(int *out_ma, Session &session);
Result SetChargerInputCurrentLimit(Session &session, int ma);
Result GetChargerInputVoltageLimit(u32 *out_mv, Session &session);
Result SetChargerInputVoltageLimit(Session &session, u32 mv);
Result GetChargerInputVoltageLimit(int *out_mv, Session &session);
Result SetChargerInputVoltageLimit(Session &session, int mv);
Result GetChargerChargerStatus(ChargerStatus *out, Session &session);
@ -49,10 +49,10 @@ namespace ams::powctl {
Result SetChargerWatchdogTimerTimeout(Session &session, TimeSpan timeout);
Result ResetChargerWatchdogTimer(Session &session);
Result GetChargerBatteryCompensation(u32 *out_mo, Session &session);
Result SetChargerBatteryCompensation(Session &session, u32 mo);
Result GetChargerBatteryCompensation(int *out_mo, Session &session);
Result SetChargerBatteryCompensation(Session &session, int mo);
Result GetChargerVoltageClamp(u32 *out_mv, Session &session);
Result SetChargerVoltageClamp(Session &session, u32 mv);
Result GetChargerVoltageClamp(int *out_mv, Session &session);
Result SetChargerVoltageClamp(Session &session, int mv);
}

View file

@ -27,9 +27,9 @@ namespace ams::powctl {
};
enum ChargerConfiguration {
ChargerConfiguration_ChargeDisable = 0,
ChargerConfiguration_ChargeBattery = 1,
ChargerConfiguration_Otg = 2,
ChargerConfiguration_ChargeDisable = 1,
ChargerConfiguration_ChargeBattery = 2,
ChargerConfiguration_Otg = 3,
};
enum ChargeCurrentState {
@ -38,4 +38,20 @@ namespace ams::powctl {
ChargeCurrentState_Charging = 0x3,
};
enum class BatteryTemperatureLevel {
TooLow = 0,
Low = 1,
Medium = 2,
High = 3,
TooHigh = 4,
};
enum class PowerState {
FullAwake = 0,
MinimumAwake = 1,
SleepCharge = 2,
SleepDischarge = 3,
ShutdownChargeMain = 4,
};
}

View file

@ -150,6 +150,15 @@ namespace ams::spl {
};
};
static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");
enum BootReason {
BootReason_Unknown = 0,
BootReason_AcOk = 1,
BootReason_OnKey = 2,
BootReason_RtcAlarm1 = 3,
BootReason_RtcAlarm2 = 4,
};
#pragma pack(push, 1)
struct AesKey {

View file

@ -0,0 +1,53 @@
/*
* 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 "cal_fs_utils.hpp"
namespace ams::cal {
namespace {
constexpr inline s64 BatteryLotOffset = 0x2CE0;
constexpr inline size_t BatteryLotSize = 0x20;
constexpr inline s64 BatteryVersionOffset = 0x4310;
constexpr inline size_t BatteryVersionSize = 0x10;
constexpr inline size_t BatteryVendorSizeMax = 0x18;
}
Result GetBatteryVersion(u8 *out) {
/* Read the battery version. */
u8 battery_version[BatteryVersionSize];
R_TRY(cal::impl::ReadCalibrationBlock(BatteryVersionOffset, battery_version, sizeof(battery_version)));
/* Write the output. */
*out = battery_version[0];
return ResultSuccess();
}
Result GetBatteryVendor(size_t *out_vendor_size, void *dst, size_t dst_size) {
/* Read the battery lot. */
char battery_lot[BatteryLotSize];
R_TRY(cal::impl::ReadCalibrationBlock(BatteryLotOffset, battery_lot, sizeof(battery_lot)));
/* Copy output. */
*out_vendor_size = static_cast<size_t>(util::Strlcpy(static_cast<char *>(dst), battery_lot, std::min(dst_size, BatteryVendorSizeMax)));
return ResultSuccess();
}
}

View file

@ -0,0 +1,53 @@
/*
* 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 "cal_crc_utils.hpp"
namespace ams::cal::impl {
namespace {
constexpr inline const u16 CrcTable[0x10] = {
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
};
}
u16 CalculateCrc16(const void *data, size_t size) {
AMS_ASSERT(data != nullptr);
u16 crc = 0x55AA;
const u8 *data_u8 = static_cast<const u8 *>(data);
for (size_t i = 0; i < size; ++i) {
crc = (crc >> 4) ^ (CrcTable[crc & 0xF]) ^ (CrcTable[(data_u8[i] >> 0) & 0xF]);
crc = (crc >> 4) ^ (CrcTable[crc & 0xF]) ^ (CrcTable[(data_u8[i] >> 4) & 0xF]);
}
return crc;
}
Result ValidateCalibrationCrc(const void *data, size_t size) {
AMS_ASSERT(data != nullptr);
AMS_ASSERT(size >= sizeof(u16));
const u16 crc = *reinterpret_cast<const u16 *>(reinterpret_cast<uintptr_t>(data) + size - sizeof(u16));
R_UNLESS(CalculateCrc16(data, size - sizeof(u16)) == crc, cal::ResultCalibrationDataCrcError());
return ResultSuccess();
}
}

View file

@ -16,12 +16,9 @@
#pragma once
#include <stratosphere.hpp>
namespace ams::boot {
namespace ams::cal::impl {
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());
u16 CalculateCrc16(const void *data, size_t size);
Result ValidateCalibrationCrc(const void *data, size_t size);
}

View file

@ -0,0 +1,36 @@
/*
* 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 "cal_crc_utils.hpp"
#include "cal_fs_utils.hpp"
namespace ams::cal::impl {
Result ReadCalibrationBlock(s64 offset, void *dst, size_t block_size) {
/* Open the calibration binary partition. */
std::unique_ptr<fs::IStorage> storage;
R_TRY(fs::OpenBisPartition(std::addressof(storage), fs::BisPartitionId::CalibrationBinary));
/* Read data from the partition. */
R_TRY(storage->Read(offset, dst, block_size));
/* Validate the crc. */
R_TRY(ValidateCalibrationCrc(dst, block_size));
return ResultSuccess();
}
}

View file

@ -16,10 +16,8 @@
#pragma once
#include <stratosphere.hpp>
namespace ams::boot {
namespace ams::cal::impl {
/* Calibration utilities. */
u32 GetBatteryVersion();
u32 GetBatteryVendor();
Result ReadCalibrationBlock(s64 offset, void *dst, size_t block_size);
}

View file

@ -0,0 +1,74 @@
/*
* 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>
namespace ams::powctl::driver::impl {
namespace {
constexpr inline const PowerState AcceptablePowerStates[] = {
PowerState::FullAwake,
PowerState::MinimumAwake,
PowerState::SleepCharge,
PowerState::SleepDischarge,
PowerState::ShutdownChargeMain,
};
constexpr inline const PowerState AcceptablePowerStatesForNotAwakeCharge[] = {
PowerState::SleepCharge,
PowerState::ShutdownChargeMain,
};
constexpr inline const int Min = std::numeric_limits<int>::min();
constexpr inline const int Max = std::numeric_limits<int>::max();
constexpr inline const UnknownParameterX UnknownXTableForBatteryVersion2[] = {
{ 20000, 4320, 95.0, 100.4 },
{ 30000, 4304, 94.0, 99.7 },
{ 40000, 4288, 93.0, 98.4 },
{ 50000, 4272, 92.0, 97.0 },
{ 60000, 4256, 90.0, 95.7 },
{ 80000, 4240, 89.0, 94.2 },
{ 100000, 4224, 88.0, 93.0 },
{ Max, 4192, 85.0, 90.0 },
};
/* Include automatically extracted charger parameters. */
#include "powctl_charger_parameters.board.nintendo_nx.inc"
}
const ChargeParameters &GetChargeParameters() {
/* Get the battery version. */
u8 battery_version;
if (R_FAILED(cal::GetBatteryVersion(std::addressof(battery_version)))) {
battery_version = 0;
}
if (battery_version == 2) {
return ChargeParametersForBatteryVersion2;
} else if (battery_version == 1) {
return ChargeParametersForBatteryVersion1;
} else {
if (spl::GetHardwareType() == spl::HardwareType::_Five_) {
return ChargeParametersForBatteryVersion0ForFive;
} else {
return ChargeParametersForBatteryVersion0;
}
}
}
}

View file

@ -0,0 +1,79 @@
/*
* 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/>.
*/
/* NOTE: This file is auto-generated by charger_parameters.py, do not edit manually. */
constexpr inline const ChargeParametersRule ChargeParametersRulesForBatteryVersion0[] = {
{ BatteryTemperatureLevel::TooLow, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 512, 0, 0 },
{ BatteryTemperatureLevel::TooLow, 3320, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 768, 0, 0 },
{ BatteryTemperatureLevel::Low, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 512, 0, 0 },
{ BatteryTemperatureLevel::Low, 3320, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 768, 0, 0 },
{ BatteryTemperatureLevel::Medium, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 512, 0, 0 },
{ BatteryTemperatureLevel::Medium, 3320, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 2048, 0, 0 },
{ BatteryTemperatureLevel::High, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 3952, 512, 0, 0 },
{ BatteryTemperatureLevel::High, 3320, 4050, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 3952, 2048, 0, 0 },
{ BatteryTemperatureLevel::High, 4050, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 2048, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 3952, 512, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, 3320, 4050, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 3952, 2048, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, 4050, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 2048, 0, 0 },
};
constexpr inline const ChargeParametersRule ChargeParametersRulesForBatteryVersion1[] = {
{ BatteryTemperatureLevel::TooLow, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 576, 0, 0 },
{ BatteryTemperatureLevel::Low, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 576, 0, 0 },
{ BatteryTemperatureLevel::Medium, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 1536, 0, 0 },
{ BatteryTemperatureLevel::High, Min, 3984, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 3984, 1536, 0, 0 },
{ BatteryTemperatureLevel::High, 3984, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 1536, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, Min, 3984, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 3984, 1536, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, 3984, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 1536, 0, 0 },
};
constexpr inline const ChargeParametersRule ChargeParametersRulesForBatteryVersion2[] = {
{ BatteryTemperatureLevel::TooLow, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4320, 640, 0, 0 },
{ BatteryTemperatureLevel::Low, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4320, 640, 0, 0 },
{ BatteryTemperatureLevel::Medium, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4320, 1664, 0, 0 },
{ BatteryTemperatureLevel::High, Min, 4080, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4080, 1664, 0, 0 },
{ BatteryTemperatureLevel::High, 4080, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4320, 1664, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, Min, 4080, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4080, 1664, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, 4080, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4320, 1664, 0, 0 },
};
constexpr inline const ChargeParametersRule ChargeParametersRulesForBatteryVersion0ForFive[] = {
{ BatteryTemperatureLevel::TooLow, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 768, 0, 0 },
{ BatteryTemperatureLevel::Low, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 768, 0, 0 },
{ BatteryTemperatureLevel::Medium, Min, Max, Min, 4001, 2049, Max, AcceptablePowerStatesForNotAwakeCharge, util::size(AcceptablePowerStatesForNotAwakeCharge), true, true, 4000, 3072, 40, 112 },
{ BatteryTemperatureLevel::Medium, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 2048, 0, 0 },
{ BatteryTemperatureLevel::High, Min, 4050, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 3952, 2048, 0, 0 },
{ BatteryTemperatureLevel::High, 4050, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 2048, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, Min, 4050, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 3952, 2048, 0, 0 },
{ BatteryTemperatureLevel::TooHigh, 4050, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 2048, 0, 0 },
};
constexpr inline const ChargeParameters ChargeParametersForBatteryVersion0 = {
4, 17, 51, 60, 512, 4208, nullptr, 0, 95.0, 99.0, ChargeParametersRulesForBatteryVersion0, util::size(ChargeParametersRulesForBatteryVersion0)
};
constexpr inline const ChargeParameters ChargeParametersForBatteryVersion1 = {
1, 19, 48, 59, 1536, 4208, nullptr, 0, 95.0, 99.0, ChargeParametersRulesForBatteryVersion1, util::size(ChargeParametersRulesForBatteryVersion1)
};
constexpr inline const ChargeParameters ChargeParametersForBatteryVersion2 = {
1, 19, 48, 59, 1664, 4320, UnknownXTableForBatteryVersion2, util::size(UnknownXTableForBatteryVersion2), 95.0, 100.4, ChargeParametersRulesForBatteryVersion2, util::size(ChargeParametersRulesForBatteryVersion2)
};
constexpr inline const ChargeParameters ChargeParametersForBatteryVersion0ForFive = {
4, 17, 51, 60, 512, 4208, nullptr, 0, 95.0, 99.0, ChargeParametersRulesForBatteryVersion0ForFive, util::size(ChargeParametersRulesForBatteryVersion0ForFive)
};

View file

@ -1,136 +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::powctl::impl {
class IDevice : public ::ams::ddsf::IDevice {
NON_COPYABLE(IDevice);
NON_MOVEABLE(IDevice);
AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::IDevice, ::ams::ddsf::IDevice);
public:
IDevice() : ddsf::IDevice(false) { /* ... */ }
virtual ~IDevice() { /* ... */ }
};
class IPowerControlDriver : public ::ams::ddsf::IDriver {
NON_COPYABLE(IPowerControlDriver);
NON_MOVEABLE(IPowerControlDriver);
AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::IPowerControlDriver, ::ams::ddsf::IDriver);
private:
bool event_handler_enabled;
protected:
constexpr bool IsEventHandlerEnabled() const {
return this->event_handler_enabled;
}
public:
IPowerControlDriver(bool ev) : IDriver(), event_handler_enabled(ev) { /* ... */ }
virtual ~IPowerControlDriver() { /* ... */ }
virtual void InitializeDriver() = 0;
virtual void FinalizeDriver() = 0;
virtual Result GetDeviceSystemEvent(IDevice *device) = 0;
virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) = 0;
/* TODO: Eventually implement proper error status enum? */
virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) = 0;
virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) = 0;
virtual Result GetBatterySocRep(float *out_percent, IDevice *device) = 0;
virtual Result GetBatterySocVf(float *out_percent, IDevice *device) = 0;
virtual Result GetBatteryFullCapacity(u32 *out_mah, IDevice *device) = 0;
virtual Result GetBatteryRemainingCapacity(u32 *out_mah, IDevice *device) = 0;
virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) = 0;
virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) = 0;
virtual Result SetBatteryPercentageFullThreshold(IDevice *device, float percentage) = 0;
virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) = 0;
virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) = 0;
virtual Result GetChargerFastChargeCurrentLimit(u32 *out_ma, IDevice *device) = 0;
virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, u32 ma) = 0;
virtual Result GetChargerChargeVoltageLimit(u32 *out_mv, IDevice *device) = 0;
virtual Result SetChargerChargeVoltageLimit(IDevice *device, u32 mv) = 0;
virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) = 0;
virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) = 0;
virtual Result SetChargerHiZEnabled(IDevice *device, bool en) = 0;
virtual Result GetBatteryAverageCurrent(u32 *out_ma, IDevice *device) = 0;
virtual Result GetBatteryCurrent(u32 *out_ma, IDevice *device) = 0;
virtual Result GetChargerInputCurrentLimit(u32 *out_ma, IDevice *device) = 0;
virtual Result SetChargerInputCurrentLimit(IDevice *device, u32 ma) = 0;
virtual Result GetChargerInputVoltageLimit(u32 *out_mv, IDevice *device) = 0;
virtual Result SetChargerInputVoltageLimit(IDevice *device, u32 mv) = 0;
virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) = 0;
virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) = 0;
virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) = 0;
virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) = 0;
virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) = 0;
virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) = 0;
virtual Result IsBatteryRemoved(bool *out, IDevice *device) = 0;
virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) = 0;
virtual Result GetBatteryCycles(u32 *out, IDevice *device) = 0;
virtual Result SetBatteryCycles(IDevice *device, u32 cycles) = 0;
virtual Result GetBatteryAge(float *out_percent, IDevice *device) = 0;
virtual Result GetBatteryTemperature(float *out_c, IDevice *device) = 0;
virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) = 0;
virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) = 0;
virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) = 0;
virtual Result GetBatteryVCell(u32 *out_mv, IDevice *device) = 0;
virtual Result GetBatteryAverageVCell(u32 *out_mv, IDevice *device) = 0;
virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) = 0;
virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, u32 mv) = 0;
virtual Result GetBatteryOpenCircuitVoltage(u32 *out_mv, IDevice *device) = 0;
virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, u32 mv) = 0;
virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) = 0;
virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) = 0;
virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) = 0;
virtual Result ResetChargerWatchdogTimer(IDevice *device) = 0;
virtual Result GetChargerBatteryCompensation(u32 *out_mo, IDevice *device) = 0;
virtual Result SetChargerBatteryCompensation(IDevice *device, u32 mo) = 0;
virtual Result GetChargerVoltageClamp(u32 *out_mv, IDevice *device) = 0;
virtual Result SetChargerVoltageClamp(IDevice *device, u32 mv) = 0;
};
}

View file

@ -55,8 +55,8 @@ namespace ams::powctl::impl {
virtual Result GetBatterySocVf(float *out_percent, IDevice *device) = 0;
virtual Result GetBatteryFullCapacity(u32 *out_mah, IDevice *device) = 0;
virtual Result GetBatteryRemainingCapacity(u32 *out_mah, IDevice *device) = 0;
virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) = 0;
virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) = 0;
virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) = 0;
virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) = 0;
@ -65,25 +65,25 @@ namespace ams::powctl::impl {
virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) = 0;
virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) = 0;
virtual Result GetChargerFastChargeCurrentLimit(u32 *out_ma, IDevice *device) = 0;
virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, u32 ma) = 0;
virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) = 0;
virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) = 0;
virtual Result GetChargerChargeVoltageLimit(u32 *out_mv, IDevice *device) = 0;
virtual Result SetChargerChargeVoltageLimit(IDevice *device, u32 mv) = 0;
virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) = 0;
virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) = 0;
virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) = 0;
virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) = 0;
virtual Result SetChargerHiZEnabled(IDevice *device, bool en) = 0;
virtual Result GetBatteryAverageCurrent(u32 *out_ma, IDevice *device) = 0;
virtual Result GetBatteryCurrent(u32 *out_ma, IDevice *device) = 0;
virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) = 0;
virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) = 0;
virtual Result GetChargerInputCurrentLimit(u32 *out_ma, IDevice *device) = 0;
virtual Result SetChargerInputCurrentLimit(IDevice *device, u32 ma) = 0;
virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) = 0;
virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) = 0;
virtual Result GetChargerInputVoltageLimit(u32 *out_mv, IDevice *device) = 0;
virtual Result SetChargerInputVoltageLimit(IDevice *device, u32 mv) = 0;
virtual Result GetChargerInputVoltageLimit(int *out_mv, IDevice *device) = 0;
virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) = 0;
virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) = 0;
virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) = 0;
@ -94,12 +94,12 @@ namespace ams::powctl::impl {
virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) = 0;
virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) = 0;
virtual Result IsBatteryRemoved(bool *out, IDevice *device) = 0;
virtual Result IsBatteryPresent(bool *out, IDevice *device) = 0;
virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) = 0;
virtual Result GetBatteryCycles(u32 *out, IDevice *device) = 0;
virtual Result SetBatteryCycles(IDevice *device, u32 cycles) = 0;
virtual Result GetBatteryCycles(int *out, IDevice *device) = 0;
virtual Result SetBatteryCycles(IDevice *device, int cycles) = 0;
virtual Result GetBatteryAge(float *out_percent, IDevice *device) = 0;
@ -109,16 +109,16 @@ namespace ams::powctl::impl {
virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) = 0;
virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) = 0;
virtual Result GetBatteryVCell(u32 *out_mv, IDevice *device) = 0;
virtual Result GetBatteryAverageVCell(u32 *out_mv, IDevice *device) = 0;
virtual Result GetBatteryVCell(int *out_mv, IDevice *device) = 0;
virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) = 0;
virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) = 0;
virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, u32 mv) = 0;
virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) = 0;
virtual Result GetBatteryOpenCircuitVoltage(u32 *out_mv, IDevice *device) = 0;
virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) = 0;
virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, u32 mv) = 0;
virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) = 0;
virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) = 0;
virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) = 0;
@ -126,11 +126,11 @@ namespace ams::powctl::impl {
virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) = 0;
virtual Result ResetChargerWatchdogTimer(IDevice *device) = 0;
virtual Result GetChargerBatteryCompensation(u32 *out_mo, IDevice *device) = 0;
virtual Result SetChargerBatteryCompensation(IDevice *device, u32 mo) = 0;
virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) = 0;
virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) = 0;
virtual Result GetChargerVoltageClamp(u32 *out_mv, IDevice *device) = 0;
virtual Result SetChargerVoltageClamp(IDevice *device, u32 mv) = 0;
virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) = 0;
virtual Result SetChargerVoltageClamp(IDevice *device, int mv) = 0;
};
}

View file

@ -58,7 +58,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetBatterySocVf(out_percent, std::addressof(device));
}
Result GetBatteryFullCapacity(u32 *out_mah, Session &session) {
Result GetBatteryFullCapacity(int *out_mah, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -72,7 +72,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetBatteryFullCapacity(out_mah, std::addressof(device));
}
Result GetBatteryRemainingCapacity(u32 *out_mah, Session &session) {
Result GetBatteryRemainingCapacity(int *out_mah, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -128,7 +128,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetBatteryPercentageFullThreshold(std::addressof(device), percentage);
}
Result GetBatteryAverageCurrent(u32 *out_ma, Session &session) {
Result GetBatteryAverageCurrent(int *out_ma, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -142,7 +142,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetBatteryAverageCurrent(out_ma, std::addressof(device));
}
Result GetBatteryCurrent(u32 *out_ma, Session &session) {
Result GetBatteryCurrent(int *out_ma, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -240,7 +240,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetBatteryI2cShutdownEnabled(std::addressof(device), en);
}
Result IsBatteryRemoved(bool *out, Session &session) {
Result IsBatteryPresent(bool *out, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -251,10 +251,10 @@ namespace ams::powctl {
auto &device = impl.GetDevice().SafeCastTo<impl::IDevice>();
/* Call into the driver. */
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().IsBatteryRemoved(out, std::addressof(device));
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().IsBatteryPresent(out, std::addressof(device));
}
Result GetBatteryCycles(u32 *out, Session &session) {
Result GetBatteryCycles(int *out, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -268,7 +268,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetBatteryCycles(out, std::addressof(device));
}
Result SetBatteryCycles(Session &session, u32 cycles) {
Result SetBatteryCycles(Session &session, int cycles) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -352,7 +352,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetBatteryTemperatureMaximumAlertThreshold(std::addressof(device), c);
}
Result GetBatteryVCell(u32 *out_mv, Session &session) {
Result GetBatteryVCell(int *out_mv, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -366,7 +366,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetBatteryVCell(out_mv, std::addressof(device));
}
Result GetBatteryAverageVCell(u32 *out_mv, Session &session) {
Result GetBatteryAverageVCell(int *out_mv, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -394,7 +394,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetBatteryAverageVCellTime(out, std::addressof(device));
}
Result GetBatteryOpenCircuitVoltage(u32 *out_mv, Session &session) {
Result GetBatteryOpenCircuitVoltage(int *out_mv, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -408,7 +408,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetBatteryOpenCircuitVoltage(out_mv, std::addressof(device));
}
Result SetBatteryVoltageMinimumAlertThreshold(Session &session, u32 mv) {
Result SetBatteryVoltageMinimumAlertThreshold(Session &session, int mv) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -422,7 +422,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetBatteryVoltageMinimumAlertThreshold(std::addressof(device), mv);
}
Result SetBatteryVoltageMaximumAlertThreshold(Session &session, u32 mv) {
Result SetBatteryVoltageMaximumAlertThreshold(Session &session, int mv) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);

View file

@ -58,7 +58,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetChargerChargeCurrentState(std::addressof(device), state);
}
Result GetChargerFastChargeCurrentLimit(u32 *out_ma, Session &session) {
Result GetChargerFastChargeCurrentLimit(int *out_ma, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -72,7 +72,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetChargerFastChargeCurrentLimit(out_ma, std::addressof(device));
}
Result SetChargerFastChargeCurrentLimit(Session &session, u32 ma) {
Result SetChargerFastChargeCurrentLimit(Session &session, int ma) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -86,7 +86,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetChargerFastChargeCurrentLimit(std::addressof(device), ma);
}
Result GetChargerChargeVoltageLimit(u32 *out_mv, Session &session) {
Result GetChargerChargeVoltageLimit(int *out_mv, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -100,7 +100,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetChargerChargeVoltageLimit(out_mv, std::addressof(device));
}
Result SetChargerChargeVoltageLimit(Session &session, u32 mv) {
Result SetChargerChargeVoltageLimit(Session &session, int mv) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -156,7 +156,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetChargerHiZEnabled(std::addressof(device), en);
}
Result GetChargerInputCurrentLimit(u32 *out_ma, Session &session) {
Result GetChargerInputCurrentLimit(int *out_ma, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -170,7 +170,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetChargerInputCurrentLimit(out_ma, std::addressof(device));
}
Result SetChargerInputCurrentLimit(Session &session, u32 ma) {
Result SetChargerInputCurrentLimit(Session &session, int ma) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -184,7 +184,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetChargerInputCurrentLimit(std::addressof(device), ma);
}
Result GetChargerInputVoltageLimit(u32 *out_mv, Session &session) {
Result GetChargerInputVoltageLimit(int *out_mv, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -198,7 +198,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetChargerInputVoltageLimit(out_mv, std::addressof(device));
}
Result SetChargerInputVoltageLimit(Session &session, u32 mv) {
Result SetChargerInputVoltageLimit(Session &session, int mv) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -270,7 +270,7 @@ namespace ams::powctl {
Result ResetChargerWatchdogTimer(Session &session);
Result GetChargerBatteryCompensation(u32 *out_mo, Session &session) {
Result GetChargerBatteryCompensation(int *out_mo, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -284,7 +284,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetChargerBatteryCompensation(out_mo, std::addressof(device));
}
Result SetChargerBatteryCompensation(Session &session, u32 mo) {
Result SetChargerBatteryCompensation(Session &session, int mo) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -298,7 +298,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().SetChargerBatteryCompensation(std::addressof(device), mo);
}
Result GetChargerVoltageClamp(u32 *out_mv, Session &session) {
Result GetChargerVoltageClamp(int *out_mv, Session &session) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);
@ -312,7 +312,7 @@ namespace ams::powctl {
return device.GetDriver().SafeCastTo<impl::IPowerControlDriver>().GetChargerVoltageClamp(out_mv, std::addressof(device));
}
Result SetChargerVoltageClamp(Session &session, u32 mv) {
Result SetChargerVoltageClamp(Session &session, int mv) {
/* Get the session impl. */
auto &impl = GetOpenSessionImpl(session);

View file

@ -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);
}
}

View file

@ -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);
}
};
}

View file

@ -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));
}
}

View file

@ -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);
}
}

View file

@ -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 */
};

View file

@ -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;
}

View file

@ -20,6 +20,6 @@ namespace ams::boot {
/* Boot Reason utilities. */
void DetectBootReason();
u32 GetBootReason();
spl::BootReason GetBootReason();
}

View file

@ -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,
};
}

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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, &reg));
*out = bq24193::DecodeChargeVoltageLimit(reg);
return ResultSuccess();
}
}

View file

@ -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);
}
};
}

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
};

View file

@ -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() {

View file

@ -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();
}