boot: refactor to use sts::boot namespace

This commit is contained in:
Michael Scire 2019-06-22 00:10:21 -07:00
parent 4fbae9e5a4
commit 06416aeded
91 changed files with 3665 additions and 3155 deletions

View file

@ -14,276 +14,286 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include "boot_functions.hpp"
#include "boot_battery_driver.hpp"
#include "boot_calibration.hpp"
#include "boot_i2c_utils.hpp"
const Max17050Parameters *BatteryDriver::GetBatteryParameters() {
const u32 battery_version = Boot::GetBatteryVersion();
const u32 battery_vendor = Boot::GetBatteryVendor();
namespace sts::boot {
if (battery_version == 2) {
if (battery_vendor == 'M') {
return &Max17050Params2M;
/* 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 {
return &Max17050Params2;
*out = static_cast<size_t>(converted_percentage);
}
} 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 Boot::ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
}
Result BatteryDriver::Write(u8 addr, u16 val) {
return Boot::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 ResultSuccess;
}
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]);
Result BatteryDriver::SetShutdownTimer() {
return this->Write(Max17050ShdnTimer, 0xE000);
}
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));
Result BatteryDriver::GetShutdownEnabled(bool *out) {
u16 val = 0;
R_TRY(this->Read(Max17050Config, &val));
*out = (val & 0x0040) != 0;
return ResultSuccess;
}
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);
Result BatteryDriver::SetShutdownEnabled(bool enabled) {
return this->ReadWrite(Max17050Config, 0x0040, enabled ? 0x0040 : 0x0000);
}
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

@ -19,46 +19,47 @@
#include <stratosphere.hpp>
#include "i2c/driver/i2c_api.hpp"
#include "boot_battery_parameters.hpp"
class BatteryDriver {
private:
sts::i2c::driver::Session i2c_session;
public:
BatteryDriver() {
sts::i2c::driver::Initialize();
sts::i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max17050);
}
namespace sts::boot {
~BatteryDriver() {
sts::i2c::driver::CloseSession(this->i2c_session);
sts::i2c::driver::Finalize();
}
private:
static const Max17050Parameters *GetBatteryParameters();
class BatteryDriver {
private:
i2c::driver::Session i2c_session;
public:
BatteryDriver() {
i2c::driver::Initialize();
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max17050);
}
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);
~BatteryDriver() {
i2c::driver::CloseSession(this->i2c_session);
i2c::driver::Finalize();
}
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);
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);
};
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);
};
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -14,70 +14,82 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_battery_icon_low.hpp"
#include "boot_battery_icon_charging.hpp"
#include "boot_battery_icon_charging_red.hpp"
#include "boot_battery_icons.hpp"
#include "boot_display.hpp"
void Boot::ShowLowBatteryIcon() {
Boot::InitializeDisplay();
{
/* Low battery icon is shown for 5 seconds. */
Boot::ShowDisplay(LowBatteryX, LowBatteryY, LowBatteryW, LowBatteryH, LowBattery);
svcSleepThread(5'000'000'000ul);
}
Boot::FinalizeDisplay();
}
namespace sts::boot {
static 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) {
const size_t fill_x = meter_x + meter_w - fill_w;
namespace {
if (fill_x + fill_w > icon_w || meter_y + meter_h > icon_h || fill_x == 0) {
return;
}
/* Pull in icon definitions. */
#include "boot_battery_icon_low.inc"
#include "boot_battery_icon_charging.inc"
#include "boot_battery_icon_charging_red.inc"
u32 *cur_row = icon + meter_y * icon_w + fill_x;
for (size_t y = 0; y < meter_h; y++) {
/* Make last column of meter identical to first column of meter. */
cur_row[-1] = icon[(meter_y + y) * icon_w + meter_x];
/* 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) {
const size_t fill_x = meter_x + meter_w - fill_w;
/* Black out further pixels. */
for (size_t x = 0; x < fill_w; x++) {
cur_row[x] = 0xFF000000;
if (fill_x + fill_w > icon_w || meter_y + meter_h > icon_h || fill_x == 0) {
return;
}
u32 *cur_row = icon + meter_y * icon_w + fill_x;
for (size_t y = 0; y < meter_h; y++) {
/* Make last column of meter identical to first column of meter. */
cur_row[-1] = icon[(meter_y + y) * icon_w + meter_x];
/* Black out further pixels. */
for (size_t x = 0; x < fill_w; x++) {
cur_row[x] = 0xFF000000;
}
cur_row += icon_w;
}
}
cur_row += icon_w;
}
}
void Boot::StartShowChargingIcon(size_t battery_percentage, bool wait) {
const bool is_red = battery_percentage <= 15;
const size_t IconX = is_red ? ChargingRedBatteryX : ChargingBatteryX;
const size_t IconY = is_red ? ChargingRedBatteryY : ChargingBatteryY;
const size_t IconW = is_red ? ChargingRedBatteryW : ChargingBatteryW;
const size_t IconH = is_red ? ChargingRedBatteryH : ChargingBatteryH;
const size_t IconMeterX = is_red ? ChargingRedBatteryMeterX : ChargingBatteryMeterX;
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);
/* Create stack buffer, copy icon into it, draw fill meter, draw. */
{
u32 Icon[IconW * IconH];
std::memcpy(Icon, is_red ? ChargingRedBattery : ChargingBattery, sizeof(Icon));
FillBatteryMeter(Icon, IconW, IconH, IconMeterX, IconMeterY, IconMeterW, IconMeterH, MeterFillW);
Boot::InitializeDisplay();
Boot::ShowDisplay(IconX, IconY, IconW, IconH, Icon);
}
/* Wait for 2 seconds if we're supposed to. */
if (wait) {
svcSleepThread(2'000'000'000ul);
void ShowLowBatteryIcon() {
InitializeDisplay();
{
/* Low battery icon is shown for 5 seconds. */
ShowDisplay(LowBatteryX, LowBatteryY, LowBatteryW, LowBatteryH, LowBattery);
svcSleepThread(5'000'000'000ul);
}
FinalizeDisplay();
}
void StartShowChargingIcon(size_t battery_percentage, bool wait) {
const bool is_red = battery_percentage <= 15;
const size_t IconX = is_red ? ChargingRedBatteryX : ChargingBatteryX;
const size_t IconY = is_red ? ChargingRedBatteryY : ChargingBatteryY;
const size_t IconW = is_red ? ChargingRedBatteryW : ChargingBatteryW;
const size_t IconH = is_red ? ChargingRedBatteryH : ChargingBatteryH;
const size_t IconMeterX = is_red ? ChargingRedBatteryMeterX : ChargingBatteryMeterX;
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);
/* Create stack buffer, copy icon into it, draw fill meter, draw. */
{
u32 Icon[IconW * IconH];
std::memcpy(Icon, is_red ? ChargingRedBattery : ChargingBattery, sizeof(Icon));
FillBatteryMeter(Icon, IconW, IconH, IconMeterX, IconMeterY, IconMeterW, IconMeterH, MeterFillW);
InitializeDisplay();
ShowDisplay(IconX, IconY, IconW, IconH, Icon);
}
/* Wait for 2 seconds if we're supposed to. */
if (wait) {
svcSleepThread(2'000'000'000ul);
}
}
void EndShowChargingIcon() {
FinalizeDisplay();
}
}
void Boot::EndShowChargingIcon() {
Boot::FinalizeDisplay();
}

View file

@ -18,13 +18,11 @@
#include <switch.h>
#include <stratosphere.hpp>
#define IRAM_BASE 0x40000000ull
#define IRAM_SIZE 0x40000
#define IRAM_PAYLOAD_MAX_SIZE 0x2E000
#define IRAM_PAYLOAD_BASE 0x40010000ull
namespace sts::boot {
class BootRebootManager {
public:
static Result PerformReboot();
static void RebootForFatalError(AtmosphereFatalErrorContext *ctx);
};
/* Battery Display utilities. */
void ShowLowBatteryIcon();
void StartShowChargingIcon(size_t battery_percentage, bool wait);
void EndShowChargingIcon();
}

View file

@ -14,99 +14,96 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
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;
static constexpr u8 Max17050Status = 0x00;
static constexpr u8 Max17050VAlrtThreshold = 0x01;
static constexpr u8 Max17050TAlrtThreshold = 0x02;
static constexpr u8 Max17050SocAlrtThreshold = 0x03;
static constexpr u8 Max17050AtRate = 0x04;
static constexpr u8 Max17050RemCapRep = 0x05;
static constexpr u8 Max17050SocRep = 0x06;
static constexpr u8 Max17050Age = 0x07;
static constexpr u8 Max17050Temperature = 0x08;
static constexpr u8 Max17050VCell = 0x09;
static constexpr u8 Max17050Current = 0x0A;
static constexpr u8 Max17050AverageCurrent = 0x0B;
static constexpr u8 Max17050SocMix = 0x0D;
static constexpr u8 Max17050SocAv = 0x0E;
static constexpr u8 Max17050RemCapMix = 0x0F;
static constexpr u8 Max17050FullCap = 0x10;
static constexpr u8 Max17050Tte = 0x11;
static constexpr u8 Max17050QResidual00 = 0x12;
static constexpr u8 Max17050FullSocThr = 0x13;
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;
static constexpr u8 Max17050AverageTemp = 0x16;
static constexpr u8 Max17050Cycles = 0x17;
static constexpr u8 Max17050DesignCap = 0x18;
static constexpr u8 Max17050AverageVCell = 0x19;
static constexpr u8 Max17050MaxMinTemp = 0x1A;
static constexpr u8 Max17050MaxMinVoltage = 0x1B;
static constexpr u8 Max17050MaxMinCurrent = 0x1C;
static constexpr u8 Max17050Config = 0x1D;
static constexpr u8 Max17050IChgTerm = 0x1E;
static constexpr u8 Max17050RemCapAv = 0x1F;
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;
static constexpr u8 Max17050Version = 0x21;
static constexpr u8 Max17050QResidual10 = 0x22;
static constexpr u8 Max17050FullCapNom = 0x23;
static constexpr u8 Max17050TempNom = 0x24;
static constexpr u8 Max17050TempLim = 0x25;
constexpr u8 Max17050Version = 0x21;
constexpr u8 Max17050QResidual10 = 0x22;
constexpr u8 Max17050FullCapNom = 0x23;
constexpr u8 Max17050TempNom = 0x24;
constexpr u8 Max17050TempLim = 0x25;
static constexpr u8 Max17050Ain = 0x27;
static constexpr u8 Max17050LearnCfg = 0x28;
static constexpr u8 Max17050FilterCfg = 0x29;
static constexpr u8 Max17050RelaxCfg = 0x2A;
static constexpr u8 Max17050MiscCfg = 0x2B;
static constexpr u8 Max17050TGain = 0x2C;
static constexpr u8 Max17050TOff = 0x2D;
static constexpr u8 Max17050CGain = 0x2E;
static constexpr u8 Max17050COff = 0x2F;
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;
static constexpr u8 Max17050QResidual20 = 0x32;
constexpr u8 Max17050QResidual20 = 0x32;
static constexpr u8 Max17050IAvgEmpty = 0x36;
static constexpr u8 Max17050FCtc = 0x37;
static constexpr u8 Max17050RComp0 = 0x38;
static constexpr u8 Max17050TempCo = 0x39;
static constexpr u8 Max17050VEmpty = 0x3A;
constexpr u8 Max17050IAvgEmpty = 0x36;
constexpr u8 Max17050FCtc = 0x37;
constexpr u8 Max17050RComp0 = 0x38;
constexpr u8 Max17050TempCo = 0x39;
constexpr u8 Max17050VEmpty = 0x3A;
static constexpr u8 Max17050FStat = 0x3D;
static constexpr u8 Max17050Timer = 0x3E;
static constexpr u8 Max17050ShdnTimer = 0x3F;
constexpr u8 Max17050FStat = 0x3D;
constexpr u8 Max17050Timer = 0x3E;
constexpr u8 Max17050ShdnTimer = 0x3F;
static constexpr u8 Max17050QResidual30 = 0x42;
constexpr u8 Max17050QResidual30 = 0x42;
static constexpr u8 Max17050DQAcc = 0x45;
static constexpr u8 Max17050DPAcc = 0x46;
constexpr u8 Max17050DQAcc = 0x45;
constexpr u8 Max17050DPAcc = 0x46;
static constexpr u8 Max17050SocVf0 = 0x48;
constexpr u8 Max17050SocVf0 = 0x48;
static constexpr u8 Max17050Qh0 = 0x4C;
static constexpr u8 Max17050Qh = 0x4D;
constexpr u8 Max17050Qh0 = 0x4C;
constexpr u8 Max17050Qh = 0x4D;
static constexpr u8 Max17050SocVfAccess = 0x60;
constexpr u8 Max17050SocVfAccess = 0x60;
static constexpr u8 Max17050ModelAccess0 = 0x62;
static constexpr u8 Max17050ModelAccess1 = 0x63;
constexpr u8 Max17050ModelAccess0 = 0x62;
constexpr u8 Max17050ModelAccess1 = 0x63;
static constexpr u8 Max17050ModelChrTblStart = 0x80;
static constexpr u8 Max17050ModelChrTblEnd = 0xB0;
constexpr u8 Max17050ModelChrTblStart = 0x80;
constexpr u8 Max17050ModelChrTblEnd = 0xB0;
static constexpr u8 Max17050VFocV = 0xFB;
static constexpr u8 Max17050SocVf = 0xFF;
constexpr u8 Max17050VFocV = 0xFB;
constexpr u8 Max17050SocVf = 0xFF;
static constexpr size_t Max17050ModelChrTblSize = Max17050ModelChrTblEnd - Max17050ModelChrTblStart;
constexpr size_t Max17050ModelChrTblSize = Max17050ModelChrTblEnd - Max17050ModelChrTblStart;
struct Max17050Parameters {
u16 relaxcfg;
@ -129,7 +126,7 @@ struct Max17050Parameters {
static_assert(sizeof(Max17050Parameters) == 0x7E, "Max17050Parameters definition!");
static constexpr Max17050Parameters Max17050ParamsA = {
constexpr Max17050Parameters Max17050ParamsA = {
0x203B, /* relaxcfg */
0x0053, /* rcomp0 */
0x1C22, /* tempco */
@ -155,7 +152,7 @@ static constexpr Max17050Parameters Max17050ParamsA = {
0x1D2A /* iavgempty */
};
static constexpr Max17050Parameters Max17050ParamsM = {
constexpr Max17050Parameters Max17050ParamsM = {
0x203B, /* relaxcfg */
0x0085, /* rcomp0 */
0x1625, /* tempco */
@ -181,7 +178,7 @@ static constexpr Max17050Parameters Max17050ParamsM = {
0x1D2A /* iavgempty */
};
static constexpr Max17050Parameters Max17050ParamsR = {
constexpr Max17050Parameters Max17050ParamsR = {
0x203B, /* relaxcfg */
0x0048, /* rcomp0 */
0x2034, /* tempco */
@ -207,7 +204,7 @@ static constexpr Max17050Parameters Max17050ParamsR = {
0x1D2A /* iavgempty */
};
static constexpr Max17050Parameters Max17050Params1 = {
constexpr Max17050Parameters Max17050Params1 = {
0x203B, /* relaxcfg */
0x0040, /* rcomp0 */
0x1624, /* tempco */
@ -233,7 +230,7 @@ static constexpr Max17050Parameters Max17050Params1 = {
0x1584 /* iavgempty */
};
static constexpr Max17050Parameters Max17050Params2 = {
constexpr Max17050Parameters Max17050Params2 = {
0x203B, /* relaxcfg */
0x004A, /* rcomp0 */
0x1D23, /* tempco */
@ -259,7 +256,7 @@ static constexpr Max17050Parameters Max17050Params2 = {
0x1680 /* iavgempty */
};
static constexpr Max17050Parameters Max17050Params2M = {
constexpr Max17050Parameters Max17050Params2M = {
0x203B, /* relaxcfg */
0x0049, /* rcomp0 */
0x222A, /* tempco */

View file

@ -14,100 +14,112 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_boot_reason.hpp"
#include "boot_pmic_driver.hpp"
#include "boot_rtc_driver.hpp"
#include "boot_spl_utils.hpp"
static u32 g_boot_reason = 0;
static bool g_detected_boot_reason = false;
namespace sts::boot {
struct BootReasonValue {
union {
struct {
u8 power_intr;
u8 rtc_intr;
u8 nv_erc;
u8 boot_reason;
namespace {
/* Types. */
struct BootReasonValue {
union {
struct {
u8 power_intr;
u8 rtc_intr;
u8 nv_erc;
u8 boot_reason;
};
u32 value;
};
};
u32 value;
};
};
static u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
if (power_intr & 0x08) {
return 2;
}
if (rtc_intr & 0x02) {
return 3;
}
if (power_intr & 0x80) {
return 1;
}
if (rtc_intr & 0x04) {
if (nv_erc != 0x80 && !Boot::IsRecoveryBoot()) {
return 4;
/* Globals. */
u32 g_boot_reason = 0;
bool g_detected_boot_reason = false;
/* Helpers. */
u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
if (power_intr & 0x08) {
return 2;
}
if (rtc_intr & 0x02) {
return 3;
}
if (power_intr & 0x80) {
return 1;
}
if (rtc_intr & 0x04) {
if (nv_erc != 0x80 && !IsRecoveryBoot()) {
return 4;
}
}
if ((nv_erc & 0x40) && ac_ok) {
return 1;
}
return 0;
}
}
if ((nv_erc & 0x40) && ac_ok) {
return 1;
}
return 0;
}
void Boot::DetectBootReason() {
u8 power_intr;
u8 rtc_intr;
u8 rtc_intr_m;
u8 nv_erc;
bool ac_ok;
}
/* Get values from PMIC. */
{
PmicDriver pmic_driver;
if (R_FAILED(pmic_driver.GetPowerIntr(&power_intr))) {
std::abort();
}
if (R_FAILED(pmic_driver.GetNvErc(&nv_erc))) {
std::abort();
}
if (R_FAILED(pmic_driver.GetAcOk(&ac_ok))) {
void DetectBootReason() {
u8 power_intr;
u8 rtc_intr;
u8 rtc_intr_m;
u8 nv_erc;
bool ac_ok;
/* Get values from PMIC. */
{
PmicDriver pmic_driver;
if (R_FAILED(pmic_driver.GetPowerIntr(&power_intr))) {
std::abort();
}
if (R_FAILED(pmic_driver.GetNvErc(&nv_erc))) {
std::abort();
}
if (R_FAILED(pmic_driver.GetAcOk(&ac_ok))) {
std::abort();
}
}
/* Get values from RTC. */
{
RtcDriver rtc_driver;
if (R_FAILED(rtc_driver.GetRtcIntr(&rtc_intr))) {
std::abort();
}
if (R_FAILED(rtc_driver.GetRtcIntrM(&rtc_intr_m))) {
std::abort();
}
}
/* Set global derived boot reason. */
g_boot_reason = MakeBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok);
/* Set boot reason for SPL. */
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) {
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;
if (R_FAILED(splSetBootReason(boot_reason_value.value))) {
std::abort();
}
}
g_detected_boot_reason = true;
}
u32 GetBootReason() {
if (!g_detected_boot_reason) {
std::abort();
}
return g_boot_reason;
}
/* Get values from RTC. */
{
RtcDriver rtc_driver;
if (R_FAILED(rtc_driver.GetRtcIntr(&rtc_intr))) {
std::abort();
}
if (R_FAILED(rtc_driver.GetRtcIntrM(&rtc_intr_m))) {
std::abort();
}
}
/* Set global derived boot reason. */
g_boot_reason = MakeBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok);
/* Set boot reason for SPL. */
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) {
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;
if (R_FAILED(splSetBootReason(boot_reason_value.value))) {
std::abort();
}
}
g_detected_boot_reason = true;
}
u32 Boot::GetBootReason() {
if (!g_detected_boot_reason) {
std::abort();
}
return g_boot_reason;
}
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
/* Boot Reason utilities. */
void DetectBootReason();
u32 GetBootReason();
}

View file

@ -14,126 +14,127 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
namespace sts::boot::bq24193 {
static constexpr u8 Bq24193InputSourceControl = 0x00;
static constexpr u8 Bq24193PowerOnConfiguration = 0x01;
static constexpr u8 Bq24193ChargeCurrentControl = 0x02;
static constexpr u8 Bq24193PreChargeTerminationCurrentControl = 0x03;
static constexpr u8 Bq24193ChargeVoltageControl = 0x04;
static constexpr u8 Bq24193ChargeTerminationTimerControl = 0x05;
static constexpr u8 Bq24193IrCompensationThermalRegulationControl = 0x06;
static constexpr u8 Bq24193MiscOperationControl = 0x07;
static constexpr u8 Bq24193SystemStatus = 0x08;
static constexpr u8 Bq24193Fault = 0x09;
static constexpr u8 Bq24193VendorPartRevisionStatus = 0x0A;
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),
};
enum ChargerConfiguration : u8 {
ChargerConfiguration_ChargeDisable = (0 << 4),
ChargerConfiguration_ChargeBattery = (1 << 4),
ChargerConfiguration_Otg = (2 << 4),
};
static constexpr u32 ChargeVoltageLimitMin = 3504;
static constexpr u32 ChargeVoltageLimitMax = 4208;
constexpr u32 ChargeVoltageLimitMin = 3504;
constexpr u32 ChargeVoltageLimitMax = 4208;
static inline u8 EncodeChargeVoltageLimit(u32 voltage) {
if (voltage < ChargeVoltageLimitMin || voltage > ChargeVoltageLimitMax) {
std::abort();
inline u8 EncodeChargeVoltageLimit(u32 voltage) {
if (voltage < ChargeVoltageLimitMin || voltage > ChargeVoltageLimitMax) {
std::abort();
}
voltage -= ChargeVoltageLimitMin;
voltage >>= 4;
return static_cast<u8>(voltage << 2);
}
voltage -= ChargeVoltageLimitMin;
voltage >>= 4;
return static_cast<u8>(voltage << 2);
}
static inline u32 DecodeChargeVoltageLimit(u8 reg) {
return ChargeVoltageLimitMin + (static_cast<u32>(reg & 0xFC) << 2);
}
static constexpr u32 FastChargeCurrentLimitMin = 512;
static constexpr u32 FastChargeCurrentLimitMax = 4544;
static inline u8 EncodeFastChargeCurrentLimit(u32 current) {
if (current < FastChargeCurrentLimitMin || current > FastChargeCurrentLimitMax) {
std::abort();
inline u32 DecodeChargeVoltageLimit(u8 reg) {
return ChargeVoltageLimitMin + (static_cast<u32>(reg & 0xFC) << 2);
}
current -= FastChargeCurrentLimitMin;
current >>= 6;
return static_cast<u8>(current << 2);
}
static inline u32 DecodeFastChargeCurrentLimit(u8 reg) {
return FastChargeCurrentLimitMin + (static_cast<u32>(reg & 0xFC) << 4);
}
constexpr u32 FastChargeCurrentLimitMin = 512;
constexpr u32 FastChargeCurrentLimitMax = 4544;
enum InputCurrentLimit : u8 {
InputCurrentLimit_100mA = 0,
InputCurrentLimit_150mA = 1,
InputCurrentLimit_500mA = 2,
InputCurrentLimit_900mA = 3,
InputCurrentLimit_1200mA = 4,
InputCurrentLimit_1500mA = 5,
InputCurrentLimit_2000mA = 6,
InputCurrentLimit_3000mA = 7,
};
static constexpr u32 PreChargeCurrentLimitMin = 128;
static constexpr u32 PreChargeCurrentLimitMax = 2048;
static inline u8 EncodePreChargeCurrentLimit(u32 current) {
if (current < PreChargeCurrentLimitMin || current > PreChargeCurrentLimitMax) {
std::abort();
inline u8 EncodeFastChargeCurrentLimit(u32 current) {
if (current < FastChargeCurrentLimitMin || current > FastChargeCurrentLimitMax) {
std::abort();
}
current -= FastChargeCurrentLimitMin;
current >>= 6;
return static_cast<u8>(current << 2);
}
current -= PreChargeCurrentLimitMin;
current >>= 7;
return static_cast<u8>(current << 4);
}
static inline u32 DecodePreChargeCurrentLimit(u8 reg) {
return PreChargeCurrentLimitMin + (static_cast<u32>(reg & 0xF0) << 3);
}
static constexpr u32 TerminationCurrentLimitMin = 128;
static constexpr u32 TerminationCurrentLimitMax = 2048;
static inline u8 EncodeTerminationCurrentLimit(u32 current) {
if (current < TerminationCurrentLimitMin || current > TerminationCurrentLimitMax) {
std::abort();
inline u32 DecodeFastChargeCurrentLimit(u8 reg) {
return FastChargeCurrentLimitMin + (static_cast<u32>(reg & 0xFC) << 4);
}
current -= TerminationCurrentLimitMin;
current >>= 7;
return static_cast<u8>(current);
}
static inline u32 DecodeTerminationCurrentLimit(u8 reg) {
return TerminationCurrentLimitMin + (static_cast<u32>(reg & 0xF) << 7);
}
enum InputCurrentLimit : u8 {
InputCurrentLimit_100mA = 0,
InputCurrentLimit_150mA = 1,
InputCurrentLimit_500mA = 2,
InputCurrentLimit_900mA = 3,
InputCurrentLimit_1200mA = 4,
InputCurrentLimit_1500mA = 5,
InputCurrentLimit_2000mA = 6,
InputCurrentLimit_3000mA = 7,
};
static constexpr u32 MinimumSystemVoltageLimitMin = 3000;
static constexpr u32 MinimumSystemVoltageLimitMax = 3700;
constexpr u32 PreChargeCurrentLimitMin = 128;
constexpr u32 PreChargeCurrentLimitMax = 2048;
static inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
if (voltage < MinimumSystemVoltageLimitMin || voltage > MinimumSystemVoltageLimitMax) {
std::abort();
inline u8 EncodePreChargeCurrentLimit(u32 current) {
if (current < PreChargeCurrentLimitMin || current > PreChargeCurrentLimitMax) {
std::abort();
}
current -= PreChargeCurrentLimitMin;
current >>= 7;
return static_cast<u8>(current << 4);
}
voltage -= MinimumSystemVoltageLimitMin;
voltage /= 100;
return static_cast<u8>(voltage << 1);
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) {
if (current < TerminationCurrentLimitMin || current > TerminationCurrentLimitMax) {
std::abort();
}
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) {
if (voltage < MinimumSystemVoltageLimitMin || voltage > MinimumSystemVoltageLimitMax) {
std::abort();
}
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,
};
}
static 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

@ -14,85 +14,95 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_calibration.hpp"
static constexpr size_t BatteryLotOffset = 0x2CE0;
static constexpr size_t BatteryLotSize = 0x20;
static constexpr size_t BatteryVersionOffset = 0x4310;
static constexpr size_t BatteryVersionSize = 0x10;
namespace sts::boot {
static constexpr u32 DefaultBatteryVendor = static_cast<u32>('A');
static constexpr u32 DefaultBatteryVersion = 0;
namespace {
static constexpr Result ResultCalInvalidCrc = 0xCAC6; /* TODO: Verify this really is cal, move to libstrat results. */
/* Convenience definitions. */
constexpr size_t BatteryLotOffset = 0x2CE0;
constexpr size_t BatteryLotSize = 0x20;
constexpr size_t BatteryVersionOffset = 0x4310;
constexpr size_t BatteryVersionSize = 0x10;
u16 Boot::GetCrc16(const void *data, size_t size) {
static constexpr u16 s_crc_table[0x10] = {
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
};
constexpr u32 DefaultBatteryVendor = static_cast<u32>('A');
constexpr u32 DefaultBatteryVersion = 0;
constexpr Result ResultCalInvalidCrc = 0xCAC6; /* TODO: Verify this really is cal, move to libstrat results. */
/* 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
};
if (data == nullptr) {
std::abort();
}
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);
if (GetCrc16(data, size - sizeof(u16)) != *(reinterpret_cast<const u16 *>(&data_u8[size - sizeof(u16)]))) {
return ResultCalInvalidCrc;
}
return ResultSuccess;
}
Result GetBatteryVendorImpl(u32 *vendor) {
FsStorage s;
R_TRY(fsOpenBisStorage(&s, FsBisStorageId_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, FsBisStorageId_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;
}
if (data == nullptr) {
std::abort();
}
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]);
u32 GetBatteryVendor() {
u32 vendor;
if (R_FAILED(GetBatteryVendorImpl(&vendor))) {
return DefaultBatteryVendor;
}
return vendor;
}
return crc16;
}
static Result ValidateCalibrationCrc16(const void *data, size_t size) {
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
if (Boot::GetCrc16(data, size - sizeof(u16)) != *(reinterpret_cast<const u16 *>(&data_u8[size - sizeof(u16)]))) {
return ResultCalInvalidCrc;
u32 GetBatteryVersion() {
u32 version;
if (R_FAILED(GetBatteryVersionImpl(&version))) {
return DefaultBatteryVersion;
}
return version;
}
return ResultSuccess;
}
static Result GetBatteryVendorImpl(u32 *vendor) {
FsStorage s;
R_TRY(fsOpenBisStorage(&s, FsBisStorageId_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;
}
static Result GetBatteryVersionImpl(u32 *version) {
FsStorage s;
R_TRY(fsOpenBisStorage(&s, FsBisStorageId_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 Boot::GetBatteryVendor() {
u32 vendor;
if (R_FAILED(GetBatteryVendorImpl(&vendor))) {
return DefaultBatteryVendor;
}
return vendor;
}
u32 Boot::GetBatteryVersion() {
u32 version;
if (R_FAILED(GetBatteryVersionImpl(&version))) {
return DefaultBatteryVersion;
}
return version;
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
/* Calibration utilities. */
u32 GetBatteryVersion();
u32 GetBatteryVendor();
}

View file

@ -14,23 +14,33 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_change_voltage.hpp"
#include "boot_pmc_wrapper.hpp"
static constexpr u32 Sdmmc3VoltageBit = (1 << 13); /* SDMMC3 */
static constexpr u32 AudioVoltageBit = (1 << 18); /* AUDIO_HV */
static constexpr u32 GpioVoltageBit = (1 << 21); /* GPIO */
static constexpr u32 SpiVoltageBit = (1 << 23); /* SPI_HV */
namespace sts::boot {
static constexpr u32 VoltageChangeMask = SpiVoltageBit | GpioVoltageBit | AudioVoltageBit | Sdmmc3VoltageBit;
namespace {
static constexpr u32 PmcPwrDet = 0x7000E448;
static constexpr u32 PmcPwrDetVal = 0x7000E4E4;
/* Convenience definitions. */
constexpr u32 Sdmmc3VoltageBit = (1 << 13); /* SDMMC3 */
constexpr u32 AudioVoltageBit = (1 << 18); /* AUDIO_HV */
constexpr u32 GpioVoltageBit = (1 << 21); /* GPIO */
constexpr u32 SpiVoltageBit = (1 << 23); /* SPI_HV */
void Boot::ChangeGpioVoltageTo1_8v() {
/* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */
WritePmcRegister(PmcPwrDet, VoltageChangeMask, VoltageChangeMask);
WritePmcRegister(PmcPwrDetVal, 0, VoltageChangeMask);
constexpr u32 VoltageChangeMask = SpiVoltageBit | GpioVoltageBit | AudioVoltageBit | Sdmmc3VoltageBit;
constexpr u32 PmcPwrDet = 0x7000E448;
constexpr u32 PmcPwrDetVal = 0x7000E4E4;
}
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);
/* Sleep for 100 us. */
svcSleepThread(100'000ul);
}
/* Sleep for 100 us. */
svcSleepThread(100'000ul);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
void ChangeGpioVoltageTo1_8v();;
}

View file

@ -16,116 +16,121 @@
#include <switch.h>
#include <stratosphere.hpp>
#include "boot_charger_driver.hpp"
Result ChargerDriver::Read(u8 addr, u8 *out) {
return Boot::ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
}
namespace sts::boot {
Result ChargerDriver::Write(u8 addr, u8 val) {
return Boot::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(InputCurrentLimit_500mA));
Result ChargerDriver::Read(u8 addr, u8 *out) {
return ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
}
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(WatchdogTimerSetting_Disabled));
R_TRY(this->SetChargingSafetyTimerEnabled(false));
R_TRY(this->ResetWatchdogTimer());
R_TRY(this->SetBoostModeCurrentLimit(BoostModeCurrentLimit_500mA));
R_TRY(this->SetHiZEnabled(false));
Result ChargerDriver::Write(u8 addr, u8 val) {
return WriteI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(&val), sizeof(val), &addr, sizeof(addr));
}
return ResultSuccess;
}
Result ChargerDriver::ReadWrite(u8 addr, u8 mask, u8 val) {
u8 cur_val;
R_TRY(this->Read(addr, &cur_val));
Result ChargerDriver::SetChargeEnabled(bool enabled) {
Boot::GpioSetValue(GpioPadName_Bq24193Charger, enabled ? GpioValue_Low : GpioValue_High);
return this->SetChargerConfiguration(ChargerConfiguration_ChargeBattery);
}
const u8 new_val = (cur_val & ~mask) | val;
R_TRY(this->Write(addr, new_val));
return ResultSuccess;
}
Result ChargerDriver::SetChargerConfiguration(ChargerConfiguration config) {
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x30, config);
}
Result ChargerDriver::Initialize() {
return this->Initialize(true);
}
Result ChargerDriver::SetChargeVoltageLimit(u32 voltage) {
return this->ReadWrite(Bq24193ChargeVoltageControl, 0xFC, EncodeChargeVoltageLimit(voltage));
}
Result ChargerDriver::Initialize(bool set_input_current_limit) {
if (set_input_current_limit) {
R_TRY(this->SetInputCurrentLimit(bq24193::InputCurrentLimit_500mA));
}
Result ChargerDriver::SetFastChargeCurrentLimit(u32 current) {
return this->ReadWrite(Bq24193ChargeCurrentControl, 0xFC, EncodeFastChargeCurrentLimit(current));
}
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));
Result ChargerDriver::SetInputCurrentLimit(InputCurrentLimit current) {
return this->ReadWrite(Bq24193InputSourceControl, 0x07, current);
}
return ResultSuccess;
}
Result ChargerDriver::SetForce20PercentChargeCurrent(bool force) {
return this->ReadWrite(Bq24193ChargeCurrentControl, 0x01, force ? 1 : 0);
}
Result ChargerDriver::SetChargeEnabled(bool enabled) {
gpio::SetValue(GpioPadName_Bq24193Charger, enabled ? GpioValue_Low : GpioValue_High);
return this->SetChargerConfiguration(bq24193::ChargerConfiguration_ChargeBattery);
}
Result ChargerDriver::SetPreChargeCurrentLimit(u32 current) {
return this->ReadWrite(Bq24193PreChargeTerminationCurrentControl, 0xF0, EncodePreChargeCurrentLimit(current));
}
Result ChargerDriver::SetChargerConfiguration(bq24193::ChargerConfiguration config) {
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x30, config);
}
Result ChargerDriver::SetTerminationCurrentLimit(u32 current) {
return this->ReadWrite(Bq24193PreChargeTerminationCurrentControl, 0x0F, EncodeTerminationCurrentLimit(current));
}
Result ChargerDriver::SetChargeVoltageLimit(u32 voltage) {
return this->ReadWrite(bq24193::ChargeVoltageControl, 0xFC, bq24193::EncodeChargeVoltageLimit(voltage));
}
Result ChargerDriver::SetMinimumSystemVoltageLimit(u32 voltage) {
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x0E, EncodeMinimumSystemVoltageLimit(voltage));
}
Result ChargerDriver::SetFastChargeCurrentLimit(u32 current) {
return this->ReadWrite(bq24193::ChargeCurrentControl, 0xFC, bq24193::EncodeFastChargeCurrentLimit(current));
}
Result ChargerDriver::SetWatchdogTimerSetting(WatchdogTimerSetting setting) {
return this->ReadWrite(Bq24193ChargeTerminationTimerControl, 0x30, setting);
}
Result ChargerDriver::SetInputCurrentLimit(bq24193::InputCurrentLimit current) {
return this->ReadWrite(bq24193::InputSourceControl, 0x07, current);
}
Result ChargerDriver::SetChargingSafetyTimerEnabled(bool enabled) {
return this->ReadWrite(Bq24193ChargeTerminationTimerControl, 0x08, enabled ? 0x08 : 0);
}
Result ChargerDriver::SetForce20PercentChargeCurrent(bool force) {
return this->ReadWrite(bq24193::ChargeCurrentControl, 0x01, force ? 1 : 0);
}
Result ChargerDriver::ResetWatchdogTimer() {
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x40, 0x40);
}
Result ChargerDriver::SetPreChargeCurrentLimit(u32 current) {
return this->ReadWrite(bq24193::PreChargeTerminationCurrentControl, 0xF0, bq24193::EncodePreChargeCurrentLimit(current));
}
Result ChargerDriver::SetBoostModeCurrentLimit(BoostModeCurrentLimit current) {
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x01, current);
}
Result ChargerDriver::SetTerminationCurrentLimit(u32 current) {
return this->ReadWrite(bq24193::PreChargeTerminationCurrentControl, 0x0F, bq24193::EncodeTerminationCurrentLimit(current));
}
Result ChargerDriver::SetHiZEnabled(bool enabled) {
return this->ReadWrite(Bq24193InputSourceControl, 0x80, enabled ? 0x80 : 0);
}
Result ChargerDriver::SetMinimumSystemVoltageLimit(u32 voltage) {
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x0E, bq24193::EncodeMinimumSystemVoltageLimit(voltage));
}
Result ChargerDriver::GetInputCurrentLimit(InputCurrentLimit *out) {
u8 limit;
R_TRY(this->Read(Bq24193InputSourceControl, &limit));
*out = static_cast<InputCurrentLimit>(limit);
return ResultSuccess;
}
Result ChargerDriver::SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting setting) {
return this->ReadWrite(bq24193::ChargeTerminationTimerControl, 0x30, setting);
}
Result ChargerDriver::GetChargeVoltageLimit(u32 *out) {
u8 reg;
R_TRY(this->Read(Bq24193ChargeVoltageControl, &reg));
*out = DecodeChargeVoltageLimit(reg);
return ResultSuccess;
}
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

@ -18,51 +18,55 @@
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c/driver/i2c_api.hpp"
#include "boot_functions.hpp"
#include "boot_bq24193_charger.hpp"
#include "boot_gpio_utils.hpp"
#include "boot_i2c_utils.hpp"
class ChargerDriver {
private:
static constexpr u32 GpioPadName_Bq24193Charger = 0xA;
private:
sts::i2c::driver::Session i2c_session;
public:
ChargerDriver() {
sts::i2c::driver::Initialize();
sts::i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Bq24193);
namespace sts::boot {
Boot::GpioConfigure(GpioPadName_Bq24193Charger);
Boot::GpioSetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output);
}
class ChargerDriver {
private:
static constexpr u32 GpioPadName_Bq24193Charger = 0xA;
private:
i2c::driver::Session i2c_session;
public:
ChargerDriver() {
i2c::driver::Initialize();
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Bq24193);
~ChargerDriver() {
sts::i2c::driver::CloseSession(this->i2c_session);
sts::i2c::driver::Finalize();
}
private:
Result Read(u8 addr, u8 *out_data);
Result Write(u8 addr, u8 val);
Result ReadWrite(u8 addr, u8 mask, u8 val);
gpio::Configure(GpioPadName_Bq24193Charger);
gpio::SetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output);
}
Result SetInputCurrentLimit(InputCurrentLimit current);
Result SetForce20PercentChargeCurrent(bool force);
Result SetPreChargeCurrentLimit(u32 current);
Result SetTerminationCurrentLimit(u32 current);
Result SetMinimumSystemVoltageLimit(u32 voltage);
Result SetWatchdogTimerSetting(WatchdogTimerSetting setting);
Result SetChargingSafetyTimerEnabled(bool enabled);
Result ResetWatchdogTimer();
Result SetBoostModeCurrentLimit(BoostModeCurrentLimit current);
Result SetHiZEnabled(bool enabled);
~ChargerDriver() {
i2c::driver::CloseSession(this->i2c_session);
i2c::driver::Finalize();
}
private:
Result Read(u8 addr, u8 *out_data);
Result Write(u8 addr, u8 val);
Result ReadWrite(u8 addr, u8 mask, u8 val);
public:
Result Initialize();
Result Initialize(bool set_input_current_limit);
Result SetChargeVoltageLimit(u32 voltage);
Result SetFastChargeCurrentLimit(u32 current);
Result SetChargeEnabled(bool enabled);
Result SetChargerConfiguration(ChargerConfiguration config);
Result GetInputCurrentLimit(InputCurrentLimit *out);
Result GetChargeVoltageLimit(u32 *out);
};
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);
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);
};
}

View file

@ -14,262 +14,276 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_pmic_driver.hpp"
#include "boot_battery_driver.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_power_utils.hpp"
enum CheckBatteryResult {
CheckBatteryResult_Success = 0,
CheckBatteryResult_Shutdown = 1,
CheckBatteryResult_Reboot = 2,
};
namespace sts::boot {
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;
};
namespace {
static constexpr BatteryChargeParameters BatteryChargeParameters0 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 60,
.allow_high_temp_charge_max_voltage = 4050,
.charge_voltage_limit_default = 4208,
.charge_voltage_limit_high_temp = 3952,
.allow_fast_charge_min_voltage = 3320,
.fast_charge_current_limit_default = 0x800,
.fast_charge_current_limit_low_temp = 0x300,
.fast_charge_current_limit_low_voltage = 0x200,
};
/* Types. */
enum class CheckBatteryResult {
Success,
Shutdown,
Reboot,
};
static constexpr BatteryChargeParameters BatteryChargeParameters1 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 59,
.allow_high_temp_charge_max_voltage = 3984,
.charge_voltage_limit_default = 4208,
.charge_voltage_limit_high_temp = 3984,
.allow_fast_charge_min_voltage = 0,
.fast_charge_current_limit_default = 0x600,
.fast_charge_current_limit_low_temp = 0x240,
.fast_charge_current_limit_low_voltage = 0x600,
};
struct BatteryChargeParameters {
u32 temp_min;
u32 temp_low;
u32 temp_high;
u32 temp_max;
u32 allow_high_temp_charge_max_voltage;
u32 charge_voltage_limit_default;
u32 charge_voltage_limit_high_temp;
u32 allow_fast_charge_min_temp;
u32 allow_fast_charge_min_voltage;
u32 fast_charge_current_limit_default;
u32 fast_charge_current_limit_low_temp;
u32 fast_charge_current_limit_low_voltage;
};
static constexpr BatteryChargeParameters 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,
};
/* 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,
};
static const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_version) {
switch (battery_version) {
case 0:
return &BatteryChargeParameters0;
case 1:
return &BatteryChargeParameters1;
case 2:
return &BatteryChargeParameters2;
default:
std::abort();
}
}
constexpr BatteryChargeParameters BatteryChargeParameters1 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 59,
.allow_high_temp_charge_max_voltage = 3984,
.charge_voltage_limit_default = 4208,
.charge_voltage_limit_high_temp = 3984,
.allow_fast_charge_min_voltage = 0,
.fast_charge_current_limit_default = 0x600,
.fast_charge_current_limit_low_temp = 0x240,
.fast_charge_current_limit_low_voltage = 0x600,
};
static void UpdateCharger(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit) {
double temperature;
u32 battery_voltage;
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,
};
if (R_FAILED(battery_driver->GetTemperature(&temperature)) || R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
pmic_driver->ShutdownSystem();
}
bool enable_charge = true;
if (temperature < double(params->temp_min)) {
enable_charge = false;
} else if (double(params->temp_high) <= temperature && temperature < double(params->temp_max)) {
if (battery_voltage < params->allow_high_temp_charge_max_voltage) {
charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp);
} else {
enable_charge = false;
}
} else if (double(params->temp_max) <= temperature) {
enable_charge = false;
if (battery_voltage < params->allow_high_temp_charge_max_voltage) {
charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp);
}
}
u32 fast_charge_current_limit = params->fast_charge_current_limit_default;
if (temperature < double(params->temp_low)) {
fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_temp);
}
if (battery_voltage < params->allow_fast_charge_min_voltage) {
fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_voltage);
}
if (R_FAILED(charger_driver->SetChargeEnabled(enable_charge))) {
pmic_driver->ShutdownSystem();
}
if (R_FAILED(charger_driver->SetChargeVoltageLimit(charge_voltage_limit))) {
pmic_driver->ShutdownSystem();
}
if (R_FAILED(charger_driver->SetFastChargeCurrentLimit(fast_charge_current_limit))) {
pmic_driver->ShutdownSystem();
}
}
static bool IsSufficientBattery(u32 battery_voltage, bool ac_ok) {
/* Nintendo has stuff for updating a static variable every 10 seconds here, but this seems, again, to be debug leftovers. */
const u32 required_voltage = ac_ok ? 4000 : 3650;
return battery_voltage >= required_voltage;
}
static CheckBatteryResult LoopCheckBattery(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit, bool reboot_on_power_button_pressed, bool succeed_on_sufficient_battery, bool shutdown_on_full_battery, bool can_show_battery_icon, bool can_show_charging_icon) {
bool is_showing_charging_icon = false;
ON_SCOPE_EXIT {
if (is_showing_charging_icon) {
Boot::EndShowChargingIcon();
}
};
if (can_show_charging_icon) {
size_t battery_percentage;
if (R_FAILED(battery_driver->GetBatteryPercentage(&battery_percentage))) {
return CheckBatteryResult_Shutdown;
}
Boot::StartShowChargingIcon(battery_percentage, true);
is_showing_charging_icon = true;
}
while (true) {
double battery_charge;
if (R_FAILED(battery_driver->GetSocRep(&battery_charge))) {
return CheckBatteryResult_Shutdown;
}
if (succeed_on_sufficient_battery && battery_charge >= 3.0) {
return CheckBatteryResult_Success;
} else if (shutdown_on_full_battery && battery_charge >= 99.0) {
return CheckBatteryResult_Shutdown;
} else {
/* Nintendo has logic for checking a value every 10 seconds. */
/* They never do anything with this value though, so it's probably just leftovers from debug? */
}
bool ac_ok;
if (R_FAILED(pmic_driver->GetAcOk(&ac_ok))) {
return CheckBatteryResult_Shutdown;
}
u32 battery_voltage;
if (R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
return CheckBatteryResult_Shutdown;
}
if (succeed_on_sufficient_battery && IsSufficientBattery(battery_voltage, ac_ok)) {
return CheckBatteryResult_Success;
}
if (!ac_ok) {
if (can_show_battery_icon && !is_showing_charging_icon) {
Boot::ShowLowBatteryIcon();
}
return CheckBatteryResult_Shutdown;
}
if (reboot_on_power_button_pressed) {
bool power_button_pressed;
if (R_FAILED(pmic_driver->GetPowerButtonPressed(&power_button_pressed))) {
return CheckBatteryResult_Shutdown;
}
if (power_button_pressed) {
return CheckBatteryResult_Reboot;
constexpr const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_version) {
switch (battery_version) {
case 0:
return &BatteryChargeParameters0;
case 1:
return &BatteryChargeParameters1;
case 2:
return &BatteryChargeParameters2;
default:
std::abort();
}
}
if (can_show_battery_icon && !is_showing_charging_icon) {
Boot::StartShowChargingIcon(1, false);
is_showing_charging_icon = true;
/* 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 enable_charge = true;
if (temperature < double(params->temp_min)) {
enable_charge = false;
} else if (double(params->temp_high) <= temperature && temperature < double(params->temp_max)) {
if (battery_voltage < params->allow_high_temp_charge_max_voltage) {
charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp);
} else {
enable_charge = false;
}
} else if (double(params->temp_max) <= temperature) {
enable_charge = false;
if (battery_voltage < params->allow_high_temp_charge_max_voltage) {
charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp);
}
}
u32 fast_charge_current_limit = params->fast_charge_current_limit_default;
if (temperature < double(params->temp_low)) {
fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_temp);
}
if (battery_voltage < params->allow_fast_charge_min_voltage) {
fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_voltage);
}
if (R_FAILED(charger_driver->SetChargeEnabled(enable_charge))) {
pmic_driver->ShutdownSystem();
}
if (R_FAILED(charger_driver->SetChargeVoltageLimit(charge_voltage_limit))) {
pmic_driver->ShutdownSystem();
}
if (R_FAILED(charger_driver->SetFastChargeCurrentLimit(fast_charge_current_limit))) {
pmic_driver->ShutdownSystem();
}
}
svcSleepThread(20'000'000ul);
UpdateCharger(pmic_driver, charger_driver, battery_driver, params, charge_voltage_limit);
}
}
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;
}
void Boot::CheckBatteryCharge() {
PmicDriver pmic_driver;
BatteryDriver battery_driver;
ChargerDriver charger_driver;
CheckBatteryResult LoopCheckBattery(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit, bool reboot_on_power_button_pressed, bool succeed_on_sufficient_battery, bool shutdown_on_full_battery, bool can_show_battery_icon, bool can_show_charging_icon) {
bool is_showing_charging_icon = false;
ON_SCOPE_EXIT {
if (is_showing_charging_icon) {
EndShowChargingIcon();
}
};
if (R_FAILED(battery_driver.InitializeBatteryParameters())) {
pmic_driver.ShutdownSystem();
if (can_show_charging_icon) {
size_t battery_percentage;
if (R_FAILED(battery_driver->GetBatteryPercentage(&battery_percentage))) {
return CheckBatteryResult::Shutdown;
}
StartShowChargingIcon(battery_percentage, true);
is_showing_charging_icon = true;
}
while (true) {
double battery_charge;
if (R_FAILED(battery_driver->GetSocRep(&battery_charge))) {
return CheckBatteryResult::Shutdown;
}
if (succeed_on_sufficient_battery && battery_charge >= 3.0) {
return CheckBatteryResult::Success;
} else if (shutdown_on_full_battery && battery_charge >= 99.0) {
return CheckBatteryResult::Shutdown;
} else {
/* Nintendo has logic for checking a value every 10 seconds. */
/* They never do anything with this value though, so it's probably just leftovers from debug? */
}
bool ac_ok;
if (R_FAILED(pmic_driver->GetAcOk(&ac_ok))) {
return CheckBatteryResult::Shutdown;
}
u32 battery_voltage;
if (R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
return CheckBatteryResult::Shutdown;
}
if (succeed_on_sufficient_battery && IsSufficientBattery(battery_voltage, ac_ok)) {
return CheckBatteryResult::Success;
}
if (!ac_ok) {
if (can_show_battery_icon && !is_showing_charging_icon) {
ShowLowBatteryIcon();
}
return CheckBatteryResult::Shutdown;
}
if (reboot_on_power_button_pressed) {
bool power_button_pressed;
if (R_FAILED(pmic_driver->GetPowerButtonPressed(&power_button_pressed))) {
return CheckBatteryResult::Shutdown;
}
if (power_button_pressed) {
return CheckBatteryResult::Reboot;
}
}
if (can_show_battery_icon && !is_showing_charging_icon) {
StartShowChargingIcon(1, false);
is_showing_charging_icon = true;
}
svcSleepThread(20'000'000ul);
UpdateCharger(pmic_driver, charger_driver, battery_driver, params, charge_voltage_limit);
}
}
}
{
bool removed;
if (R_FAILED(battery_driver.IsBatteryRemoved(&removed)) || removed) {
void CheckBatteryCharge() {
PmicDriver pmic_driver;
BatteryDriver battery_driver;
ChargerDriver charger_driver;
if (R_FAILED(battery_driver.InitializeBatteryParameters())) {
pmic_driver.ShutdownSystem();
}
}
{
bool removed;
if (R_FAILED(battery_driver.IsBatteryRemoved(&removed)) || removed) {
pmic_driver.ShutdownSystem();
}
}
const u32 boot_reason = Boot::GetBootReason();
InputCurrentLimit input_current_limit;
if (R_FAILED(charger_driver.Initialize(boot_reason != 4)) || R_FAILED(charger_driver.GetInputCurrentLimit(&input_current_limit))) {
pmic_driver.ShutdownSystem();
}
if (input_current_limit <= InputCurrentLimit_150mA) {
charger_driver.SetChargerConfiguration(ChargerConfiguration_ChargeDisable);
pmic_driver.ShutdownSystem();
}
const BatteryChargeParameters *params = GetBatteryChargeParameters(Boot::GetBatteryVersion());
u32 charge_voltage_limit = params->charge_voltage_limit_default;
CheckBatteryResult check_result;
if (boot_reason == 4) {
if (R_FAILED(charger_driver.GetChargeVoltageLimit(&charge_voltage_limit))) {
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();
}
UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit);
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, false, true, false, false);
} else {
UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit);
if (boot_reason == 1) {
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, true, false, true, true);
if (input_current_limit <= bq24193::InputCurrentLimit_150mA) {
charger_driver.SetChargerConfiguration(bq24193::ChargerConfiguration_ChargeDisable);
pmic_driver.ShutdownSystem();
}
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();
}
UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit);
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, false, true, false, false);
} else {
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, false, true, false, true, false);
UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit);
if (boot_reason == 1) {
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, true, false, true, true);
} else {
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, false, true, false, true, false);
}
}
switch (check_result) {
case CheckBatteryResult::Success:
break;
case CheckBatteryResult::Shutdown:
pmic_driver.ShutdownSystem();
break;
case CheckBatteryResult::Reboot:
RebootSystem();
break;
default:
std::abort();
}
}
switch (check_result) {
case CheckBatteryResult_Success:
break;
case CheckBatteryResult_Shutdown:
pmic_driver.ShutdownSystem();
break;
case CheckBatteryResult_Reboot:
Boot::RebootSystem();
break;
default:
std::abort();
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
void CheckBatteryCharge();
}

View file

@ -14,31 +14,43 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include <stratosphere/reg.hpp>
#include "boot_check_clock.hpp"
#include "boot_power_utils.hpp"
static constexpr u32 ExpectedPlluDivP = (1 << 16);
static constexpr u32 ExpectedPlluDivN = (25 << 8);
static constexpr u32 ExpectedPlluDivM = (2 << 0);
static constexpr u32 ExpectedPlluVal = (ExpectedPlluDivP | ExpectedPlluDivN | ExpectedPlluDivM);
static constexpr u32 ExpectedPlluMask = 0x1FFFFF;
namespace sts::boot {
static constexpr u32 ExpectedUtmipDivN = (25 << 16);
static constexpr u32 ExpectedUtmipDivM = (1 << 8);
static constexpr u32 ExpectedUtmipVal = (ExpectedUtmipDivN | ExpectedUtmipDivM);
static constexpr u32 ExpectedUtmipMask = 0xFFFF00;
namespace {
static bool IsUsbClockValid() {
volatile u32 *car_regs = reinterpret_cast<volatile u32 *>(GetIoMapping(0x60006000ul, 0x1000));
/* Convenience definitions. */
constexpr u32 ExpectedPlluDivP = (1 << 16);
constexpr u32 ExpectedPlluDivN = (25 << 8);
constexpr u32 ExpectedPlluDivM = (2 << 0);
constexpr u32 ExpectedPlluVal = (ExpectedPlluDivP | ExpectedPlluDivN | ExpectedPlluDivM);
constexpr u32 ExpectedPlluMask = 0x1FFFFF;
const u32 pllu = car_regs[0xC0 >> 2];
const u32 utmip = car_regs[0x480 >> 2];
return ((pllu & ExpectedPlluMask) == ExpectedPlluVal) && ((utmip & ExpectedUtmipMask) == ExpectedUtmipVal);
}
constexpr u32 ExpectedUtmipDivN = (25 << 16);
constexpr u32 ExpectedUtmipDivM = (1 << 8);
constexpr u32 ExpectedUtmipVal = (ExpectedUtmipDivN | ExpectedUtmipDivM);
constexpr u32 ExpectedUtmipMask = 0xFFFF00;
/* Helpers. */
bool IsUsbClockValid() {
uintptr_t car_regs = GetIoMapping(0x60006000ul, 0x1000);
const u32 pllu = reg::Read(car_regs + 0xC0);
const u32 utmip = reg::Read(car_regs + 0x480);
return ((pllu & ExpectedPlluMask) == ExpectedPlluVal) && ((utmip & ExpectedUtmipMask) == ExpectedUtmipVal);
}
void Boot::CheckClock() {
if (!IsUsbClockValid()) {
/* Sleep for 1s, then reboot. */
svcSleepThread(1'000'000'000ul);
Boot::RebootSystem();
}
void CheckClock() {
if (!IsUsbClockValid()) {
/* Sleep for 1s, then reboot. */
svcSleepThread(1'000'000'000ul);
RebootSystem();
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
void CheckClock();
}

View file

@ -14,15 +14,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_clock_initial_configuration.hpp"
#include "boot_pmc_wrapper.hpp"
#include "boot_registers_pmc.hpp"
static constexpr u32 PmcClkOutCntrl = PmcBase + APBDEV_PMC_CLK_OUT_CNTRL;
static constexpr u32 InitialClockOutMask1x = 0x00C4;
static constexpr u32 InitialClockOutMask6x = 0xC4C4;
namespace sts::boot {
namespace {
/* Convenience definitions. */
constexpr u32 PmcClkOutCntrl = PmcBase + APBDEV_PMC_CLK_OUT_CNTRL;
constexpr u32 InitialClockOutMask1x = 0x00C4;
constexpr u32 InitialClockOutMask6x = 0xC4C4;
}
void SetInitialClockConfiguration() {
/* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */
const u32 mask = GetRuntimeFirmwareVersion() >= FirmwareVersion_600 ? InitialClockOutMask6x : InitialClockOutMask1x;
WritePmcRegister(PmcClkOutCntrl, mask, mask);
}
void Boot::SetInitialClockConfiguration() {
/* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */
const u32 mask = GetRuntimeFirmwareVersion() >= FirmwareVersion_600 ? InitialClockOutMask6x : InitialClockOutMask1x;
WritePmcRegister(PmcClkOutCntrl, mask, mask);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
void SetInitialClockConfiguration();
}

View file

@ -14,513 +14,497 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_display_config.hpp"
#include "i2c/driver/i2c_api.hpp"
#include <stratosphere/reg.hpp>
/* Helpful defines. */
constexpr size_t DeviceAddressSpaceAlignSize = 0x400000;
constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1;
constexpr uintptr_t FrameBufferPaddr = DisplayConfigFrameBufferAddress;
constexpr size_t FrameBufferWidth = 768;
constexpr size_t FrameBufferHeight = 1280;
constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32);
#include "boot_display.hpp"
#include "boot_i2c_utils.hpp"
#include "boot_pmc_wrapper.hpp"
#include "boot_spl_utils.hpp"
constexpr uintptr_t Disp1Base = 0x54200000ul;
constexpr uintptr_t DsiBase = 0x54300000ul;
constexpr uintptr_t ClkRstBase = 0x60006000ul;
constexpr uintptr_t GpioBase = 0x6000D000ul;
constexpr uintptr_t ApbMiscBase = 0x70000000ul;
constexpr uintptr_t MipiCalBase = 0x700E3000ul;
constexpr size_t Disp1Size = 0x3000;
constexpr size_t DsiSize = 0x1000;
constexpr size_t ClkRstSize = 0x1000;
constexpr size_t GpioSize = 0x1000;
constexpr size_t ApbMiscSize = 0x1000;
constexpr size_t MipiCalSize = 0x1000;
#include "boot_registers_clkrst.hpp"
#include "boot_registers_di.hpp"
#include "boot_registers_gpio.hpp"
#include "boot_registers_pinmux.hpp"
#include "boot_registers_pmc.hpp"
/* Types. */
namespace sts::boot {
/* Globals. */
static bool g_is_display_intialized = false;
static u32 *g_frame_buffer = nullptr;
static bool g_is_mariko = false;
static u32 g_lcd_vendor = 0;
static Handle g_dc_das_hnd = INVALID_HANDLE;
static u8 g_frame_buffer_storage[DeviceAddressSpaceAlignSize + FrameBufferSize];
/* Display configuration included into anonymous namespace. */
namespace {
static uintptr_t g_disp1_regs = 0;
static uintptr_t g_dsi_regs = 0;
static uintptr_t g_clk_rst_regs = 0;
static uintptr_t g_gpio_regs = 0;
static uintptr_t g_apb_misc_regs = 0;
static uintptr_t g_mipi_cal_regs = 0;
#include "boot_display_config.inc"
static inline void WriteRegister(volatile u32 *reg, u32 val) {
*reg = val;
}
static inline void WriteRegister(uintptr_t reg, u32 val) {
WriteRegister(reinterpret_cast<volatile u32 *>(reg), val);
}
static inline u32 ReadRegister(volatile u32 *reg) {
u32 val = *reg;
return val;
}
static inline u32 ReadRegister(uintptr_t reg) {
return ReadRegister(reinterpret_cast<volatile u32 *>(reg));
}
static inline void SetRegisterBits(volatile u32 *reg, u32 mask) {
*reg |= mask;
}
static inline void SetRegisterBits(uintptr_t reg, u32 mask) {
SetRegisterBits(reinterpret_cast<volatile u32 *>(reg), mask);
}
static inline void ClearRegisterBits(volatile u32 *reg, u32 mask) {
*reg &= mask;
}
static inline void ClearRegisterBits(uintptr_t reg, u32 mask) {
ClearRegisterBits(reinterpret_cast<volatile u32 *>(reg), mask);
}
static inline void ReadWriteRegisterBits(volatile u32 *reg, u32 val, u32 mask) {
*reg = (*reg & (~mask)) | (val & mask);
}
static inline void ReadWriteRegisterBits(uintptr_t reg, u32 val, u32 mask) {
ReadWriteRegisterBits(reinterpret_cast<volatile u32 *>(reg), val, mask);
}
static void InitializeRegisterBaseAddresses() {
g_disp1_regs = GetIoMapping(Disp1Base, Disp1Size);
g_dsi_regs = GetIoMapping(DsiBase, DsiSize);
g_clk_rst_regs = GetIoMapping(ClkRstBase, ClkRstSize);
g_gpio_regs = GetIoMapping(GpioBase, GpioSize);
g_apb_misc_regs = GetIoMapping(ApbMiscBase, ApbMiscSize);
g_mipi_cal_regs = GetIoMapping(MipiCalBase, MipiCalSize);
}
static inline void DoRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes, size_t num_writes) {
for (size_t i = 0; i < num_writes; i++) {
*(reinterpret_cast<volatile u32 *>(base_address + reg_writes[i].offset)) = reg_writes[i].value;
}
}
static inline void DoSocDependentRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes_erista, size_t num_writes_erista, const RegisterWrite *reg_writes_mariko, size_t num_writes_mariko) {
if (g_is_mariko) {
DoRegisterWrites(base_address, reg_writes_mariko, num_writes_mariko);
} else {
DoRegisterWrites(base_address, reg_writes_erista, num_writes_erista);
}
}
namespace {
static inline void DoDsiSleepOrRegisterWrites(const DsiSleepOrRegisterWrite *reg_writes, size_t num_writes) {
for (size_t i = 0; i < num_writes; i++) {
if (reg_writes[i].kind == DsiSleepOrRegisterWriteKind_Write) {
*(reinterpret_cast<volatile u32 *>(g_dsi_regs + sizeof(u32) * reg_writes[i].offset)) = reg_writes[i].value;
} else if (reg_writes[i].kind == DsiSleepOrRegisterWriteKind_Sleep) {
svcSleepThread(1'000'000ul * u64(reg_writes[i].offset));
} else {
std::abort();
/* Helpful defines. */
constexpr size_t DeviceAddressSpaceAlignSize = 0x400000;
constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1;
constexpr uintptr_t FrameBufferPaddr = DisplayConfigFrameBufferAddress;
constexpr size_t FrameBufferWidth = 768;
constexpr size_t FrameBufferHeight = 1280;
constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32);
constexpr uintptr_t Disp1Base = 0x54200000ul;
constexpr uintptr_t DsiBase = 0x54300000ul;
constexpr uintptr_t ClkRstBase = 0x60006000ul;
constexpr uintptr_t GpioBase = 0x6000D000ul;
constexpr uintptr_t ApbMiscBase = 0x70000000ul;
constexpr uintptr_t MipiCalBase = 0x700E3000ul;
constexpr size_t Disp1Size = 0x3000;
constexpr size_t DsiSize = 0x1000;
constexpr size_t ClkRstSize = 0x1000;
constexpr size_t GpioSize = 0x1000;
constexpr size_t ApbMiscSize = 0x1000;
constexpr size_t MipiCalSize = 0x1000;
/* Types. */
/* Globals. */
bool g_is_display_intialized = false;
u32 *g_frame_buffer = nullptr;
bool g_is_mariko = false;
u32 g_lcd_vendor = 0;
Handle g_dc_das_hnd = INVALID_HANDLE;
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;
/* Helper functions. */
void InitializeRegisterBaseAddresses() {
g_disp1_regs = GetIoMapping(Disp1Base, Disp1Size);
g_dsi_regs = GetIoMapping(DsiBase, DsiSize);
g_clk_rst_regs = GetIoMapping(ClkRstBase, ClkRstSize);
g_gpio_regs = GetIoMapping(GpioBase, GpioSize);
g_apb_misc_regs = GetIoMapping(ApbMiscBase, ApbMiscSize);
g_mipi_cal_regs = GetIoMapping(MipiCalBase, MipiCalSize);
}
inline void DoRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes, size_t num_writes) {
for (size_t i = 0; i < num_writes; i++) {
reg::Write(base_address + reg_writes[i].offset, reg_writes[i].value);
}
}
inline void DoSocDependentRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes_erista, size_t num_writes_erista, const RegisterWrite *reg_writes_mariko, size_t num_writes_mariko) {
if (g_is_mariko) {
DoRegisterWrites(base_address, reg_writes_mariko, num_writes_mariko);
} else {
DoRegisterWrites(base_address, reg_writes_erista, num_writes_erista);
}
}
inline void DoDsiSleepOrRegisterWrites(const DsiSleepOrRegisterWrite *reg_writes, size_t num_writes) {
for (size_t i = 0; i < num_writes; i++) {
if (reg_writes[i].kind == DsiSleepOrRegisterWriteKind_Write) {
reg::Write(g_dsi_regs + sizeof(u32) * reg_writes[i].offset, reg_writes[i].value);
} else if (reg_writes[i].kind == DsiSleepOrRegisterWriteKind_Sleep) {
svcSleepThread(1'000'000ul * u64(reg_writes[i].offset));
} else {
std::abort();
}
}
}
}
}
#define DO_REGISTER_WRITES(base_address, writes) DoRegisterWrites(base_address, writes, sizeof(writes) / sizeof(writes[0]))
#define DO_SOC_DEPENDENT_REGISTER_WRITES(base_address, writes) DoSocDependentRegisterWrites(base_address, writes##Erista, sizeof(writes##Erista) / sizeof(writes##Erista[0]), writes##Mariko, sizeof(writes##Mariko) / sizeof(writes##Mariko[0]))
#define DO_DSI_SLEEP_OR_REGISTER_WRITES(writes) DoDsiSleepOrRegisterWrites(writes, sizeof(writes) / sizeof(writes[0]))
static void InitializeFrameBuffer() {
if (g_frame_buffer != nullptr) {
std::memset(g_frame_buffer, 0x00, FrameBufferSize);
armDCacheFlush(g_frame_buffer, FrameBufferSize);
} else {
const uintptr_t frame_buffer_aligned = ((reinterpret_cast<uintptr_t>(g_frame_buffer_storage) + DeviceAddressSpaceAlignMask) & ~uintptr_t(DeviceAddressSpaceAlignMask));
g_frame_buffer = reinterpret_cast<u32 *>(frame_buffer_aligned);
std::memset(g_frame_buffer, 0x00, FrameBufferSize);
armDCacheFlush(g_frame_buffer, FrameBufferSize);
void InitializeFrameBuffer() {
if (g_frame_buffer != nullptr) {
std::memset(g_frame_buffer, 0x00, FrameBufferSize);
armDCacheFlush(g_frame_buffer, FrameBufferSize);
} else {
const uintptr_t frame_buffer_aligned = ((reinterpret_cast<uintptr_t>(g_frame_buffer_storage) + DeviceAddressSpaceAlignMask) & ~uintptr_t(DeviceAddressSpaceAlignMask));
g_frame_buffer = reinterpret_cast<u32 *>(frame_buffer_aligned);
std::memset(g_frame_buffer, 0x00, FrameBufferSize);
armDCacheFlush(g_frame_buffer, FrameBufferSize);
constexpr u64 DeviceName_DC = 2;
constexpr u64 DeviceName_DC = 2;
/* Create Address Space. */
if (R_FAILED(svcCreateDeviceAddressSpace(&g_dc_das_hnd, 0, (1ul << 32)))) {
std::abort();
}
/* Attach it to the DC. */
if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd))) {
std::abort();
}
/* Create Address Space. */
if (R_FAILED(svcCreateDeviceAddressSpace(&g_dc_das_hnd, 0, (1ul << 32)))) {
std::abort();
}
/* Attach it to the DC. */
if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd))) {
std::abort();
}
/* Map the framebuffer for the DC as read-only. */
if (R_FAILED(svcMapDeviceAddressSpaceAligned(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr, 1))) {
std::abort();
}
}
}
static void FinalizeFrameBuffer() {
if (g_frame_buffer != nullptr) {
const uintptr_t frame_buffer_aligned = reinterpret_cast<uintptr_t>(g_frame_buffer);
constexpr u64 DeviceName_DC = 2;
/* Unmap the framebuffer from the DC. */
if (R_FAILED(svcUnmapDeviceAddressSpace(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr))) {
std::abort();
}
/* Detach address space from the DC. */
if (R_FAILED(svcDetachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd))) {
std::abort();
}
/* Close the address space. */
if (R_FAILED(svcCloseHandle(g_dc_das_hnd))) {
std::abort();
}
g_dc_das_hnd = INVALID_HANDLE;
g_frame_buffer = nullptr;
}
}
static void WaitDsiTrigger() {
TimeoutHelper timeout_helper(250'000'000ul);
while (true) {
if (timeout_helper.TimedOut()) {
break;
}
if (ReadRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER) == 0) {
break;
}
}
svcSleepThread(5'000'000ul);
}
static void WaitDsiHostControl() {
TimeoutHelper timeout_helper(150'000'000ul);
while (true) {
if (timeout_helper.TimedOut()) {
break;
}
if ((ReadRegister(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL) & DSI_HOST_CONTROL_IMM_BTA) == 0) {
break;
}
}
}
void Boot::InitializeDisplay() {
/* Setup globals. */
InitializeRegisterBaseAddresses();
g_is_mariko = Boot::IsMariko();
InitializeFrameBuffer();
/* Turn on DSI/voltage rail. */
{
sts::i2c::driver::Session i2c_session;
sts::i2c::driver::Initialize();
ON_SCOPE_EXIT { sts::i2c::driver::Finalize(); };
sts::i2c::driver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic);
if (g_is_mariko) {
Boot::WriteI2cRegister(i2c_session, 0x18, 0x3A);
Boot::WriteI2cRegister(i2c_session, 0x1F, 0x71);
}
Boot::WriteI2cRegister(i2c_session, 0x23, 0xD0);
}
/* Enable MIPI CAL, DSI, DISP1, HOST1X, UART_FST_MIPI_CAL, DSIA LP clocks. */
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_CLR, 0x1010000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_SET, 0x1010000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_CLR, 0x18000000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_SET, 0x18000000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_X_SET, 0x20000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL, 0xA);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_W_SET, 0x80000);
WriteRegister(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);
/* Configure LCD pinmux tristate + passthrough. */
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_NFC_EN, ~PINMUX_TRISTATE);
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_NFC_INT, ~PINMUX_TRISTATE);
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, ~PINMUX_TRISTATE);
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_EN, ~PINMUX_TRISTATE);
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_RST, ~PINMUX_TRISTATE);
/* Configure LCD power, VDD. */
SetRegisterBits(g_gpio_regs + GPIO_PORT3_CNF_0, 0x3);
SetRegisterBits(g_gpio_regs + GPIO_PORT3_OE_0, 0x3);
SetRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1);
svcSleepThread(10'000'000ul);
SetRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2);
svcSleepThread(10'000'000ul);
/* Configure LCD backlight. */
SetRegisterBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x7);
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x7);
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x2);
/* Configure display interface and display. */
WriteRegister(g_mipi_cal_regs + 0x060, 0);
if (g_is_mariko) {
WriteRegister(g_mipi_cal_regs + 0x058, 0);
WriteRegister(g_apb_misc_regs + 0xAC0, 0);
}
/* Execute configs. */
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01);
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc01);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init01);
/* NOTE: Nintendo bug here. */
/* As of 8.0.0, Nintendo writes this list to CAR instead of DSI */
/* This results in them zeroing CLK_SOURCE_UARTA... */
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init02);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init03);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init04);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init05);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init06);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init07);
svcSleepThread(10'000'000ul);
/* Enable backlight reset. */
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4);
svcSleepThread(60'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x50204);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x337);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
WaitDsiTrigger();
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x406);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
WaitDsiTrigger();
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_IMM_BTA | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC);
WaitDsiHostControl();
svcSleepThread(5'000'000ul);
/* Parse LCD vendor. */
{
u32 host_response[3];
for (size_t i = 0; i < sizeof(host_response) / sizeof(host_response[0]); i++) {
host_response[i] = ReadRegister(g_dsi_regs + sizeof(u32) * DSI_RD_DATA);
}
if ((host_response[2] & 0xFF) == 0x10) {
g_lcd_vendor = 0;
} else {
g_lcd_vendor = (host_response[2] >> 8) & 0xFF00;
}
g_lcd_vendor = (g_lcd_vendor & 0xFFFFFF00) | (host_response[2] & 0xFF);
}
/* LCD vendor specific configuration. */
switch (g_lcd_vendor) {
case 0xF30: /* TODO: What's this? */
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(180'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
break;
case 0xF20: /* TODO: What's this? */
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(180'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
break;
case 0x10: /* Japan Display Inc screens. */
DO_DSI_SLEEP_OR_REGISTER_WRITES(DisplayConfigJdiSpecificInit01);
break;
default:
if ((g_lcd_vendor | 0x10) == 0x1030) {
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(120'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
/* Map the framebuffer for the DC as read-only. */
if (R_FAILED(svcMapDeviceAddressSpaceAligned(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr, 1))) {
std::abort();
}
}
break;
}
void FinalizeFrameBuffer() {
if (g_frame_buffer != nullptr) {
const uintptr_t frame_buffer_aligned = reinterpret_cast<uintptr_t>(g_frame_buffer);
constexpr u64 DeviceName_DC = 2;
/* Unmap the framebuffer from the DC. */
if (R_FAILED(svcUnmapDeviceAddressSpace(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr))) {
std::abort();
}
/* Detach address space from the DC. */
if (R_FAILED(svcDetachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd))) {
std::abort();
}
/* Close the address space. */
if (R_FAILED(svcCloseHandle(g_dc_das_hnd))) {
std::abort();
}
g_dc_das_hnd = INVALID_HANDLE;
g_frame_buffer = nullptr;
}
}
void WaitDsiTrigger() {
TimeoutHelper timeout_helper(250'000'000ul);
while (true) {
if (timeout_helper.TimedOut()) {
break;
}
if (reg::Read(g_dsi_regs + sizeof(u32) * DSI_TRIGGER) == 0) {
break;
}
}
svcSleepThread(5'000'000ul);
}
void WaitDsiHostControl() {
TimeoutHelper timeout_helper(150'000'000ul);
while (true) {
if (timeout_helper.TimedOut()) {
break;
}
if ((reg::Read(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL) & DSI_HOST_CONTROL_IMM_BTA) == 0) {
break;
}
}
}
}
svcSleepThread(20'000'000ul);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld02);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init08);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init09);
void InitializeDisplay() {
/* Setup globals. */
InitializeRegisterBaseAddresses();
g_is_mariko = IsMariko();
InitializeFrameBuffer();
WriteRegister(g_disp1_regs + sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, SHIFT_CLK_DIVIDER(4));
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init10);
svcSleepThread(10'000'000ul);
/* Turn on DSI/voltage rail. */
{
i2c::driver::Session i2c_session;
i2c::driver::Initialize();
ON_SCOPE_EXIT { i2c::driver::Finalize(); };
/* Configure MIPI CAL. */
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal01);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03);
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04);
if (g_is_mariko) {
/* On Mariko the above configurations are executed twice, for some reason. */
i2c::driver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic);
if (g_is_mariko) {
WriteI2cRegister(i2c_session, 0x18, 0x3A);
WriteI2cRegister(i2c_session, 0x1F, 0x71);
}
WriteI2cRegister(i2c_session, 0x23, 0xD0);
}
/* Enable MIPI CAL, DSI, DISP1, HOST1X, UART_FST_MIPI_CAL, DSIA LP clocks. */
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_CLR, 0x1010000);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_SET, 0x1010000);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_CLR, 0x18000000);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_SET, 0x18000000);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_X_SET, 0x20000);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL, 0xA);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_W_SET, 0x80000);
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);
/* Configure LCD pinmux tristate + passthrough. */
reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_NFC_EN, PINMUX_TRISTATE);
reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_NFC_INT, PINMUX_TRISTATE);
reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, PINMUX_TRISTATE);
reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_EN, PINMUX_TRISTATE);
reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_RST, PINMUX_TRISTATE);
/* Configure LCD power, VDD. */
reg::SetBits(g_gpio_regs + GPIO_PORT3_CNF_0, 0x3);
reg::SetBits(g_gpio_regs + GPIO_PORT3_OE_0, 0x3);
reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1);
svcSleepThread(10'000'000ul);
reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2);
svcSleepThread(10'000'000ul);
/* Configure LCD backlight. */
reg::SetBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x7);
reg::SetBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x7);
reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x2);
/* Configure display interface and display. */
reg::Write(g_mipi_cal_regs + 0x060, 0);
if (g_is_mariko) {
reg::Write(g_mipi_cal_regs + 0x058, 0);
reg::Write(g_apb_misc_regs + 0xAC0, 0);
}
/* Execute configs. */
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01);
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc01);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init01);
/* NOTE: Nintendo bug here. */
/* As of 8.0.0, Nintendo writes this list to CAR instead of DSI */
/* This results in them zeroing CLK_SOURCE_UARTA... */
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init02);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init03);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init04);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init05);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init06);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init07);
svcSleepThread(10'000'000ul);
/* Enable backlight reset. */
reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4);
svcSleepThread(60'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x50204);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x337);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
WaitDsiTrigger();
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x406);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
WaitDsiTrigger();
reg::Write(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_IMM_BTA | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC);
WaitDsiHostControl();
svcSleepThread(5'000'000ul);
/* Parse LCD vendor. */
{
u32 host_response[3];
for (size_t i = 0; i < sizeof(host_response) / sizeof(host_response[0]); i++) {
host_response[i] = reg::Read(g_dsi_regs + sizeof(u32) * DSI_RD_DATA);
}
if ((host_response[2] & 0xFF) == 0x10) {
g_lcd_vendor = 0;
} else {
g_lcd_vendor = (host_response[2] >> 8) & 0xFF00;
}
g_lcd_vendor = (g_lcd_vendor & 0xFFFFFF00) | (host_response[2] & 0xFF);
}
/* LCD vendor specific configuration. */
switch (g_lcd_vendor) {
case 0xF30: /* TODO: What's this? */
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(180'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
break;
case 0xF20: /* TODO: What's this? */
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(180'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
break;
case 0x10: /* Japan Display Inc screens. */
DO_DSI_SLEEP_OR_REGISTER_WRITES(DisplayConfigJdiSpecificInit01);
break;
default:
if ((g_lcd_vendor | 0x10) == 0x1030) {
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(120'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
}
break;
}
svcSleepThread(20'000'000ul);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld02);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init08);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init09);
reg::Write(g_disp1_regs + sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, SHIFT_CLK_DIVIDER(4));
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init10);
svcSleepThread(10'000'000ul);
/* Configure MIPI CAL. */
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal01);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03);
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04);
}
svcSleepThread(10'000'000ul);
if (g_is_mariko) {
/* On Mariko the above configurations are executed twice, for some reason. */
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03);
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04);
}
svcSleepThread(10'000'000ul);
/* Write DISP1, FrameBuffer config. */
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc02);
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigFrameBuffer);
svcSleepThread(35'000'000ul);
g_is_display_intialized = true;
}
void Boot::ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img) {
if (!g_is_display_intialized) {
return;
/* Write DISP1, FrameBuffer config. */
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc02);
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigFrameBuffer);
svcSleepThread(35'000'000ul);
g_is_display_intialized = true;
}
/* Draw the image to the screen. */
std::memset(g_frame_buffer, 0, FrameBufferSize);
{
for (size_t cur_y = 0; cur_y < height; cur_y++) {
for (size_t cur_x = 0; cur_x < width; cur_x++) {
g_frame_buffer[(FrameBufferHeight - (x + cur_x)) * FrameBufferWidth + y + cur_y] = img[cur_y * width + cur_x];
void ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img) {
if (!g_is_display_intialized) {
return;
}
/* Draw the image to the screen. */
std::memset(g_frame_buffer, 0, FrameBufferSize);
{
for (size_t cur_y = 0; cur_y < height; cur_y++) {
for (size_t cur_x = 0; cur_x < width; cur_x++) {
g_frame_buffer[(FrameBufferHeight - (x + cur_x)) * FrameBufferWidth + y + cur_y] = img[cur_y * width + cur_x];
}
}
}
}
armDCacheFlush(g_frame_buffer, FrameBufferSize);
armDCacheFlush(g_frame_buffer, FrameBufferSize);
/* Enable backlight. */
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1);
}
void Boot::FinalizeDisplay() {
if (!g_is_display_intialized) {
return;
/* Enable backlight. */
reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1);
}
/* Disable backlight. */
ClearRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, ~0x1);
WriteRegister(g_disp1_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 1);
WriteRegister(g_disp1_regs + sizeof(u32) * DSI_WR_DATA, 0x2805);
/* Nintendo waits 5 frames before continuing. */
{
const uintptr_t host1x_vaddr = GetIoMapping(0x500030a4, 4);
const u32 start_val = ReadRegister(host1x_vaddr);
while (ReadRegister(host1x_vaddr) < start_val + 5) {
/* spinlock here. */
void FinalizeDisplay() {
if (!g_is_display_intialized) {
return;
}
/* Disable backlight. */
reg::ClearBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1);
reg::Write(g_disp1_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 1);
reg::Write(g_disp1_regs + sizeof(u32) * DSI_WR_DATA, 0x2805);
/* Nintendo waits 5 frames before continuing. */
{
const uintptr_t host1x_vaddr = GetIoMapping(0x500030a4, 4);
const u32 start_val = reg::Read(host1x_vaddr);
while (reg::Read(host1x_vaddr) < start_val + 5) {
/* spinlock here. */
}
}
reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_STATE_ACCESS, (READ_MUX | WRITE_MUX));
reg::Write(g_disp1_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 0);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Fini01);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Fini02);
svcSleepThread(10'000'000ul);
/* Vendor specific shutdown. */
switch (g_lcd_vendor) {
case 0x10: /* Japan Display Inc screens. */
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigJdiSpecificFini01);
break;
case 0xF30: /* TODO: What's this? */
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigF30SpecificFini01);
svcSleepThread(5'000'000ul);
break;
case 0x1020: /* TODO: What's this? */
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71143209);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x115631);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
break;
case 0x1030: /* TODO: What's this? */
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71143209);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x114D31);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
break;
default:
break;
}
svcSleepThread(5'000'000ul);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1005);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(50'000'000ul);
/* Disable backlight RST/Voltage. */
reg::ClearBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4);
svcSleepThread(10'000'000ul);
reg::ClearBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2);
svcSleepThread(10'000'000ul);
reg::ClearBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1);
svcSleepThread(10'000'000ul);
/* Cut clock to DSI. */
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_SET, 0x1010000);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, 0x1010000);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_SET, 0x18000000);
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, 0x18000000);
reg::Write(g_dsi_regs + sizeof(u32) * DSI_PAD_CONTROL_0, (DSI_PAD_CONTROL_VS1_PULLDN_CLK | DSI_PAD_CONTROL_VS1_PULLDN(0xF) | DSI_PAD_CONTROL_VS1_PDIO_CLK | DSI_PAD_CONTROL_VS1_PDIO(0xF)));
reg::Write(g_dsi_regs + sizeof(u32) * DSI_POWER_CONTROL, 0);
/* Final LCD config for PWM */
reg::ClearBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x1);
reg::SetBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, PINMUX_TRISTATE);
reg::ReadWrite(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, 1, 0x3);
/* Unmap framebuffer from DC virtual address space. */
FinalizeFrameBuffer();
g_is_display_intialized = false;
}
WriteRegister(g_disp1_regs + sizeof(u32) * DC_CMD_STATE_ACCESS, (READ_MUX | WRITE_MUX));
WriteRegister(g_disp1_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 0);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Fini01);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Fini02);
svcSleepThread(10'000'000ul);
/* Vendor specific shutdown. */
switch (g_lcd_vendor) {
case 0x10: /* Japan Display Inc screens. */
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigJdiSpecificFini01);
break;
case 0xF30: /* TODO: What's this? */
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigF30SpecificFini01);
svcSleepThread(5'000'000ul);
break;
case 0x1020: /* TODO: What's this? */
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71143209);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x115631);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
break;
case 0x1030: /* TODO: What's this? */
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71143209);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x114D31);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
break;
default:
break;
}
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1005);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(50'000'000ul);
/* Disable backlight RST/Voltage. */
ClearRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, ~0x4);
svcSleepThread(10'000'000ul);
ClearRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, ~0x2);
svcSleepThread(10'000'000ul);
ClearRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, ~0x1);
svcSleepThread(10'000'000ul);
/* Cut clock to DSI. */
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_SET, 0x1010000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, 0x1010000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_SET, 0x18000000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, 0x18000000);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_PAD_CONTROL_0, (DSI_PAD_CONTROL_VS1_PULLDN_CLK | DSI_PAD_CONTROL_VS1_PULLDN(0xF) | DSI_PAD_CONTROL_VS1_PDIO_CLK | DSI_PAD_CONTROL_VS1_PDIO(0xF)));
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_POWER_CONTROL, 0);
/* Final LCD config for PWM */
ClearRegisterBits(g_gpio_regs + GPIO_PORT6_CNF_1, ~0x1);
SetRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, PINMUX_TRISTATE);
ReadWriteRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, 1, 0x3);
/* Unmap framebuffer from DC virtual address space. */
FinalizeFrameBuffer();
g_is_display_intialized = false;
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
/* Splash Screen/Display utilities. */
void InitializeDisplay();
void ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img);
void FinalizeDisplay();
}

View file

@ -14,17 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
#include "boot_registers_clkrst.hpp"
#include "boot_registers_di.hpp"
#include "boot_registers_gpio.hpp"
#include "boot_registers_pinmux.hpp"
#include "boot_registers_pmc.hpp"
struct RegisterWrite {
u32 offset;
u32 value;
@ -41,21 +30,21 @@ struct DsiSleepOrRegisterWrite {
u32 value;
};
static constexpr RegisterWrite DisplayConfigPlld01Erista[] = {
constexpr RegisterWrite DisplayConfigPlld01Erista[] = {
{CLK_RST_CONTROLLER_CLK_SOURCE_DISP1, 0x40000000},
{CLK_RST_CONTROLLER_PLLD_BASE, 0x4830A001},
{CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000020},
{CLK_RST_CONTROLLER_PLLD_MISC, 0x002D0AAA},
};
static constexpr RegisterWrite DisplayConfigPlld01Mariko[] = {
constexpr RegisterWrite DisplayConfigPlld01Mariko[] = {
{CLK_RST_CONTROLLER_CLK_SOURCE_DISP1, 0x40000000},
{CLK_RST_CONTROLLER_PLLD_BASE, 0x4830A001},
{CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000000},
{CLK_RST_CONTROLLER_PLLD_MISC, 0x002DFC00},
};
static constexpr RegisterWrite DisplayConfigDc01[] = {
constexpr RegisterWrite DisplayConfigDc01[] = {
{sizeof(u32) * DC_CMD_STATE_ACCESS, 0},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
@ -158,7 +147,7 @@ static constexpr RegisterWrite DisplayConfigDc01[] = {
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}
};
static constexpr RegisterWrite DisplayConfigDsi01Init01[] = {
constexpr RegisterWrite DisplayConfigDsi01Init01[] = {
{sizeof(u32) * DSI_WR_DATA, 0x0},
{sizeof(u32) * DSI_INT_ENABLE, 0x0},
{sizeof(u32) * DSI_INT_STATUS, 0x0},
@ -169,15 +158,15 @@ static constexpr RegisterWrite DisplayConfigDsi01Init01[] = {
{sizeof(u32) * DSI_INIT_SEQ_DATA_3, 0x0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init02Erista[] = {
constexpr RegisterWrite DisplayConfigDsi01Init02Erista[] = {
{sizeof(u32) * DSI_INIT_SEQ_DATA_15, 0x0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init02Mariko[] = {
constexpr RegisterWrite DisplayConfigDsi01Init02Mariko[] = {
{sizeof(u32) * DSI_INIT_SEQ_DATA_15_MARIKO, 0x0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init03[] = {
constexpr RegisterWrite DisplayConfigDsi01Init03[] = {
{sizeof(u32) * DSI_DCS_CMDS, 0},
{sizeof(u32) * DSI_PKT_SEQ_0_LO, 0},
{sizeof(u32) * DSI_PKT_SEQ_1_LO, 0},
@ -193,11 +182,11 @@ static constexpr RegisterWrite DisplayConfigDsi01Init03[] = {
{sizeof(u32) * DSI_PKT_SEQ_5_HI, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init04Erista[] = {
constexpr RegisterWrite DisplayConfigDsi01Init04Erista[] = {
/* No register writes. */
};
static constexpr RegisterWrite DisplayConfigDsi01Init04Mariko[] = {
constexpr RegisterWrite DisplayConfigDsi01Init04Mariko[] = {
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
{sizeof(u32) * DSI_PAD_CONTROL_2, 0},
{sizeof(u32) * DSI_PAD_CONTROL_3, 0},
@ -207,7 +196,7 @@ static constexpr RegisterWrite DisplayConfigDsi01Init04Mariko[] = {
{sizeof(u32) * DSI_PAD_CONTROL_7_MARIKO, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init05[] = {
constexpr RegisterWrite DisplayConfigDsi01Init05[] = {
{sizeof(u32) * DSI_PAD_CONTROL_CD, 0},
{sizeof(u32) * DSI_SOL_DELAY, 0x18},
{sizeof(u32) * DSI_MAX_THRESHOLD, 0x1E0},
@ -220,7 +209,7 @@ static constexpr RegisterWrite DisplayConfigDsi01Init05[] = {
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init06[] = {
constexpr RegisterWrite DisplayConfigDsi01Init06[] = {
{sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05},
{sizeof(u32) * DSI_PHY_TIMING_2, 0x30109},
{sizeof(u32) * DSI_BTA_TIMING, 0x190A14},
@ -236,7 +225,7 @@ static constexpr RegisterWrite DisplayConfigDsi01Init06[] = {
};
static constexpr RegisterWrite DisplayConfigDsi01Init07[] = {
constexpr RegisterWrite DisplayConfigDsi01Init07[] = {
{sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05},
{sizeof(u32) * DSI_PHY_TIMING_2, 0x30118},
{sizeof(u32) * DSI_BTA_TIMING, 0x190A14},
@ -253,15 +242,15 @@ static constexpr RegisterWrite DisplayConfigDsi01Init07[] = {
{sizeof(u32) * DSI_INIT_SEQ_CONTROL, 0}
};
static constexpr RegisterWrite DisplayConfigDsiPhyTimingErista[] = {
constexpr RegisterWrite DisplayConfigDsiPhyTimingErista[] = {
{sizeof(u32) * DSI_PHY_TIMING_0, 0x6070601},
};
static constexpr RegisterWrite DisplayConfigDsiPhyTimingMariko[] = {
constexpr RegisterWrite DisplayConfigDsiPhyTimingMariko[] = {
{sizeof(u32) * DSI_PHY_TIMING_0, 0x6070603},
};
static constexpr DsiSleepOrRegisterWrite DisplayConfigJdiSpecificInit01[] = {
constexpr DsiSleepOrRegisterWrite DisplayConfigJdiSpecificInit01[] = {
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x9483FFB9},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
@ -312,23 +301,23 @@ static constexpr DsiSleepOrRegisterWrite DisplayConfigJdiSpecificInit01[] = {
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
};
static constexpr RegisterWrite DisplayConfigPlld02Erista[] = {
constexpr RegisterWrite DisplayConfigPlld02Erista[] = {
{CLK_RST_CONTROLLER_PLLD_BASE, 0x4810c001},
{CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000020},
{CLK_RST_CONTROLLER_PLLD_MISC, 0x002D0AAA},
};
static constexpr RegisterWrite DisplayConfigPlld02Mariko[] = {
constexpr RegisterWrite DisplayConfigPlld02Mariko[] = {
{CLK_RST_CONTROLLER_PLLD_BASE, 0x4810c001},
{CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000000},
{CLK_RST_CONTROLLER_PLLD_MISC, 0x002DFC00},
};
static constexpr RegisterWrite DisplayConfigDsi01Init08[] = {
constexpr RegisterWrite DisplayConfigDsi01Init08[] = {
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init09[] = {
constexpr RegisterWrite DisplayConfigDsi01Init09[] = {
{sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05},
{sizeof(u32) * DSI_PHY_TIMING_2, 0x30172},
{sizeof(u32) * DSI_BTA_TIMING, 0x190A14},
@ -350,7 +339,7 @@ static constexpr RegisterWrite DisplayConfigDsi01Init09[] = {
{sizeof(u32) * DSI_HOST_CONTROL, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init10[] = {
constexpr RegisterWrite DisplayConfigDsi01Init10[] = {
{sizeof(u32) * DSI_TRIGGER, 0},
{sizeof(u32) * DSI_CONTROL, 0},
{sizeof(u32) * DSI_SOL_DELAY, 6},
@ -363,14 +352,14 @@ static constexpr RegisterWrite DisplayConfigDsi01Init10[] = {
{sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC}
};
static constexpr RegisterWrite DisplayConfigDsi01Init11Erista[] = {
constexpr RegisterWrite DisplayConfigDsi01Init11Erista[] = {
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
{sizeof(u32) * DSI_PAD_CONTROL_2, 0},
{sizeof(u32) * DSI_PAD_CONTROL_3, DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3)},
{sizeof(u32) * DSI_PAD_CONTROL_4, 0}
};
static constexpr RegisterWrite DisplayConfigDsi01Init11Mariko[] = {
constexpr RegisterWrite DisplayConfigDsi01Init11Mariko[] = {
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
{sizeof(u32) * DSI_PAD_CONTROL_2, 0},
{sizeof(u32) * DSI_PAD_CONTROL_3, 0},
@ -380,24 +369,24 @@ static constexpr RegisterWrite DisplayConfigDsi01Init11Mariko[] = {
{sizeof(u32) * DSI_PAD_CONTROL_7_MARIKO, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal01[] = {
constexpr RegisterWrite DisplayConfigMipiCal01[] = {
{0x60, 0},
{0x08, 0xF3F10000},
{0x58, 1},
{0x60, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal02Erista[] = {
constexpr RegisterWrite DisplayConfigMipiCal02Erista[] = {
{0x60, 0x10010},
{0x5C, 0x300},
};
static constexpr RegisterWrite DisplayConfigMipiCal02Mariko[] = {
constexpr RegisterWrite DisplayConfigMipiCal02Mariko[] = {
{0x60, 0x10010},
{0x5C, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal03Erista[] = {
constexpr RegisterWrite DisplayConfigMipiCal03Erista[] = {
{0x38, 0x200200},
{0x3C, 0x200200},
{0x64, 0x200002},
@ -406,7 +395,7 @@ static constexpr RegisterWrite DisplayConfigMipiCal03Erista[] = {
{0x18, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal03Mariko[] = {
constexpr RegisterWrite DisplayConfigMipiCal03Mariko[] = {
{0x38, 0x200006},
{0x3C, 0x200006},
{0x64, 0x260000},
@ -415,7 +404,7 @@ static constexpr RegisterWrite DisplayConfigMipiCal03Mariko[] = {
{0x18, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal04[] = {
constexpr RegisterWrite DisplayConfigMipiCal04[] = {
{0x1C, 0},
{0x20, 0},
{0x24, 0},
@ -428,7 +417,7 @@ static constexpr RegisterWrite DisplayConfigMipiCal04[] = {
{0x00, 0x2A000001},
};
static constexpr RegisterWrite DisplayConfigDc02[] = {
constexpr RegisterWrite DisplayConfigDc02[] = {
{sizeof(u32) * DC_CMD_STATE_ACCESS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
@ -552,9 +541,9 @@ static constexpr RegisterWrite DisplayConfigDc02[] = {
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0}
};
static constexpr u32 DisplayConfigFrameBufferAddress = 0xC0000000;
constexpr u32 DisplayConfigFrameBufferAddress = 0xC0000000;
static constexpr RegisterWrite DisplayConfigFrameBuffer[] = {
constexpr RegisterWrite DisplayConfigFrameBuffer[] = {
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, //Enable window C.
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, //Enable window B.
@ -589,12 +578,12 @@ static constexpr RegisterWrite DisplayConfigFrameBuffer[] = {
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ} //General activation request; window A activation request.
};
static constexpr RegisterWrite DisplayConfigDsi01Fini01[] = {
constexpr RegisterWrite DisplayConfigDsi01Fini01[] = {
{sizeof(u32) * DSI_POWER_CONTROL, 0},
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Fini02[] = {
constexpr RegisterWrite DisplayConfigDsi01Fini02[] = {
{sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05},
{sizeof(u32) * DSI_PHY_TIMING_2, 0x30109},
{sizeof(u32) * DSI_BTA_TIMING, 0x190A14},
@ -610,7 +599,7 @@ static constexpr RegisterWrite DisplayConfigDsi01Fini02[] = {
{sizeof(u32) * DSI_INIT_SEQ_CONTROL, 0}
};
static constexpr RegisterWrite DisplayConfigJdiSpecificFini01[] = {
constexpr RegisterWrite DisplayConfigJdiSpecificFini01[] = {
{sizeof(u32) * DSI_WR_DATA, 0x439},
{sizeof(u32) * DSI_WR_DATA, 0x9483FFB9},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
@ -635,7 +624,7 @@ static constexpr RegisterWrite DisplayConfigJdiSpecificFini01[] = {
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
};
static constexpr RegisterWrite DisplayConfigF30SpecificFini01[] = {
constexpr RegisterWrite DisplayConfigF30SpecificFini01[] = {
{sizeof(u32) * DSI_WR_DATA, 0x439},
{sizeof(u32) * DSI_WR_DATA, 0x9483FFB9},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},

View file

@ -14,14 +14,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_fan_enable.hpp"
#include "boot_gpio_utils.hpp"
#include "boot_spl_utils.hpp"
static constexpr u32 GpioPadName_FanEnable = 0x4B;
namespace sts::boot {
namespace {
/* Convenience definitions. */
constexpr u32 GpioPadName_FanEnable = 0x4B;
void Boot::SetFanEnabled() {
if (Boot::GetHardwareType() == HardwareType_Copper) {
Boot::GpioConfigure(GpioPadName_FanEnable);
Boot::GpioSetDirection(GpioPadName_FanEnable, GpioDirection_Output);
Boot::GpioSetValue(GpioPadName_FanEnable, GpioValue_High);
}
void SetFanEnabled() {
if (GetHardwareType() == spl::HardwareType::Copper) {
gpio::Configure(GpioPadName_FanEnable);
gpio::SetDirection(GpioPadName_FanEnable, GpioDirection_Output);
gpio::SetValue(GpioPadName_FanEnable, GpioValue_High);
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
void SetFanEnabled();
}

View file

@ -1,93 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "boot_types.hpp"
#include "i2c/i2c_types.hpp"
#include "i2c/driver/i2c_api.hpp"
class Boot {
public:
static constexpr u32 GpioPhysicalBase = 0x6000D000;
static constexpr u32 ApbMiscPhysicalBase = 0x70000000;
public:
/* Functions for actually booting. */
static void ChangeGpioVoltageTo1_8v();
static void SetInitialGpioConfiguration();
static void CheckClock();
static void DetectBootReason();
static void ShowSplashScreen();
static void CheckBatteryCharge();
static void SetInitialClockConfiguration();
static void ConfigurePinmux();
static void SetInitialWakePinConfiguration();
static void SetFanEnabled();
static void CheckAndRepairBootImages();
/* Power utilities. */
static void RebootSystem();
static void ShutdownSystem();
/* Register Utilities. */
static u32 ReadPmcRegister(u32 phys_addr);
static void WritePmcRegister(u32 phys_addr, u32 value, u32 mask = UINT32_MAX);
/* GPIO Utilities. */
static u32 GpioConfigure(u32 gpio_pad_name);
static u32 GpioSetDirection(u32 gpio_pad_name, GpioDirection dir);
static u32 GpioSetValue(u32 gpio_pad_name, GpioValue val);
/* Pinmux Utilities. */
static u32 PinmuxUpdatePark(u32 pinmux_name);
static u32 PinmuxUpdatePad(u32 pinmux_name, u32 config_val, u32 config_mask);
static u32 PinmuxUpdateDrivePad(u32 pinmux_drivepad_name, u32 config_val, u32 config_mask);
static u32 PinmuxDummyReadDrivePad(u32 pinmux_drivepad_name);
static void ConfigurePinmuxInitialPads();
static void ConfigurePinmuxInitialDrivePads();
/* SPL Utilities. */
static HardwareType GetHardwareType();
static u32 GetBootReason();
static bool IsRecoveryBoot();
static bool IsMariko();
/* I2C Utilities. */
static Result ReadI2cRegister(sts::i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size);
static Result WriteI2cRegister(sts::i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size);
static Result WriteI2cRegister(sts::i2c::driver::Session &session, const u8 address, const u8 value);
/* Splash Screen/Display utilities. */
static void InitializeDisplay();
static void ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img);
static void FinalizeDisplay();
/* Battery Display utilities. */
static void ShowLowBatteryIcon();
static void StartShowChargingIcon(size_t battery_percentage, bool wait);
static void EndShowChargingIcon();
/* Calibration utilities. */
static u16 GetCrc16(const void *data, size_t size);
static u32 GetBatteryVersion();
static u32 GetBatteryVendor();
/* Wake pin utiliies. */
static void SetWakeEventLevel(u32 index, u32 level);
static void SetWakeEventEnabled(u32 index, bool enabled);
};

View file

@ -14,69 +14,88 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_gpio_initial_configuration_icosa.hpp"
#include "boot_gpio_initial_configuration_copper.hpp"
#include "boot_gpio_initial_configuration_hoag.hpp"
#include "boot_gpio_initial_configuration_iowa.hpp"
#include "boot_gpio_initial_configuration.hpp"
#include "boot_gpio_utils.hpp"
#include "boot_spl_utils.hpp"
void Boot::SetInitialGpioConfiguration() {
const GpioInitialConfig *configs = nullptr;
size_t num_configs = 0;
const HardwareType hw_type = Boot::GetHardwareType();
const FirmwareVersion fw_ver = GetRuntimeFirmwareVersion();
namespace sts::boot::gpio {
/* Choose GPIO map. */
if (fw_ver >= FirmwareVersion_200) {
switch (hw_type) {
case HardwareType_Icosa:
{
if (fw_ver >= FirmwareVersion_400) {
configs = GpioInitialConfigsIcosa4x;
num_configs = GpioNumInitialConfigsIcosa4x;
} else {
configs = GpioInitialConfigsIcosa;
num_configs = GpioNumInitialConfigsIcosa;
namespace {
struct InitialConfig {
u32 pad_name;
GpioDirection direction;
GpioValue value;
};
/* Include all initial configuration definitions. */
#include "boot_gpio_initial_configuration_icosa.inc"
#include "boot_gpio_initial_configuration_copper.inc"
#include "boot_gpio_initial_configuration_hoag.inc"
#include "boot_gpio_initial_configuration_iowa.inc"
}
void SetInitialConfiguration() {
const InitialConfig *configs = nullptr;
size_t num_configs = 0;
const auto hw_type = GetHardwareType();
const FirmwareVersion fw_ver = GetRuntimeFirmwareVersion();
/* Choose GPIO map. */
if (fw_ver >= FirmwareVersion_200) {
switch (hw_type) {
case spl::HardwareType::Icosa:
{
if (fw_ver >= FirmwareVersion_400) {
configs = InitialConfigsIcosa4x;
num_configs = NumInitialConfigsIcosa4x;
} else {
configs = InitialConfigsIcosa;
num_configs = NumInitialConfigsIcosa;
}
}
}
break;
case HardwareType_Copper:
configs = GpioInitialConfigsCopper;
num_configs = GpioNumInitialConfigsCopper;
break;
case HardwareType_Hoag:
configs = GpioInitialConfigsHoag;
num_configs = GpioNumInitialConfigsHoag;
break;
case HardwareType_Iowa:
configs = GpioInitialConfigsIowa;
num_configs = GpioNumInitialConfigsIowa;
break;
default:
/* Unknown hardware type, we can't proceed. */
std::abort();
break;
case spl::HardwareType::Copper:
configs = InitialConfigsCopper;
num_configs = NumInitialConfigsCopper;
break;
case spl::HardwareType::Hoag:
configs = InitialConfigsHoag;
num_configs = NumInitialConfigsHoag;
break;
case spl::HardwareType::Iowa:
configs = InitialConfigsIowa;
num_configs = NumInitialConfigsIowa;
break;
default:
/* Unknown hardware type, we can't proceed. */
std::abort();
}
} else {
/* Until 2.0.0, the GPIO map for Icosa was used for all hardware types. */
configs = InitialConfigsIcosa;
num_configs = NumInitialConfigsIcosa;
}
} else {
/* Until 2.0.0, the GPIO map for Icosa was used for all hardware types. */
configs = GpioInitialConfigsIcosa;
num_configs = GpioNumInitialConfigsIcosa;
}
/* Ensure we found an appropriate config. */
if (configs == nullptr) {
std::abort();
}
/* Ensure we found an appropriate config. */
if (configs == nullptr) {
std::abort();
}
for (size_t i = 0; i < num_configs; i++) {
/* Configure the GPIO. */
Boot::GpioConfigure(configs[i].pad_name);
for (size_t i = 0; i < num_configs; i++) {
/* Configure the GPIO. */
Configure(configs[i].pad_name);
/* Set the GPIO's direction. */
Boot::GpioSetDirection(configs[i].pad_name, configs[i].direction);
/* Set the GPIO's direction. */
SetDirection(configs[i].pad_name, configs[i].direction);
if (configs[i].direction == GpioDirection_Output) {
/* Set the GPIO's value. */
Boot::GpioSetValue(configs[i].pad_name, configs[i].value);
if (configs[i].direction == GpioDirection_Output) {
/* Set the GPIO's value. */
SetValue(configs[i].pad_name, configs[i].value);
}
}
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot::gpio {
void SetInitialConfiguration();
}

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr GpioInitialConfig GpioInitialConfigsCopper[] = {
constexpr InitialConfig InitialConfigsCopper[] = {
{0x40, GpioDirection_Output, GpioValue_Low},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x41, GpioDirection_Input, GpioValue_High},
@ -66,4 +61,4 @@ static constexpr GpioInitialConfig GpioInitialConfigsCopper[] = {
{0x4E, GpioDirection_Input, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsCopper = (sizeof(GpioInitialConfigsCopper) / sizeof(GpioInitialConfigsCopper[0]));
constexpr u32 NumInitialConfigsCopper = (sizeof(InitialConfigsCopper) / sizeof(InitialConfigsCopper[0]));

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr GpioInitialConfig GpioInitialConfigsIowa[] = {
constexpr InitialConfig InitialConfigsHoag[] = {
{0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x06, GpioDirection_Input, GpioValue_Low},
@ -78,7 +73,6 @@ static constexpr GpioInitialConfig GpioInitialConfigsIowa[] = {
{0x35, GpioDirection_Input, GpioValue_High},
{0x2C, GpioDirection_Output, GpioValue_Low},
{0x36, GpioDirection_Output, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsIowa = (sizeof(GpioInitialConfigsIowa) / sizeof(GpioInitialConfigsIowa[0]));
constexpr u32 NumInitialConfigsHoag = (sizeof(InitialConfigsHoag) / sizeof(InitialConfigsHoag[0]));

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr GpioInitialConfig GpioInitialConfigsIcosa[] = {
constexpr InitialConfig InitialConfigsIcosa[] = {
{0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x06, GpioDirection_Input, GpioValue_Low},
@ -82,9 +77,9 @@ static constexpr GpioInitialConfig GpioInitialConfigsIcosa[] = {
{0x36, GpioDirection_Output, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsIcosa = (sizeof(GpioInitialConfigsIcosa) / sizeof(GpioInitialConfigsIcosa[0]));
constexpr u32 NumInitialConfigsIcosa = (sizeof(InitialConfigsIcosa) / sizeof(InitialConfigsIcosa[0]));
static constexpr GpioInitialConfig GpioInitialConfigsIcosa4x[] = {
constexpr InitialConfig InitialConfigsIcosa4x[] = {
{0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x06, GpioDirection_Input, GpioValue_Low},
@ -147,4 +142,4 @@ static constexpr GpioInitialConfig GpioInitialConfigsIcosa4x[] = {
{0x36, GpioDirection_Output, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsIcosa4x = (sizeof(GpioInitialConfigsIcosa4x) / sizeof(GpioInitialConfigsIcosa4x[0]));
constexpr u32 NumInitialConfigsIcosa4x = (sizeof(InitialConfigsIcosa4x) / sizeof(InitialConfigsIcosa4x[0]));

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr GpioInitialConfig GpioInitialConfigsHoag[] = {
constexpr InitialConfig InitialConfigsIowa[] = {
{0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x06, GpioDirection_Input, GpioValue_Low},
@ -78,7 +73,6 @@ static constexpr GpioInitialConfig GpioInitialConfigsHoag[] = {
{0x35, GpioDirection_Input, GpioValue_High},
{0x2C, GpioDirection_Output, GpioValue_Low},
{0x36, GpioDirection_Output, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsHoag = (sizeof(GpioInitialConfigsHoag) / sizeof(GpioInitialConfigsHoag[0]));
constexpr u32 NumInitialConfigsIowa = (sizeof(InitialConfigsIowa) / sizeof(InitialConfigsIowa[0]));

View file

@ -14,13 +14,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
constexpr u32 InvalidPadName = UINT32_MAX;
static constexpr u32 GpioInvalid = UINT32_MAX;
static constexpr u32 GpioMap[] = {
GpioInvalid, /* Invalid */
constexpr u32 Map[] = {
InvalidPadName, /* Invalid */
0x000000CC, /* Port Z, Pin 4 */
0x00000024, /* Port E, Pin 4 */
0x0000003C, /* Port H, Pin 4 */
@ -83,7 +80,7 @@ static constexpr u32 GpioMap[] = {
0x00000026, /* Port E, Pin 6 */
/* Copper only */
GpioInvalid, /* Invalid */
InvalidPadName, /* Invalid */
0x00000033, /* Port G, Pin 3 */
0x0000001C, /* Port D, Pin 4 */
0x000000D9, /* Port BB, Pin 1 */
@ -108,4 +105,4 @@ static constexpr u32 GpioMap[] = {
0x00000056, /* Port K, Pin 6 */
};
static constexpr u32 GpioPadNameMax = (sizeof(GpioMap) / sizeof(GpioMap[0]));
static constexpr u32 PadNameMax = (sizeof(Map) / sizeof(Map[0]));

View file

@ -14,102 +14,119 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_gpio_map.hpp"
#include <stratosphere/reg.hpp>
#include "boot_gpio_utils.hpp"
namespace sts::boot::gpio {
namespace {
/* Pull in GPIO map definitions. */
#include "boot_gpio_map.inc"
constexpr u32 PhysicalBase = 0x6000D000;
/* Globals. */
bool g_initialized_gpio_vaddr = false;
uintptr_t g_gpio_vaddr = 0;
/* Helpers. */
inline u32 GetPadDescriptor(u32 gpio_pad_name) {
if (gpio_pad_name >= PadNameMax) {
std::abort();
}
return Map[gpio_pad_name];
}
uintptr_t GetBaseAddress() {
if (!g_initialized_gpio_vaddr) {
g_gpio_vaddr = GetIoMapping(PhysicalBase, 0x1000);
g_initialized_gpio_vaddr = true;
}
return g_gpio_vaddr;
}
static bool g_initialized_gpio_vaddr = false;
static uintptr_t g_gpio_vaddr = 0;
static inline u32 GetGpioPadDescriptor(u32 gpio_pad_name) {
if (gpio_pad_name >= GpioPadNameMax) {
std::abort();
}
return GpioMap[gpio_pad_name];
}
u32 Configure(u32 gpio_pad_name) {
uintptr_t gpio_base_vaddr = GetBaseAddress();
static uintptr_t GetGpioBaseAddress() {
if (!g_initialized_gpio_vaddr) {
g_gpio_vaddr = GetIoMapping(Boot::GpioPhysicalBase, 0x1000);
g_initialized_gpio_vaddr = true;
}
return g_gpio_vaddr;
}
/* Fetch this GPIO's pad descriptor */
const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name);
u32 Boot::GpioConfigure(u32 gpio_pad_name) {
uintptr_t gpio_base_vaddr = GetGpioBaseAddress();
/* Discard invalid GPIOs */
if (gpio_pad_desc == InvalidPadName) {
return InvalidPadName;
}
/* Fetch this GPIO's pad descriptor */
const u32 gpio_pad_desc = GetGpioPadDescriptor(gpio_pad_name);
/* Convert the GPIO pad descriptor into its register offset */
u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C));
/* Discard invalid GPIOs */
if (gpio_pad_desc == GpioInvalid) {
return GpioInvalid;
/* Extract the bit and lock values from the GPIO pad descriptor */
u32 gpio_cnf_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (0x01 << (gpio_pad_desc & 0x07)));
/* Write to the appropriate GPIO_CNF_x register (upper offset) */
reg::Write(gpio_base_vaddr + gpio_reg_offset + 0x80, gpio_cnf_val);
/* Do a dummy read from GPIO_CNF_x register (lower offset) */
gpio_cnf_val = reg::Read(gpio_base_vaddr + gpio_reg_offset + 0x00);
return gpio_cnf_val;
}
/* Convert the GPIO pad descriptor into its register offset */
u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C));
u32 SetDirection(u32 gpio_pad_name, GpioDirection dir) {
uintptr_t gpio_base_vaddr = GetBaseAddress();
/* Extract the bit and lock values from the GPIO pad descriptor */
u32 gpio_cnf_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (0x01 << (gpio_pad_desc & 0x07)));
/* Fetch this GPIO's pad descriptor */
const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name);
/* Write to the appropriate GPIO_CNF_x register (upper offset) */
*(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset + 0x80)) = gpio_cnf_val;
/* Discard invalid GPIOs */
if (gpio_pad_desc == InvalidPadName) {
return InvalidPadName;
}
/* Do a dummy read from GPIO_CNF_x register (lower offset) */
gpio_cnf_val = *(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset));
/* Convert the GPIO pad descriptor into its register offset */
u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C));
return gpio_cnf_val;
}
/* Set the direction bit and lock values */
u32 gpio_oe_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (static_cast<u32>(dir) << (gpio_pad_desc & 0x07)));
u32 Boot::GpioSetDirection(u32 gpio_pad_name, GpioDirection dir) {
uintptr_t gpio_base_vaddr = GetGpioBaseAddress();
/* Write to the appropriate GPIO_OE_x register (upper offset) */
reg::Write(gpio_base_vaddr + gpio_reg_offset + 0x90, gpio_oe_val);
/* Fetch this GPIO's pad descriptor */
const u32 gpio_pad_desc = GetGpioPadDescriptor(gpio_pad_name);
/* Do a dummy read from GPIO_OE_x register (lower offset) */
gpio_oe_val = reg::Read(gpio_base_vaddr + gpio_reg_offset + 0x10);
/* Discard invalid GPIOs */
if (gpio_pad_desc == GpioInvalid) {
return GpioInvalid;
return gpio_oe_val;
}
/* Convert the GPIO pad descriptor into its register offset */
u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C));
u32 SetValue(u32 gpio_pad_name, GpioValue val) {
uintptr_t gpio_base_vaddr = GetBaseAddress();
/* Set the direction bit and lock values */
u32 gpio_oe_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (static_cast<u32>(dir) << (gpio_pad_desc & 0x07)));
/* Fetch this GPIO's pad descriptor */
const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name);
/* Write to the appropriate GPIO_OE_x register (upper offset) */
*(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset + 0x90)) = gpio_oe_val;
/* Discard invalid GPIOs */
if (gpio_pad_desc == InvalidPadName) {
return InvalidPadName;
}
/* Do a dummy read from GPIO_OE_x register (lower offset) */
gpio_oe_val = *(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset + 0x10));
/* Convert the GPIO pad descriptor into its register offset */
u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C));
return gpio_oe_val;
}
/* Set the output bit and lock values */
u32 gpio_out_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (static_cast<u32>(val) << (gpio_pad_desc & 0x07)));
u32 Boot::GpioSetValue(u32 gpio_pad_name, GpioValue val) {
uintptr_t gpio_base_vaddr = GetGpioBaseAddress();
/* Write to the appropriate GPIO_OUT_x register (upper offset) */
reg::Write(gpio_base_vaddr + gpio_reg_offset + 0xA0, gpio_out_val);
/* Fetch this GPIO's pad descriptor */
const u32 gpio_pad_desc = GetGpioPadDescriptor(gpio_pad_name);
/* Do a dummy read from GPIO_OUT_x register (lower offset) */
gpio_out_val = reg::Read(gpio_base_vaddr + gpio_reg_offset + 0x20);
/* Discard invalid GPIOs */
if (gpio_pad_desc == GpioInvalid) {
return GpioInvalid;
return gpio_out_val;
}
/* Convert the GPIO pad descriptor into its register offset */
u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C));
/* Set the output bit and lock values */
u32 gpio_out_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (static_cast<u32>(val) << (gpio_pad_desc & 0x07)));
/* Write to the appropriate GPIO_OUT_x register (upper offset) */
*(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset + 0xA0)) = gpio_out_val;
/* Do a dummy read from GPIO_OUT_x register (lower offset) */
gpio_out_val = *(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset + 0x20));
return gpio_out_val;
}

View file

@ -18,27 +18,13 @@
#include <switch.h>
#include <stratosphere.hpp>
enum HardwareType {
HardwareType_Icosa = 0,
HardwareType_Copper = 1,
HardwareType_Hoag = 2,
HardwareType_Iowa = 3,
};
#include "i2c/driver/i2c_api.hpp"
struct GpioInitialConfig {
u32 pad_name;
GpioDirection direction;
GpioValue value;
};
namespace sts::boot::gpio {
struct PinmuxInitialConfig {
u32 name;
u32 val;
u32 mask;
};
/* GPIO Utilities. */
u32 Configure(u32 gpio_pad_name);
u32 SetDirection(u32 gpio_pad_name, GpioDirection dir);
u32 SetValue(u32 gpio_pad_name, GpioValue val);
struct WakePinConfig {
u32 index;
bool enabled;
u32 level;
};
}

View file

@ -14,55 +14,62 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "i2c/driver/i2c_api.hpp"
#include "boot_i2c_utils.hpp"
template<typename F>
static Result RetryUntilSuccess(F f) {
constexpr u64 timeout = 10'000'000'000ul;
constexpr u64 retry_interval = 20'000'000ul;
namespace sts::boot {
u64 cur_time = 0;
while (true) {
R_TRY_CLEANUP(f(), {
cur_time += retry_interval;
if (cur_time < timeout) {
svcSleepThread(retry_interval);
continue;
namespace {
template<typename F>
constexpr Result RetryUntilSuccess(F f) {
constexpr u64 timeout = 10'000'000'000ul;
constexpr u64 retry_interval = 20'000'000ul;
u64 cur_time = 0;
while (true) {
R_TRY_CLEANUP(f(), {
cur_time += retry_interval;
if (cur_time < timeout) {
svcSleepThread(retry_interval);
continue;
}
});
return ResultSuccess;
}
});
return ResultSuccess;
}
}
}
Result Boot::ReadI2cRegister(sts::i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) {
if (dst == nullptr || dst_size == 0 || cmd == nullptr || cmd_size == 0) {
std::abort();
}
u8 cmd_list[sts::i2c::CommandListFormatter::MaxCommandListSize];
Result ReadI2cRegister(i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) {
if (dst == nullptr || dst_size == 0 || cmd == nullptr || cmd_size == 0) {
std::abort();
}
sts::i2c::CommandListFormatter formatter(cmd_list, sizeof(cmd_list));
R_ASSERT(formatter.EnqueueSendCommand(I2cTransactionOption_Start, cmd, cmd_size));
R_ASSERT(formatter.EnqueueReceiveCommand(static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop), dst_size));
u8 cmd_list[i2c::CommandListFormatter::MaxCommandListSize];
return RetryUntilSuccess([&]() { return sts::i2c::driver::ExecuteCommandList(session, dst, dst_size, cmd_list, formatter.GetCurrentSize()); });
}
i2c::CommandListFormatter formatter(cmd_list, sizeof(cmd_list));
R_ASSERT(formatter.EnqueueSendCommand(I2cTransactionOption_Start, cmd, cmd_size));
R_ASSERT(formatter.EnqueueReceiveCommand(static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop), dst_size));
Result Boot::WriteI2cRegister(sts::i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) {
if (src == nullptr || src_size == 0 || cmd == nullptr || cmd_size == 0) {
std::abort();
return RetryUntilSuccess([&]() { return i2c::driver::ExecuteCommandList(session, dst, dst_size, cmd_list, formatter.GetCurrentSize()); });
}
u8 cmd_list[0x20];
Result WriteI2cRegister(i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) {
if (src == nullptr || src_size == 0 || cmd == nullptr || cmd_size == 0) {
std::abort();
}
/* N doesn't use a CommandListFormatter here... */
std::memcpy(&cmd_list[0], cmd, cmd_size);
std::memcpy(&cmd_list[cmd_size], src, src_size);
u8 cmd_list[0x20];
/* N doesn't use a CommandListFormatter here... */
std::memcpy(&cmd_list[0], cmd, cmd_size);
std::memcpy(&cmd_list[cmd_size], src, src_size);
return RetryUntilSuccess([&]() { return i2c::driver::Send(session, cmd_list, src_size + cmd_size, static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop)); });
}
Result WriteI2cRegister(i2c::driver::Session &session, const u8 address, const u8 value) {
return WriteI2cRegister(session, &value, sizeof(value), &address, sizeof(address));
}
return RetryUntilSuccess([&]() { return sts::i2c::driver::Send(session, cmd_list, src_size + cmd_size, static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop)); });
}
Result Boot::WriteI2cRegister(sts::i2c::driver::Session &session, const u8 address, const u8 value) {
return Boot::WriteI2cRegister(session, &value, sizeof(value), &address, sizeof(address));
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c/driver/i2c_api.hpp"
namespace sts::boot {
/* I2C Utilities. */
Result ReadI2cRegister(i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size);
Result WriteI2cRegister(i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size);
Result WriteI2cRegister(i2c::driver::Session &session, const u8 address, const u8 value);
}

View file

@ -23,8 +23,22 @@
#include <atmosphere.h>
#include <stratosphere.hpp>
#include "boot_functions.hpp"
#include "boot_reboot_manager.hpp"
#include "boot_boot_reason.hpp"
#include "boot_change_voltage.hpp"
#include "boot_check_battery.hpp"
#include "boot_check_clock.hpp"
#include "boot_clock_initial_configuration.hpp"
#include "boot_fan_enable.hpp"
#include "boot_gpio_initial_configuration.hpp"
#include "boot_pinmux_initial_configuration.hpp"
#include "boot_repair_boot_images.hpp"
#include "boot_splash_screen.hpp"
#include "boot_wake_pins.hpp"
#include "boot_power_utils.hpp"
#include "boot_spl_utils.hpp"
using namespace sts;
extern "C" {
extern u32 __start__;
@ -53,7 +67,7 @@ void __libnx_exception_handler(ThreadExceptionDump *ctx) {
void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx) {
/* We're boot sysmodule, so manually reboot to fatal error. */
BootRebootManager::RebootForFatalError(ctx);
boot::RebootForFatalError(ctx);
}
void __libnx_initheap(void) {
@ -94,47 +108,45 @@ int main(int argc, char **argv)
consoleDebugInit(debugDevice_SVC);
/* Change voltage from 3.3v to 1.8v for select devices. */
Boot::ChangeGpioVoltageTo1_8v();
boot::ChangeGpioVoltageTo1_8v();
/* Setup GPIO. */
Boot::SetInitialGpioConfiguration();
boot::gpio::SetInitialConfiguration();
/* Check USB PLL/UTMIP clock. */
Boot::CheckClock();
boot::CheckClock();
/* Talk to PMIC/RTC, set boot reason with SPL. */
Boot::DetectBootReason();
boot::DetectBootReason();
const HardwareType hw_type = Boot::GetHardwareType();
if (hw_type != HardwareType_Copper) {
const auto hw_type = boot::GetHardwareType();
if (hw_type != spl::HardwareType::Copper) {
/* Display splash screen for two seconds. */
Boot::ShowSplashScreen();
boot::ShowSplashScreen();
/* Check that the battery has enough to boot. */
Boot::CheckBatteryCharge();
boot::CheckBatteryCharge();
}
/* Configure pinmux + drive pads. */
Boot::ConfigurePinmux();
boot::pinmux::SetInitialConfiguration();
/* Configure the PMC wake pin settings. */
Boot::SetInitialWakePinConfiguration();
boot::SetInitialWakePinConfiguration();
/* Configure output clock. */
if (hw_type != HardwareType_Copper) {
Boot::SetInitialClockConfiguration();
if (hw_type != spl::HardwareType::Copper) {
boot::SetInitialClockConfiguration();
}
/* Set Fan enable config (Copper only). */
Boot::SetFanEnabled();
boot::SetFanEnabled();
/* Repair boot partitions in NAND if needed. */
Boot::CheckAndRepairBootImages();
boot::CheckAndRepairBootImages();
/* Tell PM to start boot2. */
if (R_FAILED(pmshellNotifyBootFinished())) {
std::abort();
}
R_ASSERT(pmshellNotifyBootFinished());
return 0;
}

View file

@ -38,10 +38,10 @@ namespace sts::pcv {
ClkRstRegisters regs;
regs.SetBus(ConvertFromPcvModule(module));
/* Set clock enabled/source. */
SetRegisterBits(regs.clk_en_reg, regs.mask);
ReadWriteRegisterBits(regs.clk_src_reg, 0x4, 0xFF);
reg::SetBits(regs.clk_en_reg, regs.mask);
reg::ReadWrite(regs.clk_src_reg, 0x4, 0xFF);
svcSleepThread(1000ul);
ReadWriteRegisterBits(regs.clk_src_reg, 0, 0xE0000000);
reg::ReadWrite(regs.clk_src_reg, 0, 0xE0000000);
svcSleepThread(2000ul);
return ResultSuccess;
@ -66,9 +66,9 @@ namespace sts::pcv {
/* Set/clear reset. */
if (reset) {
SetRegisterBits(regs.rst_reg, regs.mask);
reg::SetBits(regs.rst_reg, regs.mask);
} else {
ClearRegisterBits(regs.rst_reg, ~regs.mask);
reg::ClearBits(regs.rst_reg, regs.mask);
}
return ResultSuccess;

View file

@ -1,95 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_pinmux_map.hpp"
#include "boot_pinmux_initial_configuration_icosa.hpp"
#include "boot_pinmux_initial_configuration_copper.hpp"
#include "boot_pinmux_initial_configuration_hoag.hpp"
#include "boot_pinmux_initial_configuration_iowa.hpp"
#include "boot_pinmux_initial_drive_pad_configuration.hpp"
void Boot::ConfigurePinmux() {
/* Update parks. */
for (size_t i = 0; i < PinmuxPadNameMax; i++) {
Boot::PinmuxUpdatePark(static_cast<u32>(i));
}
/* Dummy read all drive pads. */
for (size_t i = 0; i < PinmuxDrivePadNameMax; i++) {
Boot::PinmuxDummyReadDrivePad(static_cast<u32>(i));
}
/* Set initial pad configs. */
Boot::ConfigurePinmuxInitialPads();
/* Set initial drive pad configs. */
Boot::ConfigurePinmuxInitialDrivePads();
}
void Boot::ConfigurePinmuxInitialPads() {
const PinmuxInitialConfig *configs = nullptr;
size_t num_configs = 0;
const HardwareType hw_type = Boot::GetHardwareType();
switch (hw_type) {
case HardwareType_Icosa:
configs = PinmuxInitialConfigsIcosa;
num_configs = PinmuxNumInitialConfigsIcosa;
break;
case HardwareType_Copper:
configs = PinmuxInitialConfigsCopper;
num_configs = PinmuxNumInitialConfigsCopper;
break;
case HardwareType_Hoag:
configs = PinmuxInitialConfigsHoag;
num_configs = PinmuxNumInitialConfigsHoag;
break;
case HardwareType_Iowa:
configs = PinmuxInitialConfigsIowa;
num_configs = PinmuxNumInitialConfigsIowa;
break;
default:
/* Unknown hardware type, we can't proceed. */
std::abort();
}
/* Ensure we found an appropriate config. */
if (configs == nullptr) {
std::abort();
}
for (size_t i = 0; i < num_configs - 1; i++) {
Boot::PinmuxUpdatePad(configs[i].name, configs[i].val, configs[i].mask);
}
/* Extra configs for iowa only. */
if (hw_type == HardwareType_Iowa) {
static constexpr u32 ExtraIowaPinmuxPadNames[] = {
0xAA, 0xAC, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9
};
for (size_t i = 0; i < sizeof(ExtraIowaPinmuxPadNames) / sizeof(ExtraIowaPinmuxPadNames[0]); i++) {
Boot::PinmuxUpdatePad(ExtraIowaPinmuxPadNames[i], 0x2000, 0x2000);
}
}
}
void Boot::ConfigurePinmuxInitialDrivePads() {
const PinmuxInitialConfig *configs = PinmuxInitialDrivePadConfigs;
for (size_t i = 0; i < PinmuxNumInitialDrivePadConfigs; i++) {
Boot::PinmuxUpdateDrivePad(configs[i].name, configs[i].val, configs[i].mask);
}
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_pinmux_initial_configuration.hpp"
#include "boot_pinmux_utils.hpp"
#include "boot_spl_utils.hpp"
namespace sts::boot::pinmux {
namespace {
struct InitialConfig {
u32 name;
u32 val;
u32 mask;
};
/* Include all initial configuration definitions. */
#include "boot_pinmux_initial_configuration_icosa.inc"
#include "boot_pinmux_initial_configuration_copper.inc"
#include "boot_pinmux_initial_configuration_hoag.inc"
#include "boot_pinmux_initial_configuration_iowa.inc"
#include "boot_pinmux_initial_drive_pad_configuration.inc"
/* Configuration helpers. */
void ConfigureInitialPads() {
const InitialConfig *configs = nullptr;
size_t num_configs = 0;
const auto hw_type = GetHardwareType();
switch (hw_type) {
case spl::HardwareType::Icosa:
configs = InitialConfigsIcosa;
num_configs = NumInitialConfigsIcosa;
break;
case spl::HardwareType::Copper:
configs = InitialConfigsCopper;
num_configs = NumInitialConfigsCopper;
break;
case spl::HardwareType::Hoag:
configs = InitialConfigsHoag;
num_configs = NumInitialConfigsHoag;
break;
case spl::HardwareType::Iowa:
configs = InitialConfigsIowa;
num_configs = NumInitialConfigsIowa;
break;
default:
/* Unknown hardware type, we can't proceed. */
std::abort();
}
/* Ensure we found an appropriate config. */
if (configs == nullptr) {
std::abort();
}
for (size_t i = 0; i < num_configs - 1; i++) {
UpdatePad(configs[i].name, configs[i].val, configs[i].mask);
}
/* Extra configs for iowa only. */
if (hw_type == spl::HardwareType::Iowa) {
static constexpr u32 ExtraIowaPadNames[] = {
0xAA, 0xAC, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9
};
for (size_t i = 0; i < sizeof(ExtraIowaPadNames) / sizeof(ExtraIowaPadNames[0]); i++) {
UpdatePad(ExtraIowaPadNames[i], 0x2000, 0x2000);
}
}
}
void ConfigureInitialDrivePads() {
const InitialConfig *configs = InitialDrivePadConfigs;
for (size_t i = 0; i < NumInitialDrivePadConfigs; i++) {
UpdateDrivePad(configs[i].name, configs[i].val, configs[i].mask);
}
}
}
void SetInitialConfiguration() {
/* Update all parks. */
UpdateAllParks();
/* Dummy read all drive pads. */
DummyReadAllDrivePads();
/* Set initial pad configs. */
ConfigureInitialPads();
/* Set initial drive pad configs. */
ConfigureInitialDrivePads();
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot::pinmux {
void SetInitialConfiguration();
}

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialConfigsCopper[] = {
constexpr InitialConfig InitialConfigsCopper[] = {
{0x10, 0x20, 0x27F},
{0x0F, 0x00, 0x267},
{0x0E, 0x20, 0x27F},
@ -183,4 +178,4 @@ static constexpr PinmuxInitialConfig PinmuxInitialConfigsCopper[] = {
{0x68, 0x05, 0x07},
};
static constexpr u32 PinmuxNumInitialConfigsCopper = (sizeof(PinmuxInitialConfigsCopper) / sizeof(PinmuxInitialConfigsCopper[0]));
constexpr u32 NumInitialConfigsCopper = (sizeof(InitialConfigsCopper) / sizeof(InitialConfigsCopper[0]));

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialConfigsHoag[] = {
constexpr InitialConfig InitialConfigsHoag[] = {
{0x5D, 0x00, 0x67},
{0x47, 0x28, 0x7F},
{0x48, 0x00, 0x67},
@ -183,4 +178,4 @@ static constexpr PinmuxInitialConfig PinmuxInitialConfigsHoag[] = {
{0x63, 0x05, 0x07},
};
static constexpr u32 PinmuxNumInitialConfigsHoag = (sizeof(PinmuxInitialConfigsHoag) / sizeof(PinmuxInitialConfigsHoag[0]));
constexpr u32 NumInitialConfigsHoag = (sizeof(InitialConfigsHoag) / sizeof(InitialConfigsHoag[0]));

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialConfigsIcosa[] = {
constexpr InitialConfig InitialConfigsIcosa[] = {
{0x5D, 0x00, 0x67},
{0x47, 0x28, 0x7F},
{0x48, 0x00, 0x67},
@ -183,4 +178,4 @@ static constexpr PinmuxInitialConfig PinmuxInitialConfigsIcosa[] = {
{0x63, 0x05, 0x07},
};
static constexpr u32 PinmuxNumInitialConfigsIcosa = (sizeof(PinmuxInitialConfigsIcosa) / sizeof(PinmuxInitialConfigsIcosa[0]));
constexpr u32 NumInitialConfigsIcosa = (sizeof(InitialConfigsIcosa) / sizeof(InitialConfigsIcosa[0]));

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialConfigsIowa[] = {
constexpr InitialConfig InitialConfigsIowa[] = {
{0x5D, 0x00, 0x7F},
{0x47, 0x28, 0x7F},
{0x48, 0x00, 0x7F},
@ -196,4 +191,4 @@ static constexpr PinmuxInitialConfig PinmuxInitialConfigsIowa[] = {
{0x63, 0x05, 0x07},
};
static constexpr u32 PinmuxNumInitialConfigsIowa = (sizeof(PinmuxInitialConfigsIowa) / sizeof(PinmuxInitialConfigsIowa[0]));
constexpr u32 NumInitialConfigsIowa = (sizeof(InitialConfigsIowa) / sizeof(InitialConfigsIowa[0]));

View file

@ -14,12 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialDrivePadConfigs[] = {
constexpr InitialConfig InitialDrivePadConfigs[] = {
{0x04, 0x01010000, 0x01F1F000},
{0x0D, 0x01010000, 0x01F1F000},
{0x10, 0x01010000, 0x01F1F000},
@ -69,4 +64,4 @@ static constexpr PinmuxInitialConfig PinmuxInitialDrivePadConfigs[] = {
{0x69, 0x51212000, 0xF1F1F000},
};
static constexpr u32 PinmuxNumInitialDrivePadConfigs = (sizeof(PinmuxInitialDrivePadConfigs) / sizeof(PinmuxInitialDrivePadConfigs[0]));
constexpr u32 NumInitialDrivePadConfigs = (sizeof(InitialDrivePadConfigs) / sizeof(InitialDrivePadConfigs[0]));

View file

@ -14,21 +14,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
struct PinmuxDefinition {
struct Definition {
u32 reg_offset;
u32 mask_val;
u32 pm_val;
};
struct PinmuxDrivePadDefinition {
struct DrivePadDefinition {
u32 reg_offset;
u32 mask_val;
};
static constexpr PinmuxDefinition PinmuxMap[] = {
constexpr Definition Map[] = {
{0x00003000, 0x72FF, 0x01}, /* Sdmmc1Clk */
{0x00003004, 0x72FF, 0x02}, /* Sdmmc1Cmd */
{0x00003008, 0x72FF, 0x02}, /* Sdmmc1Dat3 */
@ -208,9 +205,9 @@ static constexpr PinmuxDefinition PinmuxMap[] = {
{0x000032C4, 0x1F2FF, 0x00}, /* Sdmmc2Dqsb */
};
static constexpr u32 PinmuxPadNameMax = (sizeof(PinmuxMap) / sizeof(PinmuxMap[0]));
constexpr u32 PadNameMax = (sizeof(Map) / sizeof(Map[0]));
static constexpr PinmuxDrivePadDefinition PinmuxDrivePadMap[] = {
constexpr DrivePadDefinition DrivePadMap[] = {
{0x000008E4, 0x01F1F000}, /* AlsProxInt */
{0x000008E8, 0x01F1F000}, /* ApReady */
{0x000008EC, 0x01F1F000}, /* ApWakeBt */
@ -361,4 +358,4 @@ static constexpr PinmuxDrivePadDefinition PinmuxDrivePadMap[] = {
{0x00000B6C, 0x01F1F000}, /* WifiWakeAp */
};
static constexpr u32 PinmuxDrivePadNameMax = (sizeof(PinmuxDrivePadMap) / sizeof(PinmuxDrivePadMap[0]));
constexpr u32 DrivePadNameMax = (sizeof(DrivePadMap) / sizeof(DrivePadMap[0]));

View file

@ -14,488 +14,520 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_pinmux_map.hpp"
#include <stratosphere/reg.hpp>
static bool g_initialized_pinmux_vaddr = false;
static uintptr_t g_pinmux_vaddr = 0;
#include "boot_pinmux_utils.hpp"
static inline const PinmuxDefinition *GetPinmuxDefinition(u32 pinmux_name) {
if (pinmux_name >= PinmuxPadNameMax) {
std::abort();
}
namespace sts::boot::pinmux {
return &PinmuxMap[pinmux_name];
}
namespace {
static inline const PinmuxDrivePadDefinition *GetPinmuxDrivePadDefinition(u32 pinmux_name) {
if (pinmux_name >= PinmuxDrivePadNameMax) {
std::abort();
}
/* Pull in Pinmux map definitions. */
#include "boot_pinmux_map.inc"
return &PinmuxDrivePadMap[pinmux_name];
}
constexpr u32 ApbMiscPhysicalBase = 0x70000000;
static uintptr_t GetPinmuxBaseAddress() {
if (!g_initialized_pinmux_vaddr) {
g_pinmux_vaddr = GetIoMapping(Boot::ApbMiscPhysicalBase, 0x4000);
g_initialized_pinmux_vaddr = true;
}
return g_pinmux_vaddr;
}
/* Globals. */
bool g_initialized_pinmux_vaddr = false;
uintptr_t g_pinmux_vaddr = 0;
u32 Boot::PinmuxUpdatePark(u32 pinmux_name) {
const uintptr_t pinmux_base_vaddr = GetPinmuxBaseAddress();
const PinmuxDefinition *pinmux_def = GetPinmuxDefinition(pinmux_name);
/* Fetch this PINMUX's register offset */
u32 pinmux_reg_offset = pinmux_def->reg_offset;
/* Fetch this PINMUX's mask value */
u32 pinmux_mask_val = pinmux_def->mask_val;
/* Get current register ptr. */
volatile u32 *pinmux_reg = reinterpret_cast<volatile u32 *>(pinmux_base_vaddr + pinmux_reg_offset);
/* Read from the PINMUX register */
u32 pinmux_val = *pinmux_reg;
/* This PINMUX supports park change */
if (pinmux_mask_val & 0x20) {
/* Clear park status if set */
if (pinmux_val & 0x20) {
pinmux_val &= ~(0x20);
}
}
/* Write to the appropriate PINMUX register */
*pinmux_reg = pinmux_val;
/* Do a dummy read from the PINMUX register */
pinmux_val = *pinmux_reg;
return pinmux_val;
}
u32 Boot::PinmuxUpdatePad(u32 pinmux_name, u32 pinmux_config_val, u32 pinmux_config_mask_val) {
const uintptr_t pinmux_base_vaddr = GetPinmuxBaseAddress();
const PinmuxDefinition *pinmux_def = GetPinmuxDefinition(pinmux_name);
/* Fetch this PINMUX's register offset */
u32 pinmux_reg_offset = pinmux_def->reg_offset;
/* Fetch this PINMUX's mask value */
u32 pinmux_mask_val = pinmux_def->mask_val;
/* Get current register ptr. */
volatile u32 *pinmux_reg = reinterpret_cast<volatile u32 *>(pinmux_base_vaddr + pinmux_reg_offset);
/* Read from the PINMUX register */
u32 pinmux_val = *pinmux_reg;
/* This PINMUX register is locked */
if (pinmux_val & 0x80) {
std::abort();
}
u32 pm_val = (pinmux_config_val & 0x07);
/* Adjust PM */
if (pinmux_config_mask_val & 0x07) {
/* Apply additional changes first */
if (pm_val == 0x05) {
/* This pin supports PUPD change */
if (pinmux_mask_val & 0x0C) {
/* Change PUPD */
if ((pinmux_val & 0x0C) != 0x04) {
pinmux_val &= 0xFFFFFFF3;
pinmux_val |= 0x04;
}
/* Helpers. */
inline const Definition *GetDefinition(u32 pinmux_name) {
if (pinmux_name >= PadNameMax) {
std::abort();
}
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (!(pinmux_val & 0x10)) {
pinmux_val |= 0x10;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (pinmux_val & 0x40) {
pinmux_val &= 0xFFFFFFBF;
}
}
} else if (pm_val >= 0x06) {
/* Default to safe value */
pm_val = 0x04;
return &Map[pinmux_name];
}
/* Translate PM value if necessary */
if (pm_val == 0x04 || pm_val == 0x05) {
pm_val = pinmux_def->pm_val;
inline const DrivePadDefinition *GetDrivePadDefinition(u32 pinmux_name) {
if (pinmux_name >= DrivePadNameMax) {
std::abort();
}
return &DrivePadMap[pinmux_name];
}
/* This pin supports PM change */
if (pinmux_mask_val & 0x03) {
/* Change PM */
if ((pinmux_val & 0x03) != (pm_val & 0x03)) {
pinmux_val &= 0xFFFFFFFC;
pinmux_val |= (pm_val & 0x03);
uintptr_t GetBaseAddress() {
if (!g_initialized_pinmux_vaddr) {
g_pinmux_vaddr = GetIoMapping(ApbMiscPhysicalBase, 0x4000);
g_initialized_pinmux_vaddr = true;
}
return g_pinmux_vaddr;
}
}
u32 pupd_config_val = (pinmux_config_val & 0x18);
u32 UpdatePark(u32 pinmux_name) {
const uintptr_t pinmux_base_vaddr = GetBaseAddress();
const Definition *pinmux_def = GetDefinition(pinmux_name);
/* Adjust PUPD */
if (pinmux_config_mask_val & 0x18) {
if (pupd_config_val < 0x11) {
/* This pin supports PUPD change */
if (pinmux_mask_val & 0x0C) {
/* Change PUPD */
if (((pinmux_val >> 0x02) & 0x03) != (pupd_config_val >> 0x03)) {
pinmux_val &= 0xFFFFFFF3;
pinmux_val |= (pupd_config_val >> 0x01);
}
}
}
}
/* Fetch this PINMUX's register offset */
u32 pinmux_reg_offset = pinmux_def->reg_offset;
u32 eod_config_val = (pinmux_config_val & 0x60);
/* Fetch this PINMUX's mask value */
u32 pinmux_mask_val = pinmux_def->mask_val;
/* Adjust EOd field */
if (pinmux_config_mask_val & 0x60) {
if (eod_config_val == 0x20) {
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (!(pinmux_val & 0x10)) {
pinmux_val |= 0x10;
}
}
/* Get current register ptr. */
uintptr_t pinmux_reg = pinmux_base_vaddr + pinmux_reg_offset;
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (!(pinmux_val & 0x40)) {
pinmux_val |= 0x40;
}
}
/* Read from the PINMUX register */
u32 pinmux_val = reg::Read(pinmux_reg);
/* This pin supports EOd change */
if (pinmux_mask_val & 0x800) {
/* Change EOd */
if (pinmux_val & 0x800) {
pinmux_val &= 0xFFFFF7FF;
}
}
} else if (eod_config_val == 0x40) {
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (pinmux_val & 0x10) {
pinmux_val &= 0xFFFFFFEF;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (!(pinmux_val & 0x40)) {
pinmux_val |= 0x40;
}
}
/* This pin supports EOd change */
if (pinmux_mask_val & 0x800) {
/* Change EOd */
if (pinmux_val & 0x800) {
pinmux_val &= 0xFFFFF7FF;
}
}
} else if (eod_config_val == 0x60) {
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (pinmux_val & 0x10) {
pinmux_val &= 0xFFFFFFEF;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (!(pinmux_val & 0x40)) {
pinmux_val |= 0x40;
}
}
/* This pin supports EOd change */
if (pinmux_mask_val & 0x800) {
/* Change EOd */
if (!(pinmux_val & 0x800)) {
pinmux_val |= 0x800;
}
}
} else {
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (pinmux_val & 0x10) {
pinmux_val &= 0xFFFFFFEF;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (pinmux_val & 0x40) {
pinmux_val &= 0xFFFFFFBF;
}
}
/* This pin supports EOd change */
if (pinmux_mask_val & 0x800) {
/* Change EOd */
if (pinmux_val & 0x800) {
pinmux_val &= 0xFFFFF7FF;
}
}
}
}
u32 lock_config_val = (pinmux_config_val & 0x80);
/* Adjust Lock */
if (pinmux_config_mask_val & 0x80) {
/* This pin supports Lock change */
if (pinmux_mask_val & 0x80) {
/* Change Lock */
if ((pinmux_val ^ pinmux_config_val) & 0x80) {
pinmux_val &= 0xFFFFFF7F;
pinmux_val |= lock_config_val;
}
}
}
u32 ioreset_config_val = (((pinmux_config_val >> 0x08) & 0x1) << 0x10);
/* Adjust IoReset */
if (pinmux_config_mask_val & 0x100) {
/* This pin supports IoReset change */
if (pinmux_mask_val & 0x10000) {
/* Change IoReset */
if (((pinmux_val >> 0x10) ^ (pinmux_config_val >> 0x08)) & 0x01) {
pinmux_val &= 0xFFFEFFFF;
pinmux_val |= ioreset_config_val;
}
}
}
u32 park_config_val = (((pinmux_config_val >> 0x0A) & 0x1) << 0x5);
/* Adjust Park */
if (pinmux_config_mask_val & 0x400) {
/* This pin supports Park change */
/* This PINMUX supports park change */
if (pinmux_mask_val & 0x20) {
/* Change Park */
if (((pinmux_val >> 0x05) ^ (pinmux_config_val >> 0x0A)) & 0x01) {
pinmux_val &= 0xFFFFFFDF;
pinmux_val |= park_config_val;
/* Clear park status if set */
if (pinmux_val & 0x20) {
pinmux_val &= ~(0x20);
}
}
/* Write to the appropriate PINMUX register */
reg::Write(pinmux_reg, pinmux_val);
/* Do a dummy read from the PINMUX register */
pinmux_val = reg::Read(pinmux_reg);
return pinmux_val;
}
u32 UpdatePad(u32 pinmux_name, u32 pinmux_config_val, u32 pinmux_config_mask_val) {
const uintptr_t pinmux_base_vaddr = GetBaseAddress();
const Definition *pinmux_def = GetDefinition(pinmux_name);
/* Fetch this PINMUX's register offset */
u32 pinmux_reg_offset = pinmux_def->reg_offset;
/* Fetch this PINMUX's mask value */
u32 pinmux_mask_val = pinmux_def->mask_val;
/* Get current register ptr. */
uintptr_t pinmux_reg = pinmux_base_vaddr + pinmux_reg_offset;
/* Read from the PINMUX register */
u32 pinmux_val = reg::Read(pinmux_reg);
/* This PINMUX register is locked */
if (pinmux_val & 0x80) {
std::abort();
}
u32 pm_val = (pinmux_config_val & 0x07);
/* Adjust PM */
if (pinmux_config_mask_val & 0x07) {
/* Apply additional changes first */
if (pm_val == 0x05) {
/* This pin supports PUPD change */
if (pinmux_mask_val & 0x0C) {
/* Change PUPD */
if ((pinmux_val & 0x0C) != 0x04) {
pinmux_val &= 0xFFFFFFF3;
pinmux_val |= 0x04;
}
}
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (!(pinmux_val & 0x10)) {
pinmux_val |= 0x10;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (pinmux_val & 0x40) {
pinmux_val &= 0xFFFFFFBF;
}
}
} else if (pm_val >= 0x06) {
/* Default to safe value */
pm_val = 0x04;
}
/* Translate PM value if necessary */
if (pm_val == 0x04 || pm_val == 0x05) {
pm_val = pinmux_def->pm_val;
}
/* This pin supports PM change */
if (pinmux_mask_val & 0x03) {
/* Change PM */
if ((pinmux_val & 0x03) != (pm_val & 0x03)) {
pinmux_val &= 0xFFFFFFFC;
pinmux_val |= (pm_val & 0x03);
}
}
}
u32 pupd_config_val = (pinmux_config_val & 0x18);
/* Adjust PUPD */
if (pinmux_config_mask_val & 0x18) {
if (pupd_config_val < 0x11) {
/* This pin supports PUPD change */
if (pinmux_mask_val & 0x0C) {
/* Change PUPD */
if (((pinmux_val >> 0x02) & 0x03) != (pupd_config_val >> 0x03)) {
pinmux_val &= 0xFFFFFFF3;
pinmux_val |= (pupd_config_val >> 0x01);
}
}
}
}
u32 eod_config_val = (pinmux_config_val & 0x60);
/* Adjust EOd field */
if (pinmux_config_mask_val & 0x60) {
if (eod_config_val == 0x20) {
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (!(pinmux_val & 0x10)) {
pinmux_val |= 0x10;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (!(pinmux_val & 0x40)) {
pinmux_val |= 0x40;
}
}
/* This pin supports EOd change */
if (pinmux_mask_val & 0x800) {
/* Change EOd */
if (pinmux_val & 0x800) {
pinmux_val &= 0xFFFFF7FF;
}
}
} else if (eod_config_val == 0x40) {
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (pinmux_val & 0x10) {
pinmux_val &= 0xFFFFFFEF;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (!(pinmux_val & 0x40)) {
pinmux_val |= 0x40;
}
}
/* This pin supports EOd change */
if (pinmux_mask_val & 0x800) {
/* Change EOd */
if (pinmux_val & 0x800) {
pinmux_val &= 0xFFFFF7FF;
}
}
} else if (eod_config_val == 0x60) {
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (pinmux_val & 0x10) {
pinmux_val &= 0xFFFFFFEF;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (!(pinmux_val & 0x40)) {
pinmux_val |= 0x40;
}
}
/* This pin supports EOd change */
if (pinmux_mask_val & 0x800) {
/* Change EOd */
if (!(pinmux_val & 0x800)) {
pinmux_val |= 0x800;
}
}
} else {
/* This pin supports Tristate change */
if (pinmux_mask_val & 0x10) {
/* Change Tristate */
if (pinmux_val & 0x10) {
pinmux_val &= 0xFFFFFFEF;
}
}
/* This pin supports EInput change */
if (pinmux_mask_val & 0x40) {
/* Change EInput */
if (pinmux_val & 0x40) {
pinmux_val &= 0xFFFFFFBF;
}
}
/* This pin supports EOd change */
if (pinmux_mask_val & 0x800) {
/* Change EOd */
if (pinmux_val & 0x800) {
pinmux_val &= 0xFFFFF7FF;
}
}
}
}
u32 lock_config_val = (pinmux_config_val & 0x80);
/* Adjust Lock */
if (pinmux_config_mask_val & 0x80) {
/* This pin supports Lock change */
if (pinmux_mask_val & 0x80) {
/* Change Lock */
if ((pinmux_val ^ pinmux_config_val) & 0x80) {
pinmux_val &= 0xFFFFFF7F;
pinmux_val |= lock_config_val;
}
}
}
u32 ioreset_config_val = (((pinmux_config_val >> 0x08) & 0x1) << 0x10);
/* Adjust IoReset */
if (pinmux_config_mask_val & 0x100) {
/* This pin supports IoReset change */
if (pinmux_mask_val & 0x10000) {
/* Change IoReset */
if (((pinmux_val >> 0x10) ^ (pinmux_config_val >> 0x08)) & 0x01) {
pinmux_val &= 0xFFFEFFFF;
pinmux_val |= ioreset_config_val;
}
}
}
u32 park_config_val = (((pinmux_config_val >> 0x0A) & 0x1) << 0x5);
/* Adjust Park */
if (pinmux_config_mask_val & 0x400) {
/* This pin supports Park change */
if (pinmux_mask_val & 0x20) {
/* Change Park */
if (((pinmux_val >> 0x05) ^ (pinmux_config_val >> 0x0A)) & 0x01) {
pinmux_val &= 0xFFFFFFDF;
pinmux_val |= park_config_val;
}
}
}
u32 elpdr_config_val = (((pinmux_config_val >> 0x0B) & 0x1) << 0x08);
/* Adjust ELpdr */
if (pinmux_config_mask_val & 0x800) {
/* This pin supports ELpdr change */
if (pinmux_mask_val & 0x100) {
/* Change ELpdr */
if (((pinmux_val >> 0x08) ^ (pinmux_config_val >> 0x0B)) & 0x01) {
pinmux_val &= 0xFFFFFEFF;
pinmux_val |= elpdr_config_val;
}
}
}
u32 ehsm_config_val = (((pinmux_config_val >> 0x0C) & 0x1) << 0x09);
/* Adjust EHsm */
if (pinmux_config_mask_val & 0x1000) {
/* This pin supports EHsm change */
if (pinmux_mask_val & 0x200) {
/* Change EHsm */
if (((pinmux_val >> 0x09) ^ (pinmux_config_val >> 0x0C)) & 0x01) {
pinmux_val &= 0xFFFFFDFF;
pinmux_val |= ehsm_config_val;
}
}
}
u32 eiohv_config_val = (((pinmux_config_val >> 0x09) & 0x1) << 0x0A);
/* Adjust EIoHv */
if (pinmux_config_mask_val & 0x200) {
/* This pin supports EIoHv change */
if (pinmux_mask_val & 0x400) {
/* Change EIoHv */
if (((pinmux_val >> 0x0A) ^ (pinmux_config_val >> 0x09)) & 0x01) {
pinmux_val &= 0xFFFFFBFF;
pinmux_val |= eiohv_config_val;
}
}
}
u32 eschmt_config_val = (((pinmux_config_val >> 0x0D) & 0x1) << 0x0C);
/* Adjust ESchmt */
if (pinmux_config_mask_val & 0x2000) {
/* This pin supports ESchmt change */
if (pinmux_mask_val & 0x1000) {
/* Change ESchmt */
if (((pinmux_val >> 0x0C) ^ (pinmux_config_val >> 0x0D)) & 0x01) {
pinmux_val &= 0xFFFFEFFF;
pinmux_val |= eschmt_config_val;
}
}
}
u32 preemp_config_val = (((pinmux_config_val >> 0x10) & 0x1) << 0xF);
/* Adjust Preemp */
if (pinmux_config_mask_val & 0x10000) {
/* This pin supports Preemp change */
if (pinmux_mask_val & 0x8000) {
/* Change Preemp */
if (((pinmux_val >> 0x0F) ^ (pinmux_config_val >> 0x10)) & 0x01) {
pinmux_val &= 0xFFFF7FFF;
pinmux_val |= preemp_config_val;
}
}
}
u32 drvtype_config_val = (((pinmux_config_val >> 0x0E) & 0x3) << 0xD);
/* Adjust DrvType */
if (pinmux_config_mask_val & 0xC000) {
/* This pin supports DrvType change */
if (pinmux_mask_val & 0x6000) {
/* Change DrvType */
if (((pinmux_val >> 0x0D) ^ (pinmux_config_val >> 0x0E)) & 0x03) {
pinmux_val &= 0xFFFF9FFF;
pinmux_val |= drvtype_config_val;
}
}
}
/* Write to the appropriate PINMUX register */
reg::Write(pinmux_reg, pinmux_val);
/* Do a dummy read from the PINMUX register */
pinmux_val = reg::Read(pinmux_reg);
return pinmux_val;
}
u32 UpdateDrivePad(u32 pinmux_drivepad_name, u32 pinmux_drivepad_config_val, u32 pinmux_drivepad_config_mask_val) {
const uintptr_t pinmux_base_vaddr = GetBaseAddress();
const DrivePadDefinition *pinmux_drivepad_def = GetDrivePadDefinition(pinmux_drivepad_name);
/* Fetch this PINMUX drive group's register offset */
u32 pinmux_drivepad_reg_offset = pinmux_drivepad_def->reg_offset;
/* Fetch this PINMUX drive group's mask value */
u32 pinmux_drivepad_mask_val = pinmux_drivepad_def->mask_val;
/* Get current register ptr. */
uintptr_t pinmux_drivepad_reg = pinmux_base_vaddr + pinmux_drivepad_reg_offset;
/* Read from the PINMUX drive group register */
u32 pinmux_drivepad_val = reg::Read(pinmux_drivepad_reg);
/* Adjust DriveDownStrength */
if (pinmux_drivepad_config_mask_val & 0x1F000) {
u32 mask_val = 0x7F000;
/* Adjust mask value */
if ((pinmux_drivepad_mask_val & 0x7F000) != 0x7F000)
mask_val = 0x1F000;
/* This drive group supports DriveDownStrength change */
if (pinmux_drivepad_mask_val & mask_val) {
/* Change DriveDownStrength */
if (((pinmux_drivepad_config_val & 0x7F000) & mask_val) != (pinmux_drivepad_val & mask_val)) {
pinmux_drivepad_val &= ~(mask_val);
pinmux_drivepad_val |= ((pinmux_drivepad_config_val & 0x7F000) & mask_val);
}
}
}
/* Adjust DriveUpStrength */
if (pinmux_drivepad_config_mask_val & 0x1F00000) {
u32 mask_val = 0x7F00000;
/* Adjust mask value */
if ((pinmux_drivepad_mask_val & 0x7F00000) != 0x7F00000)
mask_val = 0x1F00000;
/* This drive group supports DriveUpStrength change */
if (pinmux_drivepad_mask_val & mask_val) {
/* Change DriveUpStrength */
if (((pinmux_drivepad_config_val & 0x7F00000) & mask_val) != (pinmux_drivepad_val & mask_val)) {
pinmux_drivepad_val &= ~(mask_val);
pinmux_drivepad_val |= ((pinmux_drivepad_config_val & 0x7F00000) & mask_val);
}
}
}
/* Adjust DriveDownSlew */
if (pinmux_drivepad_config_mask_val & 0x30000000) {
/* This drive group supports DriveDownSlew change */
if (pinmux_drivepad_mask_val & 0x30000000) {
/* Change DriveDownSlew */
if ((pinmux_drivepad_val ^ pinmux_drivepad_config_val) & 0x30000000) {
pinmux_drivepad_val &= 0xCFFFFFFF;
pinmux_drivepad_val |= (pinmux_drivepad_config_val & 0x30000000);
}
}
}
/* Adjust DriveUpSlew */
if (pinmux_drivepad_config_mask_val & 0xC0000000) {
/* This drive group supports DriveUpSlew change */
if (pinmux_drivepad_mask_val & 0xC0000000) {
/* Change DriveUpSlew */
if ((pinmux_drivepad_val ^ pinmux_drivepad_config_val) & 0xC0000000) {
pinmux_drivepad_val &= 0x3FFFFFFF;
pinmux_drivepad_val |= (pinmux_drivepad_config_val & 0xC0000000);
}
}
}
/* Write to the appropriate PINMUX drive group register */
reg::Write(pinmux_drivepad_reg, pinmux_drivepad_val);
/* Do a dummy read from the PINMUX drive group register */
pinmux_drivepad_val = reg::Read(pinmux_drivepad_reg);
return pinmux_drivepad_val;
}
u32 DummyReadDrivePad(u32 pinmux_drivepad_name) {
const uintptr_t pinmux_base_vaddr = GetBaseAddress();
const DrivePadDefinition *pinmux_drivepad_def = GetDrivePadDefinition(pinmux_drivepad_name);
/* Fetch this PINMUX drive group's register offset */
u32 pinmux_drivepad_reg_offset = pinmux_drivepad_def->reg_offset;
/* Get current register ptr. */
uintptr_t pinmux_drivepad_reg = pinmux_base_vaddr + pinmux_drivepad_reg_offset;
return reg::Read(pinmux_drivepad_reg);
}
void UpdateAllParks() {
/* Update parks. */
for (size_t i = 0; i < PadNameMax; i++) {
UpdatePark(static_cast<u32>(i));
}
}
u32 elpdr_config_val = (((pinmux_config_val >> 0x0B) & 0x1) << 0x08);
/* Adjust ELpdr */
if (pinmux_config_mask_val & 0x800) {
/* This pin supports ELpdr change */
if (pinmux_mask_val & 0x100) {
/* Change ELpdr */
if (((pinmux_val >> 0x08) ^ (pinmux_config_val >> 0x0B)) & 0x01) {
pinmux_val &= 0xFFFFFEFF;
pinmux_val |= elpdr_config_val;
}
void DummyReadAllDrivePads() {
/* Dummy read all drive pads. */
for (size_t i = 0; i < DrivePadNameMax; i++) {
DummyReadDrivePad(static_cast<u32>(i));
}
}
u32 ehsm_config_val = (((pinmux_config_val >> 0x0C) & 0x1) << 0x09);
/* Adjust EHsm */
if (pinmux_config_mask_val & 0x1000) {
/* This pin supports EHsm change */
if (pinmux_mask_val & 0x200) {
/* Change EHsm */
if (((pinmux_val >> 0x09) ^ (pinmux_config_val >> 0x0C)) & 0x01) {
pinmux_val &= 0xFFFFFDFF;
pinmux_val |= ehsm_config_val;
}
}
}
u32 eiohv_config_val = (((pinmux_config_val >> 0x09) & 0x1) << 0x0A);
/* Adjust EIoHv */
if (pinmux_config_mask_val & 0x200) {
/* This pin supports EIoHv change */
if (pinmux_mask_val & 0x400) {
/* Change EIoHv */
if (((pinmux_val >> 0x0A) ^ (pinmux_config_val >> 0x09)) & 0x01) {
pinmux_val &= 0xFFFFFBFF;
pinmux_val |= eiohv_config_val;
}
}
}
u32 eschmt_config_val = (((pinmux_config_val >> 0x0D) & 0x1) << 0x0C);
/* Adjust ESchmt */
if (pinmux_config_mask_val & 0x2000) {
/* This pin supports ESchmt change */
if (pinmux_mask_val & 0x1000) {
/* Change ESchmt */
if (((pinmux_val >> 0x0C) ^ (pinmux_config_val >> 0x0D)) & 0x01) {
pinmux_val &= 0xFFFFEFFF;
pinmux_val |= eschmt_config_val;
}
}
}
u32 preemp_config_val = (((pinmux_config_val >> 0x10) & 0x1) << 0xF);
/* Adjust Preemp */
if (pinmux_config_mask_val & 0x10000) {
/* This pin supports Preemp change */
if (pinmux_mask_val & 0x8000) {
/* Change Preemp */
if (((pinmux_val >> 0x0F) ^ (pinmux_config_val >> 0x10)) & 0x01) {
pinmux_val &= 0xFFFF7FFF;
pinmux_val |= preemp_config_val;
}
}
}
u32 drvtype_config_val = (((pinmux_config_val >> 0x0E) & 0x3) << 0xD);
/* Adjust DrvType */
if (pinmux_config_mask_val & 0xC000) {
/* This pin supports DrvType change */
if (pinmux_mask_val & 0x6000) {
/* Change DrvType */
if (((pinmux_val >> 0x0D) ^ (pinmux_config_val >> 0x0E)) & 0x03) {
pinmux_val &= 0xFFFF9FFF;
pinmux_val |= drvtype_config_val;
}
}
}
/* Write to the appropriate PINMUX register */
*pinmux_reg = pinmux_val;
/* Do a dummy read from the PINMUX register */
pinmux_val = *pinmux_reg;
return pinmux_val;
}
u32 Boot::PinmuxUpdateDrivePad(u32 pinmux_drivepad_name, u32 pinmux_drivepad_config_val, u32 pinmux_drivepad_config_mask_val) {
const uintptr_t pinmux_base_vaddr = GetPinmuxBaseAddress();
const PinmuxDrivePadDefinition *pinmux_drivepad_def = GetPinmuxDrivePadDefinition(pinmux_drivepad_name);
/* Fetch this PINMUX drive group's register offset */
u32 pinmux_drivepad_reg_offset = pinmux_drivepad_def->reg_offset;
/* Fetch this PINMUX drive group's mask value */
u32 pinmux_drivepad_mask_val = pinmux_drivepad_def->mask_val;
/* Get current register ptr. */
volatile u32 *pinmux_drivepad_reg = reinterpret_cast<volatile u32 *>(pinmux_base_vaddr + pinmux_drivepad_reg_offset);
/* Read from the PINMUX drive group register */
u32 pinmux_drivepad_val = *pinmux_drivepad_reg;
/* Adjust DriveDownStrength */
if (pinmux_drivepad_config_mask_val & 0x1F000) {
u32 mask_val = 0x7F000;
/* Adjust mask value */
if ((pinmux_drivepad_mask_val & 0x7F000) != 0x7F000)
mask_val = 0x1F000;
/* This drive group supports DriveDownStrength change */
if (pinmux_drivepad_mask_val & mask_val) {
/* Change DriveDownStrength */
if (((pinmux_drivepad_config_val & 0x7F000) & mask_val) != (pinmux_drivepad_val & mask_val)) {
pinmux_drivepad_val &= ~(mask_val);
pinmux_drivepad_val |= ((pinmux_drivepad_config_val & 0x7F000) & mask_val);
}
}
}
/* Adjust DriveUpStrength */
if (pinmux_drivepad_config_mask_val & 0x1F00000) {
u32 mask_val = 0x7F00000;
/* Adjust mask value */
if ((pinmux_drivepad_mask_val & 0x7F00000) != 0x7F00000)
mask_val = 0x1F00000;
/* This drive group supports DriveUpStrength change */
if (pinmux_drivepad_mask_val & mask_val) {
/* Change DriveUpStrength */
if (((pinmux_drivepad_config_val & 0x7F00000) & mask_val) != (pinmux_drivepad_val & mask_val)) {
pinmux_drivepad_val &= ~(mask_val);
pinmux_drivepad_val |= ((pinmux_drivepad_config_val & 0x7F00000) & mask_val);
}
}
}
/* Adjust DriveDownSlew */
if (pinmux_drivepad_config_mask_val & 0x30000000) {
/* This drive group supports DriveDownSlew change */
if (pinmux_drivepad_mask_val & 0x30000000) {
/* Change DriveDownSlew */
if ((pinmux_drivepad_val ^ pinmux_drivepad_config_val) & 0x30000000) {
pinmux_drivepad_val &= 0xCFFFFFFF;
pinmux_drivepad_val |= (pinmux_drivepad_config_val & 0x30000000);
}
}
}
/* Adjust DriveUpSlew */
if (pinmux_drivepad_config_mask_val & 0xC0000000) {
/* This drive group supports DriveUpSlew change */
if (pinmux_drivepad_mask_val & 0xC0000000) {
/* Change DriveUpSlew */
if ((pinmux_drivepad_val ^ pinmux_drivepad_config_val) & 0xC0000000) {
pinmux_drivepad_val &= 0x3FFFFFFF;
pinmux_drivepad_val |= (pinmux_drivepad_config_val & 0xC0000000);
}
}
}
/* Write to the appropriate PINMUX drive group register */
*pinmux_drivepad_reg = pinmux_drivepad_val;
/* Do a dummy read from the PINMUX drive group register */
pinmux_drivepad_val = *pinmux_drivepad_reg;
return pinmux_drivepad_val;
}
u32 Boot::PinmuxDummyReadDrivePad(u32 pinmux_drivepad_name) {
const uintptr_t pinmux_base_vaddr = GetPinmuxBaseAddress();
const PinmuxDrivePadDefinition *pinmux_drivepad_def = GetPinmuxDrivePadDefinition(pinmux_drivepad_name);
/* Fetch this PINMUX drive group's register offset */
u32 pinmux_drivepad_reg_offset = pinmux_drivepad_def->reg_offset;
/* Get current register ptr. */
volatile u32 *pinmux_drivepad_reg = reinterpret_cast<volatile u32 *>(pinmux_base_vaddr + pinmux_drivepad_reg_offset);
return *pinmux_drivepad_reg;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot::pinmux {
/* Pinmux Utilities. */
u32 UpdatePark(u32 pinmux_name);
u32 UpdatePad(u32 pinmux_name, u32 config_val, u32 config_mask);
u32 UpdateDrivePad(u32 pinmux_drivepad_name, u32 config_val, u32 config_mask);
u32 DummyReadDrivePad(u32 pinmux_drivepad_name);
/* Helper API. */
void UpdateAllParks();
void DummyReadAllDrivePads();
}

View file

@ -14,44 +14,54 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_pmc_wrapper.hpp"
static constexpr u32 SmcFunctionId_AtmosphereReadWriteRegister = 0xF0000002;
namespace sts::boot {
static constexpr u32 PmcPhysStart = 0x7000E400;
static constexpr u32 PmcPhysEnd = 0x7000EFFF;
namespace {
static inline bool IsValidPmcAddress(u32 phys_addr) {
return (phys_addr & 3) == 0 && PmcPhysStart <= phys_addr && phys_addr <= PmcPhysEnd;
}
/* Convenience definitions. */
constexpr u32 SmcFunctionId_AtmosphereReadWriteRegister = 0xF0000002;
static inline u32 SmcAtmosphereReadWriteRegister(u32 phys_addr, u32 value, u32 mask) {
SecmonArgs args;
constexpr u32 PmcPhysStart = 0x7000E400;
constexpr u32 PmcPhysEnd = 0x7000EFFF;
/* Helpers. */
bool IsValidPmcAddress(u32 phys_addr) {
return (phys_addr & 3) == 0 && PmcPhysStart <= phys_addr && phys_addr <= PmcPhysEnd;
}
inline u32 SmcAtmosphereReadWriteRegister(u32 phys_addr, u32 value, u32 mask) {
SecmonArgs args;
args.X[0] = SmcFunctionId_AtmosphereReadWriteRegister;
args.X[1] = phys_addr;
args.X[2] = mask;
args.X[3] = value;
R_ASSERT(svcCallSecureMonitor(&args));
if (args.X[0] != 0) {
std::abort();
}
return static_cast<u32>(args.X[1]);
}
args.X[0] = SmcFunctionId_AtmosphereReadWriteRegister;
args.X[1] = phys_addr;
args.X[2] = mask;
args.X[3] = value;
svcCallSecureMonitor(&args);
if (args.X[0] != 0) {
std::abort();
}
return static_cast<u32>(args.X[1]);
}
u32 ReadPmcRegister(u32 phys_addr) {
if (!IsValidPmcAddress(phys_addr)) {
std::abort();
}
u32 Boot::ReadPmcRegister(u32 phys_addr) {
if (!IsValidPmcAddress(phys_addr)) {
std::abort();
return SmcAtmosphereReadWriteRegister(phys_addr, 0, 0);
}
return SmcAtmosphereReadWriteRegister(phys_addr, 0, 0);
}
void WritePmcRegister(u32 phys_addr, u32 value, u32 mask) {
if (!IsValidPmcAddress(phys_addr)) {
std::abort();
}
void Boot::WritePmcRegister(u32 phys_addr, u32 value, u32 mask) {
if (!IsValidPmcAddress(phys_addr)) {
std::abort();
SmcAtmosphereReadWriteRegister(phys_addr, value, mask);
}
SmcAtmosphereReadWriteRegister(phys_addr, value, mask);
}

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
/* PMC Access Utilities. */
u32 ReadPmcRegister(u32 phys_addr);
void WritePmcRegister(u32 phys_addr, u32 value, u32 mask = UINT32_MAX);
}

View file

@ -16,110 +16,103 @@
#include <switch.h>
#include <stratosphere.hpp>
#include "boot_functions.hpp"
#include "boot_i2c_utils.hpp"
#include "boot_pmic_driver.hpp"
void PmicDriver::ShutdownSystem() {
if (R_FAILED(this->ShutdownSystem(false))) {
std::abort();
namespace sts::boot {
void PmicDriver::ShutdownSystem() {
R_ASSERT(this->ShutdownSystem(false));
}
}
void PmicDriver::RebootSystem() {
if (R_FAILED(this->ShutdownSystem(true))) {
std::abort();
void PmicDriver::RebootSystem() {
R_ASSERT(this->ShutdownSystem(true));
}
}
Result PmicDriver::GetAcOk(bool *out) {
u8 power_status;
R_TRY(this->GetPowerStatus(&power_status));
*out = (power_status & 0x02) != 0;
return ResultSuccess;
}
Result PmicDriver::GetPowerIntr(u8 *out) {
const u8 addr = 0x0B;
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetPowerStatus(u8 *out) {
const u8 addr = 0x15;
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetNvErc(u8 *out) {
const u8 addr = 0x0C;
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetPowerButtonPressed(bool *out) {
u8 power_intr;
R_TRY(this->GetPowerIntr(&power_intr));
*out = (power_intr & 0x08) != 0;
return ResultSuccess;
}
Result 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;
if (R_FAILED(Boot::ReadI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr)))) {
std::abort();
Result PmicDriver::GetAcOk(bool *out) {
u8 power_status;
R_TRY(this->GetPowerStatus(&power_status));
*out = (power_status & 0x02) != 0;
return ResultSuccess;
}
if (reboot) {
on_off_2_val |= 0x80;
} else {
on_off_2_val &= ~0x80;
Result PmicDriver::GetPowerIntr(u8 *out) {
const u8 addr = 0x0B;
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
if (R_FAILED(Boot::WriteI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr)))) {
Result PmicDriver::GetPowerStatus(u8 *out) {
const u8 addr = 0x15;
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetNvErc(u8 *out) {
const u8 addr = 0x0C;
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetPowerButtonPressed(bool *out) {
u8 power_intr;
R_TRY(this->GetPowerIntr(&power_intr));
*out = (power_intr & 0x08) != 0;
return ResultSuccess;
}
Result 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_ASSERT(ReadI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr)));
if (reboot) {
on_off_2_val |= 0x80;
} else {
on_off_2_val &= ~0x80;
}
R_ASSERT(WriteI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr)));
/* Get value, set software reset mask. */
u8 on_off_1_val = 0;
R_ASSERT(ReadI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr)));
on_off_1_val |= 0x80;
/* Finalize the battery. */
{
BatteryDriver battery_driver;
this->FinalizeBattery(&battery_driver);
}
/* Actually write the value to trigger shutdown/reset. */
R_ASSERT(WriteI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &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);
std::abort();
}
/* Get value, set software reset mask. */
u8 on_off_1_val = 0;
if (R_FAILED(Boot::ReadI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr)))) {
std::abort();
}
on_off_1_val |= 0x80;
void PmicDriver::FinalizeBattery(BatteryDriver *battery_driver) {
/* Set shutdown timer. */
battery_driver->SetShutdownTimer();
/* Finalize the battery. */
{
BatteryDriver battery_driver;
this->FinalizeBattery(&battery_driver);
/* Get whether shutdown is enabled. */
bool shutdown_enabled;
if (R_FAILED(battery_driver->GetShutdownEnabled(&shutdown_enabled))) {
return;
}
bool ac_ok;
bool desired_shutdown_enabled;
if (R_FAILED(this->GetAcOk(&ac_ok)) || ac_ok) {
desired_shutdown_enabled = false;
} else {
desired_shutdown_enabled = true;
}
if (shutdown_enabled != desired_shutdown_enabled) {
battery_driver->SetShutdownEnabled(desired_shutdown_enabled);
}
}
/* Actually write the value to trigger shutdown/reset. */
if (R_FAILED(Boot::WriteI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr)))) {
std::abort();
}
/* Allow up to 5 seconds for shutdown/reboot to take place. */
svcSleepThread(5'000'000'000ul);
std::abort();
}
void PmicDriver::FinalizeBattery(BatteryDriver *battery_driver) {
/* Set shutdown timer. */
battery_driver->SetShutdownTimer();
/* Get whether shutdown is enabled. */
bool shutdown_enabled;
if (R_FAILED(battery_driver->GetShutdownEnabled(&shutdown_enabled))) {
return;
}
bool ac_ok;
bool desired_shutdown_enabled;
if (R_FAILED(this->GetAcOk(&ac_ok)) || ac_ok) {
desired_shutdown_enabled = false;
} else {
desired_shutdown_enabled = true;
}
if (shutdown_enabled != desired_shutdown_enabled) {
battery_driver->SetShutdownEnabled(desired_shutdown_enabled);
}
}

View file

@ -18,31 +18,36 @@
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c/driver/i2c_api.hpp"
#include "boot_battery_driver.hpp"
class PmicDriver {
private:
sts::i2c::driver::Session i2c_session;
public:
PmicDriver() {
sts::i2c::driver::Initialize();
sts::i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic);
}
namespace sts::boot {
/* Driver object. */
class PmicDriver {
private:
i2c::driver::Session i2c_session;
public:
PmicDriver() {
i2c::driver::Initialize();
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic);
}
~PmicDriver() {
i2c::driver::CloseSession(this->i2c_session);
i2c::driver::Finalize();
}
private:
Result GetPowerStatus(u8 *out);
Result ShutdownSystem(bool reboot);
void FinalizeBattery(BatteryDriver *battery_driver);
public:
void ShutdownSystem();
void RebootSystem();
Result GetAcOk(bool *out);
Result GetPowerIntr(u8 *out);
Result GetNvErc(u8 *out);
Result GetPowerButtonPressed(bool *out);
};
}
~PmicDriver() {
sts::i2c::driver::CloseSession(this->i2c_session);
sts::i2c::driver::Finalize();
}
private:
Result GetPowerStatus(u8 *out);
Result ShutdownSystem(bool reboot);
void FinalizeBattery(BatteryDriver *battery_driver);
public:
void ShutdownSystem();
void RebootSystem();
Result GetAcOk(bool *out);
Result GetPowerIntr(u8 *out);
Result GetNvErc(u8 *out);
Result GetPowerButtonPressed(bool *out);
};

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include <strings.h>
#include "boot_power_utils.hpp"
#include "fusee-primary_bin.h"
namespace sts::boot {
namespace {
/* Convenience definitions. */
constexpr uintptr_t IramBase = 0x40000000ull;
constexpr uintptr_t IramPayloadBase = 0x40010000ull;
constexpr size_t IramSize = 0x40000;
constexpr size_t IramPayloadMaxSize = 0x2E000;
/* Globals. */
u8 __attribute__ ((aligned (0x1000))) g_work_page[0x1000];
/* Helpers. */
void ClearIram() {
/* Make page FFs. */
memset(g_work_page, 0xFF, sizeof(g_work_page));
/* Overwrite all of IRAM with FFs. */
for (size_t ofs = 0; ofs < IramSize; ofs += sizeof(g_work_page)) {
CopyToIram(IramBase + ofs, g_work_page, sizeof(g_work_page));
}
}
void DoRebootToPayload(AtmosphereFatalErrorContext *ctx) {
/* Ensure clean IRAM state. */
ClearIram();
/* Copy in payload. */
for (size_t ofs = 0; ofs < fusee_primary_bin_size; ofs += 0x1000) {
std::memcpy(g_work_page, &fusee_primary_bin[ofs], std::min(static_cast<size_t>(fusee_primary_bin_size - ofs), size_t(0x1000)));
CopyToIram(IramPayloadBase + ofs, g_work_page, 0x1000);
}
/* Copy in fatal error context, if relevant. */
if (ctx != nullptr) {
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
std::memcpy(g_work_page, ctx, sizeof(*ctx));
CopyToIram(IramPayloadBase + IramPayloadMaxSize, g_work_page, sizeof(g_work_page));
}
RebootToIramPayload();
}
}
void RebootSystem() {
DoRebootToPayload(nullptr);
}
void RebootForFatalError(AtmosphereFatalErrorContext *ctx) {
DoRebootToPayload(ctx);
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
/* Power utilities. */
void RebootSystem();
void ShutdownSystem();
/* Atmosphere power utilities. */
void RebootForFatalError(AtmosphereFatalErrorContext *ctx);
}

View file

@ -1,75 +0,0 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include <strings.h>
#include "boot_functions.hpp"
#include "boot_reboot_manager.hpp"
#include "fusee-primary_bin.h"
static u8 g_work_page[0x1000] __attribute__ ((aligned (0x1000)));
static void ClearIram() {
/* Make page FFs. */
memset(g_work_page, 0xFF, sizeof(g_work_page));
/* Overwrite all of IRAM with FFs. */
for (size_t ofs = 0; ofs < IRAM_SIZE; ofs += sizeof(g_work_page)) {
CopyToIram(IRAM_BASE + ofs, g_work_page, sizeof(g_work_page));
}
}
static void DoRebootToPayload() {
/* Ensure clean IRAM state. */
ClearIram();
/* Copy in payload. */
for (size_t ofs = 0; ofs < fusee_primary_bin_size; ofs += 0x1000) {
std::memcpy(g_work_page, &fusee_primary_bin[ofs], std::min(static_cast<size_t>(fusee_primary_bin_size - ofs), size_t(0x1000)));
CopyToIram(IRAM_PAYLOAD_BASE + ofs, g_work_page, 0x1000);
}
RebootToIramPayload();
}
Result BootRebootManager::PerformReboot() {
DoRebootToPayload();
return ResultSuccess;
}
void BootRebootManager::RebootForFatalError(AtmosphereFatalErrorContext *ctx) {
/* Ensure clean IRAM state. */
ClearIram();
/* Copy in payload. */
for (size_t ofs = 0; ofs < fusee_primary_bin_size; ofs += 0x1000) {
std::memcpy(g_work_page, &fusee_primary_bin[ofs], std::min(static_cast<size_t>(fusee_primary_bin_size - ofs), size_t(0x1000)));
CopyToIram(IRAM_PAYLOAD_BASE + ofs, g_work_page, 0x1000);
}
std::memset(g_work_page, 0xCC, sizeof(g_work_page));
std::memcpy(g_work_page, ctx, sizeof(*ctx));
CopyToIram(IRAM_PAYLOAD_BASE + IRAM_PAYLOAD_MAX_SIZE, g_work_page, sizeof(g_work_page));
RebootToIramPayload();
}
void Boot::RebootSystem() {
if (R_FAILED(BootRebootManager::PerformReboot())) {
std::abort();
}
}

View file

@ -17,8 +17,6 @@
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr size_t CLK_RST_CONTROLLER_RST_SOURCE = 0x0;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_L = 0x4;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_H = 0x8;

View file

@ -19,8 +19,6 @@
#pragma once
#include <switch.h>
#include "boot_types.hpp"
#define DC_CMD_GENERAL_INCR_SYNCPT 0x00
#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x01

View file

@ -17,8 +17,6 @@
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr size_t GPIO_PORT3_CNF_0 = 0x200;
static constexpr size_t GPIO_PORT3_OE_0 = 0x210;
static constexpr size_t GPIO_PORT3_OUT_0 = 0x220;

View file

@ -17,8 +17,6 @@
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr size_t APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL = 0x8D4;
static constexpr size_t APB_MISC_GP_SDMMC3_CLK_LPBK_CONTROL = 0x8D8;
static constexpr size_t APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL = 0xA98;

View file

@ -17,8 +17,6 @@
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr uintptr_t PmcBase = 0x7000E400ul;
static constexpr size_t APBDEV_PMC_CNTRL = 0x0;

View file

@ -14,17 +14,29 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_power_utils.hpp"
#include "boot_repair_boot_images.hpp"
#include "boot_spl_utils.hpp"
#include "updater/updater_api.hpp"
static u8 __attribute__((__aligned__(0x1000))) g_boot_image_work_buffer[0x10000];
namespace sts::boot {
void Boot::CheckAndRepairBootImages() {
const auto boot_image_update_type = sts::updater::GetBootImageUpdateType(Boot::GetHardwareType());
namespace {
/* Globals. */
u8 __attribute__((aligned(0x1000))) g_boot_image_work_buffer[0x10000];
bool repaired_normal, repaired_safe;
if (R_SUCCEEDED(sts::updater::VerifyBootImagesAndRepairIfNeeded(&repaired_normal, &repaired_safe, g_boot_image_work_buffer, sizeof(g_boot_image_work_buffer), boot_image_update_type)) && repaired_normal) {
/* Nintendo only performs a reboot on successful normal repair. */
Boot::RebootSystem();
}
void CheckAndRepairBootImages() {
const auto boot_image_update_type = sts::updater::GetBootImageUpdateType(GetHardwareType());
bool repaired_normal, repaired_safe;
if (R_SUCCEEDED(sts::updater::VerifyBootImagesAndRepairIfNeeded(&repaired_normal, &repaired_safe, g_boot_image_work_buffer, sizeof(g_boot_image_work_buffer), boot_image_update_type)) && repaired_normal) {
/* Nintendo only performs a reboot on successful normal repair. */
RebootSystem();
}
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
void CheckAndRepairBootImages();
}

View file

@ -16,23 +16,27 @@
#include <switch.h>
#include <stratosphere.hpp>
#include "boot_functions.hpp"
#include "boot_rtc_driver.hpp"
Result RtcDriver::ReadRtcRegister(u8 *out, u8 address) {
const u8 update_addr = 0x04;
const u8 update_val = 0x10;
R_TRY(Boot::WriteI2cRegister(this->i2c_session, &update_val, sizeof(update_val), &update_addr, sizeof(update_addr)));
svcSleepThread(16'000'000ul);
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &address, sizeof(address));
}
namespace sts::boot {
Result RtcDriver::GetRtcIntr(u8 *out) {
const u8 addr = 0x00;
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result RtcDriver::ReadRtcRegister(u8 *out, u8 address) {
const u8 update_addr = 0x04;
const u8 update_val = 0x10;
R_TRY(WriteI2cRegister(this->i2c_session, &update_val, sizeof(update_val), &update_addr, sizeof(update_addr)));
svcSleepThread(16'000'000ul);
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &address, sizeof(address));
}
Result RtcDriver::GetRtcIntr(u8 *out) {
const u8 addr = 0x00;
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result RtcDriver::GetRtcIntrM(u8 *out) {
const u8 addr = 0x01;
return this->ReadRtcRegister(out, addr);
}
Result RtcDriver::GetRtcIntrM(u8 *out) {
const u8 addr = 0x01;
return this->ReadRtcRegister(out, addr);
}

View file

@ -18,24 +18,28 @@
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c/driver/i2c_api.hpp"
#include "boot_i2c_utils.hpp"
class RtcDriver {
private:
sts::i2c::driver::Session i2c_session;
public:
RtcDriver() {
sts::i2c::driver::Initialize();
sts::i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc);
}
namespace sts::boot {
~RtcDriver() {
sts::i2c::driver::CloseSession(this->i2c_session);
sts::i2c::driver::Finalize();
}
private:
Result ReadRtcRegister(u8 *out, u8 address);
public:
Result GetRtcIntr(u8 *out);
Result GetRtcIntrM(u8 *out);
};
class RtcDriver {
private:
i2c::driver::Session i2c_session;
public:
RtcDriver() {
i2c::driver::Initialize();
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc);
}
~RtcDriver() {
i2c::driver::CloseSession(this->i2c_session);
i2c::driver::Finalize();
}
private:
Result ReadRtcRegister(u8 *out, u8 address);
public:
Result GetRtcIntr(u8 *out);
Result GetRtcIntrM(u8 *out);
};
}

View file

@ -14,34 +14,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_spl_utils.hpp"
HardwareType Boot::GetHardwareType() {
u64 out_val = 0;
if (R_FAILED(splGetConfig(SplConfigItem_HardwareType, &out_val))) {
std::abort();
namespace sts::boot {
spl::HardwareType GetHardwareType() {
u64 out_val = 0;
R_ASSERT(splGetConfig(SplConfigItem_HardwareType, &out_val));
return static_cast<spl::HardwareType>(out_val);
}
return static_cast<HardwareType>(out_val);
bool IsRecoveryBoot() {
u64 val = 0;
R_ASSERT(splGetConfig(SplConfigItem_IsRecoveryBoot, &val));
return val != 0;
}
bool IsMariko() {
const auto hw_type = GetHardwareType();
switch (hw_type) {
case spl::HardwareType::Icosa:
case spl::HardwareType::Copper:
return false;
case spl::HardwareType::Hoag:
case spl::HardwareType::Iowa:
return true;
default:
std::abort();
}
}
}
bool Boot::IsRecoveryBoot() {
u64 val = 0;
if (R_FAILED(splGetConfig(SplConfigItem_IsRecoveryBoot, &val))) {
std::abort();
}
return val != 0;
}
bool Boot::IsMariko() {
HardwareType hw_type = GetHardwareType();
switch (hw_type) {
case HardwareType_Icosa:
case HardwareType_Copper:
return false;
case HardwareType_Hoag:
case HardwareType_Iowa:
return true;
default:
std::abort();
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::spl {
enum class HardwareType {
Icosa = 0,
Copper = 1,
Hoag = 2,
Iowa = 3,
};
}
namespace sts::boot {
/* SPL Utilities. */
spl::HardwareType GetHardwareType();
bool IsRecoveryBoot();
bool IsMariko();
}

View file

@ -14,21 +14,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_splash_screen_notext.hpp"
#include "boot_boot_reason.hpp"
#include "boot_display.hpp"
#include "boot_splash_screen.hpp"
namespace sts::boot {
namespace {
/* Include splash screen into anonymous namespace. */
/* TODO: Compile-time switch for splash_screen_text.hpp? */
#include "boot_splash_screen_notext.inc"
void Boot::ShowSplashScreen() {
const u32 boot_reason = Boot::GetBootReason();
if (boot_reason == 1 || boot_reason == 4) {
return;
}
Boot::InitializeDisplay();
{
/* Splash screen is shown for 2 seconds. */
Boot::ShowDisplay(SplashScreenX, SplashScreenY, SplashScreenW, SplashScreenH, SplashScreen);
svcSleepThread(2'000'000'000ul);
void ShowSplashScreen() {
const u32 boot_reason = GetBootReason();
if (boot_reason == 1 || boot_reason == 4) {
return;
}
InitializeDisplay();
{
/* Splash screen is shown for 2 seconds. */
ShowDisplay(SplashScreenX, SplashScreenY, SplashScreenW, SplashScreenH, SplashScreen);
svcSleepThread(2'000'000'000ul);
}
FinalizeDisplay();
}
Boot::FinalizeDisplay();
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
void ShowSplashScreen();
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -14,19 +14,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
#include "boot_registers_pmc.hpp"
struct WakeControlConfig {
u32 reg_offset;
u32 mask_val;
bool flag_val;
};
static constexpr WakeControlConfig WakeControlConfigs[] = {
constexpr WakeControlConfig WakeControlConfigs[] = {
{APBDEV_PMC_CNTRL, 0x0800, true},
{APBDEV_PMC_CNTRL, 0x0400, false},
{APBDEV_PMC_CNTRL, 0x0200, true},
@ -38,4 +32,4 @@ static constexpr WakeControlConfig WakeControlConfigs[] = {
{APBDEV_PMC_CNTRL2, 0x0001, true},
};
static constexpr size_t NumWakeControlConfigs = sizeof(WakeControlConfigs) / sizeof(WakeControlConfigs[0]);
constexpr size_t NumWakeControlConfigs = sizeof(WakeControlConfigs) / sizeof(WakeControlConfigs[0]);

View file

@ -14,11 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr WakePinConfig WakePinConfigs[] = {
{0x00, false, 0x02},
{0x01, false, 0x02},

View file

@ -14,11 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "boot_types.hpp"
static constexpr WakePinConfig WakePinConfigsCopper[] = {
{0x00, true, 0x02},
{0x01, false, 0x02},

View file

@ -14,75 +14,99 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "boot_functions.hpp"
#include "boot_pmc_wrapper.hpp"
#include "boot_spl_utils.hpp"
#include "boot_wake_pins.hpp"
#include "boot_registers_pmc.hpp"
#include "boot_wake_control_configs.hpp"
#include "boot_wake_pin_configuration.hpp"
#include "boot_wake_pin_configuration_copper.hpp"
static void UpdatePmcControlBit(const u32 reg_offset, const u32 mask_val, const bool flag) {
Boot::WritePmcRegister(PmcBase + reg_offset, flag ? UINT32_MAX : 0, mask_val);
Boot::ReadPmcRegister(PmcBase + reg_offset);
}
namespace sts::boot {
static void InitializePmcWakeConfiguration(const bool is_blink) {
/* Initialize APBDEV_PMC_WAKE_DEBOUNCE_EN, do a dummy read. */
Boot::WritePmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN, 0);
Boot::ReadPmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN);
/* Include configuration into anonymous namespace. */
namespace {
/* Initialize APBDEV_PMC_BLINK_TIMER, do a dummy read. */
Boot::WritePmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER, 0x8008800);
Boot::ReadPmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER);
struct WakePinConfig {
u32 index;
bool enabled;
u32 level;
};
#include "boot_wake_control_configs.inc"
#include "boot_wake_pin_configuration.inc"
#include "boot_wake_pin_configuration_copper.inc"
/* Set control bits, do dummy reads. */
for (size_t i = 0; i < NumWakeControlConfigs; i++) {
UpdatePmcControlBit(WakeControlConfigs[i].reg_offset, WakeControlConfigs[i].mask_val, WakeControlConfigs[i].flag_val);
}
/* Set bit 0x80 in APBDEV_PMC_CNTRL based on is_blink, do dummy read. */
UpdatePmcControlBit(APBDEV_PMC_CNTRL, 0x80, is_blink);
namespace {
/* Set bit 0x100000 in APBDEV_PMC_DPD_PADS_ORIDE based on is_blink, do dummy read. */
UpdatePmcControlBit(APBDEV_PMC_DPD_PADS_ORIDE, 0x100000, is_blink);
}
/* Helpers. */
void UpdatePmcControlBit(const u32 reg_offset, const u32 mask_val, const bool flag) {
WritePmcRegister(PmcBase + reg_offset, flag ? UINT32_MAX : 0, mask_val);
ReadPmcRegister(PmcBase + reg_offset);
}
void InitializePmcWakeConfiguration(const bool is_blink) {
/* Initialize APBDEV_PMC_WAKE_DEBOUNCE_EN, do a dummy read. */
WritePmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN, 0);
ReadPmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN);
/* Initialize APBDEV_PMC_BLINK_TIMER, do a dummy read. */
WritePmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER, 0x8008800);
ReadPmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER);
/* Set control bits, do dummy reads. */
for (size_t i = 0; i < NumWakeControlConfigs; i++) {
UpdatePmcControlBit(WakeControlConfigs[i].reg_offset, WakeControlConfigs[i].mask_val, WakeControlConfigs[i].flag_val);
}
/* Set bit 0x80 in APBDEV_PMC_CNTRL based on is_blink, do dummy read. */
UpdatePmcControlBit(APBDEV_PMC_CNTRL, 0x80, is_blink);
/* Set bit 0x100000 in APBDEV_PMC_DPD_PADS_ORIDE based on is_blink, do dummy read. */
UpdatePmcControlBit(APBDEV_PMC_DPD_PADS_ORIDE, 0x100000, is_blink);
}
void SetWakeEventLevel(u32 index, u32 level) {
u32 pmc_wake_level_reg_offset = index <= 0x1F ? APBDEV_PMC_WAKE_LVL : APBDEV_PMC_WAKE2_LVL;
u32 pmc_wake_level_mask_reg_offset = index <= 0x1F ? APBDEV_PMC_AUTO_WAKE_LVL_MASK : APBDEV_PMC_AUTO_WAKE2_LVL_MASK;
if (level != 2) {
std::swap(pmc_wake_level_reg_offset, pmc_wake_level_mask_reg_offset);
}
const u32 mask_val = (1 << (index & 0x1F));
/* Clear level reg bit. */
UpdatePmcControlBit(pmc_wake_level_reg_offset, mask_val, false);
/* Set or clear mask reg bit. */
UpdatePmcControlBit(pmc_wake_level_mask_reg_offset, mask_val, level > 0);
}
void SetWakeEventEnabled(u32 index, bool enabled) {
/* Set or clear enabled bit. */
UpdatePmcControlBit(index <= 0x1F ? APBDEV_PMC_WAKE_MASK : APBDEV_PMC_WAKE2_MASK, (1 << (index & 0x1F)), enabled);
}
void Boot::SetWakeEventLevel(u32 index, u32 level) {
u32 pmc_wake_level_reg_offset = index <= 0x1F ? APBDEV_PMC_WAKE_LVL : APBDEV_PMC_WAKE2_LVL;
u32 pmc_wake_level_mask_reg_offset = index <= 0x1F ? APBDEV_PMC_AUTO_WAKE_LVL_MASK : APBDEV_PMC_AUTO_WAKE2_LVL_MASK;
if (level != 2) {
std::swap(pmc_wake_level_reg_offset, pmc_wake_level_mask_reg_offset);
}
const u32 mask_val = (1 << (index & 0x1F));
void SetInitialWakePinConfiguration() {
InitializePmcWakeConfiguration(false);
/* Clear level reg bit. */
UpdatePmcControlBit(pmc_wake_level_reg_offset, mask_val, false);
/* Set wake event levels, wake event enables. */
const WakePinConfig *configs;
size_t num_configs;
if (GetHardwareType() == spl::HardwareType::Copper) {
configs = WakePinConfigsCopper;
num_configs = NumWakePinConfigsCopper;
} else {
configs = WakePinConfigs;
num_configs = NumWakePinConfigs;
}
/* Set or clear mask reg bit. */
UpdatePmcControlBit(pmc_wake_level_mask_reg_offset, mask_val, level > 0);
}
void Boot::SetWakeEventEnabled(u32 index, bool enabled) {
/* Set or clear enabled bit. */
UpdatePmcControlBit(index <= 0x1F ? APBDEV_PMC_WAKE_MASK : APBDEV_PMC_WAKE2_MASK, (1 << (index & 0x1F)), enabled);
}
void Boot::SetInitialWakePinConfiguration() {
InitializePmcWakeConfiguration(false);
/* Set wake event levels, wake event enables. */
const WakePinConfig *configs;
size_t num_configs;
if (Boot::GetHardwareType() == HardwareType_Copper) {
configs = WakePinConfigsCopper;
num_configs = NumWakePinConfigsCopper;
} else {
configs = WakePinConfigs;
num_configs = NumWakePinConfigs;
for (size_t i = 0; i < num_configs; i++) {
SetWakeEventLevel(configs[i].index, configs[i].level);
SetWakeEventEnabled(configs[i].index, configs[i].enabled);
}
}
for (size_t i = 0; i < num_configs; i++) {
Boot::SetWakeEventLevel(configs[i].index, configs[i].level);
Boot::SetWakeEventEnabled(configs[i].index, configs[i].enabled);
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
namespace sts::boot {
void SetInitialWakePinConfiguration();
}

View file

@ -129,8 +129,8 @@ namespace sts::i2c::driver::impl {
size_t remaining = num_bytes;
/* Set interrupt enable, clear interrupt status. */
WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8E);
WriteRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC);
reg::Write(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8E);
reg::Write(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC);
ON_SCOPE_EXIT { this->ClearInterruptMask(); };
@ -139,7 +139,7 @@ namespace sts::i2c::driver::impl {
/* Send bytes. */
while (true) {
const u32 fifo_status = ReadRegister(&this->i2c_registers->I2C_FIFO_STATUS_0);
const u32 fifo_status = reg::Read(&this->i2c_registers->I2C_FIFO_STATUS_0);
const size_t fifo_cnt = (fifo_status >> 4);
for (size_t fifo_idx = 0; remaining > 0 && fifo_idx < fifo_cnt; fifo_idx++) {
@ -148,7 +148,7 @@ namespace sts::i2c::driver::impl {
for (size_t i = 0; i < cur_bytes; i++) {
val |= cur_src[i] << (8 * i);
}
WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, val);
reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, val);
cur_src += cur_bytes;
remaining -= cur_bytes;
@ -168,14 +168,14 @@ namespace sts::i2c::driver::impl {
R_TRY(this->GetAndHandleTransactionResult());
}
WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8C);
reg::Write(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8C);
/* Wait for successful completion. */
while (true) {
R_TRY(this->GetAndHandleTransactionResult());
/* Check PACKET_XFER_COMPLETE */
const u32 interrupt_status = ReadRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0);
const u32 interrupt_status = reg::Read(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0);
if (interrupt_status & 0x80) {
R_TRY(this->GetAndHandleTransactionResult());
break;
@ -198,8 +198,8 @@ namespace sts::i2c::driver::impl {
size_t remaining = num_bytes;
/* Set interrupt enable, clear interrupt status. */
WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8D);
WriteRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC);
reg::Write(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8D);
reg::Write(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC);
/* Send header. */
this->WriteTransferHeader(TransferMode::Receive, option, addressing_mode, slave_address, num_bytes);
@ -216,11 +216,11 @@ namespace sts::i2c::driver::impl {
R_TRY(this->GetAndHandleTransactionResult());
const u32 fifo_status = ReadRegister(&this->i2c_registers->I2C_FIFO_STATUS_0);
const u32 fifo_status = reg::Read(&this->i2c_registers->I2C_FIFO_STATUS_0);
const size_t fifo_cnt = std::min((remaining + 3) >> 2, static_cast<size_t>(fifo_status & 0xF));
for (size_t fifo_idx = 0; remaining > 0 && fifo_idx < fifo_cnt; fifo_idx++) {
const u32 val = ReadRegister(&this->i2c_registers->I2C_I2C_RX_FIFO_0);
const u32 val = reg::Read(&this->i2c_registers->I2C_I2C_RX_FIFO_0);
const size_t cur_bytes = std::min(remaining, sizeof(u32));
for (size_t i = 0; i < cur_bytes; i++) {
cur_dst[i] = static_cast<u8>((val >> (8 * i)) & 0xFF);
@ -296,15 +296,15 @@ namespace sts::i2c::driver::impl {
}
if (speed_mode == SpeedMode::HighSpeed) {
WriteRegister(&this->i2c_registers->I2C_I2C_HS_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low));
WriteRegister(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, clk_div);
reg::Write(&this->i2c_registers->I2C_I2C_HS_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low));
reg::Write(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, clk_div);
} else {
WriteRegister(&this->i2c_registers->I2C_I2C_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low));
WriteRegister(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, (clk_div << 16));
reg::Write(&this->i2c_registers->I2C_I2C_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low));
reg::Write(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, (clk_div << 16));
}
WriteRegister(&this->i2c_registers->I2C_I2C_CNFG_0, debounce);
ReadRegister(&this->i2c_registers->I2C_I2C_CNFG_0);
reg::Write(&this->i2c_registers->I2C_I2C_CNFG_0, debounce);
reg::Read(&this->i2c_registers->I2C_I2C_CNFG_0);
if (this->pcv_module != PcvModule_I2C5) {
if (R_FAILED(pcv::SetReset(this->pcv_module, true))) {
@ -340,14 +340,14 @@ namespace sts::i2c::driver::impl {
this->ResetController();
WriteRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x90000);
SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x4);
SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x2);
reg::Write(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x90000);
reg::SetBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x4);
reg::SetBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x2);
SetRegisterBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1);
reg::SetBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1);
{
u64 start_tick = armGetSystemTick();
while (ReadRegister(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0) & 1) {
while (reg::Read(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0) & 1) {
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
success = false;
break;
@ -358,10 +358,10 @@ namespace sts::i2c::driver::impl {
continue;
}
SetRegisterBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x1);
reg::SetBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x1);
{
u64 start_tick = armGetSystemTick();
while (ReadRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0) & 1) {
while (reg::Read(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0) & 1) {
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
success = false;
break;
@ -374,7 +374,7 @@ namespace sts::i2c::driver::impl {
{
u64 start_tick = armGetSystemTick();
while (ReadRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_STATUS_0) & 1) {
while (reg::Read(&this->i2c_registers->I2C_I2C_BUS_CLEAR_STATUS_0) & 1) {
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
success = false;
break;
@ -395,19 +395,19 @@ namespace sts::i2c::driver::impl {
void BusAccessor::SetPacketMode() {
/* Set PACKET_MODE_EN, MSTR_CONFIG_LOAD */
SetRegisterBits(&this->i2c_registers->I2C_I2C_CNFG_0, 0x400);
SetRegisterBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1);
reg::SetBits(&this->i2c_registers->I2C_I2C_CNFG_0, 0x400);
reg::SetBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1);
/* Set TX_FIFO_TRIGGER, RX_FIFO_TRIGGER */
WriteRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFC);
reg::Write(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFC);
}
Result BusAccessor::FlushFifos() {
WriteRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFF);
reg::Write(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFF);
/* Wait for flush to finish, check every ms for 5 ms. */
for (size_t i = 0; i < 5; i++) {
if (!(ReadRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0) & 3)) {
if (!(reg::Read(&this->i2c_registers->I2C_FIFO_CONTROL_0) & 3)) {
return ResultSuccess;
}
svcSleepThread(1'000'000ul);
@ -417,8 +417,8 @@ namespace sts::i2c::driver::impl {
}
Result BusAccessor::GetTransactionResult() const {
const u32 packet_status = ReadRegister(&this->i2c_registers->I2C_PACKET_TRANSFER_STATUS_0);
const u32 interrupt_status = ReadRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0);
const u32 packet_status = reg::Read(&this->i2c_registers->I2C_PACKET_TRANSFER_STATUS_0);
const u32 interrupt_status = reg::Read(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0);
/* Check for no ack. */
if ((packet_status & 0xC) || (interrupt_status & 0x8)) {
@ -460,8 +460,8 @@ namespace sts::i2c::driver::impl {
void BusAccessor::WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes) {
this->FlushFifos();
WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, 0x10);
WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, static_cast<u32>(num_bytes - 1) & 0xFFF);
reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, 0x10);
reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, static_cast<u32>(num_bytes - 1) & 0xFFF);
const u32 slave_addr_val = ((transfer_mode == TransferMode::Receive) & 1) | ((slave_address & 0x7F) << 1);
u32 hdr_val = 0;
@ -472,7 +472,7 @@ namespace sts::i2c::driver::impl {
hdr_val |= (((option & I2cTransactionOption_Stop) == 0) & 1) << 16;
hdr_val |= slave_addr_val;
WriteRegister(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, hdr_val);
reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, hdr_val);
}
}

View file

@ -45,8 +45,8 @@ namespace sts::i2c::driver::impl {
BusAccessor() { /* ... */ }
private:
inline void ClearInterruptMask() const {
WriteRegister(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0);
ReadRegister(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0);
reg::Write(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0);
reg::Read(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0);
}
void SetBus(Bus bus);

View file

@ -17,6 +17,7 @@
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include <stratosphere/reg.hpp>
#include "i2c_driver_types.hpp"
@ -68,9 +69,9 @@ namespace sts::i2c::driver::impl {
struct ClkRstRegisters {
public:
volatile u32 *clk_src_reg;
volatile u32 *clk_en_reg;
volatile u32 *rst_reg;
uintptr_t clk_src_reg;
uintptr_t clk_en_reg;
uintptr_t rst_reg;
u32 mask;
public:
void SetBus(Bus bus) {
@ -89,9 +90,9 @@ namespace sts::i2c::driver::impl {
const uintptr_t registers = GetIoMapping(0x60006000ul, 0x1000);
const size_t idx = ConvertToIndex(bus);
this->clk_src_reg = reinterpret_cast<volatile u32 *>(registers + s_clk_src_offsets[idx]);
this->clk_en_reg = reinterpret_cast<volatile u32 *>(registers + s_clk_en_offsets[idx]);
this->rst_reg = reinterpret_cast<volatile u32 *>(registers + s_rst_offsets[idx]);
this->clk_src_reg = registers + s_clk_src_offsets[idx];
this->clk_en_reg = registers + s_clk_en_offsets[idx];
this->rst_reg = registers + s_rst_offsets[idx];
this->mask = (1u << s_bit_shifts[idx]);
}
};
@ -105,25 +106,4 @@ namespace sts::i2c::driver::impl {
return reinterpret_cast<Registers *>(registers);
}
inline void WriteRegister(volatile u32 *reg, u32 val) {
*reg = val;
}
inline u32 ReadRegister(volatile u32 *reg) {
u32 val = *reg;
return val;
}
inline void SetRegisterBits(volatile u32 *reg, u32 mask) {
*reg |= mask;
}
inline void ClearRegisterBits(volatile u32 *reg, u32 mask) {
*reg &= mask;
}
inline void ReadWriteRegisterBits(volatile u32 *reg, u32 val, u32 mask) {
*reg = (*reg & (~mask)) | (val & mask);
}
}

View file

@ -501,13 +501,13 @@ namespace sts::updater {
}
BootImageUpdateType GetBootImageUpdateType(HardwareType hw_type) {
BootImageUpdateType GetBootImageUpdateType(spl::HardwareType hw_type) {
switch (hw_type) {
case HardwareType_Icosa:
case HardwareType_Copper:
case spl::HardwareType::Icosa:
case spl::HardwareType::Copper:
return BootImageUpdateType::Erista;
case HardwareType_Hoag:
case HardwareType_Iowa:
case spl::HardwareType::Hoag:
case spl::HardwareType::Iowa:
return BootImageUpdateType::Mariko;
default:
std::abort();

View file

@ -23,7 +23,7 @@
namespace sts::updater {
/* Public API. */
BootImageUpdateType GetBootImageUpdateType(HardwareType hw_type);
BootImageUpdateType GetBootImageUpdateType(spl::HardwareType hw_type);
Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
}

View file

@ -19,7 +19,7 @@
#include <stratosphere.hpp>
/* TODO: Better way to do this? */
#include "../boot_types.hpp"
#include "../boot_spl_utils.hpp"
namespace sts::updater {

@ -1 +1 @@
Subproject commit 2e36c24a01f30f91873cfab208ffdfe13a18f097
Subproject commit 13e3f517cb5db1053e01fb21ec189728d5b9d6db