mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-15 11:33:32 +01:00
Merge pull request #592 from Atmosphere-NX/boot_refactor
Refactor boot sysmodule to use sts::boot namespace.
This commit is contained in:
commit
6bbece39bc
@ -33,7 +33,7 @@ endef
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source source/i2c_driver source/updater
|
||||
SOURCES := source source/i2c source/i2c/driver source/i2c/driver/impl source/gpio source/pinmux
|
||||
DATA := data
|
||||
INCLUDES := include ../../common/include
|
||||
EXEFS_SRC := exefs_src
|
||||
|
@ -14,14 +14,20 @@
|
||||
* 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 {
|
||||
|
||||
/* 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') {
|
||||
@ -44,12 +50,14 @@ const Max17050Parameters *BatteryDriver::GetBatteryParameters() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result BatteryDriver::Read(u8 addr, u16 *out) {
|
||||
return Boot::ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
|
||||
return 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));
|
||||
return WriteI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(&val), sizeof(val), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
Result BatteryDriver::ReadWrite(u8 addr, u16 mask, u16 val) {
|
||||
@ -287,3 +295,5 @@ Result BatteryDriver::GetShutdownEnabled(bool *out) {
|
||||
Result BatteryDriver::SetShutdownEnabled(bool enabled) {
|
||||
return this->ReadWrite(Max17050Config, 0x0040, enabled ? 0x0040 : 0x0000);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,25 +18,24 @@
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "i2c_driver/i2c_api.hpp"
|
||||
#include "boot_battery_parameters.hpp"
|
||||
#include "i2c/driver/i2c_api.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
class BatteryDriver {
|
||||
private:
|
||||
I2cSessionImpl i2c_session;
|
||||
i2c::driver::Session i2c_session;
|
||||
public:
|
||||
BatteryDriver() {
|
||||
I2cDriver::Initialize();
|
||||
I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max17050);
|
||||
i2c::driver::Initialize();
|
||||
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max17050);
|
||||
}
|
||||
|
||||
~BatteryDriver() {
|
||||
I2cDriver::CloseSession(this->i2c_session);
|
||||
I2cDriver::Finalize();
|
||||
i2c::driver::CloseSession(this->i2c_session);
|
||||
i2c::driver::Finalize();
|
||||
}
|
||||
private:
|
||||
static const Max17050Parameters *GetBatteryParameters();
|
||||
|
||||
Result Read(u8 addr, u16 *out_data);
|
||||
Result Write(u8 addr, u16 val);
|
||||
Result ReadWrite(u8 addr, u16 mask, u16 val);
|
||||
@ -62,3 +61,5 @@ class BatteryDriver {
|
||||
Result GetShutdownEnabled(bool *out);
|
||||
Result SetShutdownEnabled(bool enabled);
|
||||
};
|
||||
|
||||
}
|
||||
|
29
stratosphere/boot/source/boot_battery_icon_charging.inc
Normal file
29
stratosphere/boot/source/boot_battery_icon_charging.inc
Normal file
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
23
stratosphere/boot/source/boot_battery_icon_low.inc
Normal file
23
stratosphere/boot/source/boot_battery_icon_low.inc
Normal file
File diff suppressed because one or more lines are too long
@ -14,22 +14,20 @@
|
||||
* 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) {
|
||||
namespace {
|
||||
|
||||
/* Pull in icon definitions. */
|
||||
#include "boot_battery_icon_low.inc"
|
||||
#include "boot_battery_icon_charging.inc"
|
||||
#include "boot_battery_icon_charging_red.inc"
|
||||
|
||||
/* Helpers. */
|
||||
void FillBatteryMeter(u32 *icon, const size_t icon_w, const size_t icon_h, const size_t meter_x, const size_t meter_y, const size_t meter_w, const size_t meter_h, const size_t fill_w) {
|
||||
const size_t fill_x = meter_x + meter_w - fill_w;
|
||||
|
||||
if (fill_x + fill_w > icon_w || meter_y + meter_h > icon_h || fill_x == 0) {
|
||||
@ -49,7 +47,19 @@ static void FillBatteryMeter(u32 *icon, const size_t icon_w, const size_t icon_h
|
||||
}
|
||||
}
|
||||
|
||||
void Boot::StartShowChargingIcon(size_t battery_percentage, bool wait) {
|
||||
}
|
||||
|
||||
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;
|
||||
@ -68,8 +78,8 @@ void Boot::StartShowChargingIcon(size_t battery_percentage, bool wait) {
|
||||
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);
|
||||
InitializeDisplay();
|
||||
ShowDisplay(IconX, IconY, IconW, IconH, Icon);
|
||||
}
|
||||
|
||||
/* Wait for 2 seconds if we're supposed to. */
|
||||
@ -78,6 +88,8 @@ void Boot::StartShowChargingIcon(size_t battery_percentage, bool wait) {
|
||||
}
|
||||
}
|
||||
|
||||
void Boot::EndShowChargingIcon() {
|
||||
Boot::FinalizeDisplay();
|
||||
void EndShowChargingIcon() {
|
||||
FinalizeDisplay();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
}
|
@ -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 */
|
@ -14,13 +14,17 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "boot_functions.hpp"
|
||||
#include <stratosphere/spl.hpp>
|
||||
|
||||
#include "boot_boot_reason.hpp"
|
||||
#include "boot_pmic_driver.hpp"
|
||||
#include "boot_rtc_driver.hpp"
|
||||
|
||||
static u32 g_boot_reason = 0;
|
||||
static bool g_detected_boot_reason = false;
|
||||
namespace sts::boot {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Types. */
|
||||
struct BootReasonValue {
|
||||
union {
|
||||
struct {
|
||||
@ -33,7 +37,12 @@ struct BootReasonValue {
|
||||
};
|
||||
};
|
||||
|
||||
static u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
|
||||
/* 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;
|
||||
}
|
||||
@ -44,7 +53,7 @@ static u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
|
||||
return 1;
|
||||
}
|
||||
if (rtc_intr & 0x04) {
|
||||
if (nv_erc != 0x80 && !Boot::IsRecoveryBoot()) {
|
||||
if (nv_erc != 0x80 && !spl::IsRecoveryBoot()) {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
@ -54,7 +63,9 @@ static u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Boot::DetectBootReason() {
|
||||
}
|
||||
|
||||
void DetectBootReason() {
|
||||
u8 power_intr;
|
||||
u8 rtc_intr;
|
||||
u8 rtc_intr_m;
|
||||
@ -104,10 +115,12 @@ void Boot::DetectBootReason() {
|
||||
g_detected_boot_reason = true;
|
||||
}
|
||||
|
||||
u32 Boot::GetBootReason() {
|
||||
u32 GetBootReason() {
|
||||
if (!g_detected_boot_reason) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return g_boot_reason;
|
||||
}
|
||||
|
||||
}
|
@ -18,16 +18,10 @@
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
/* pcv isn't alive at the time boot runs, but nn::i2c::driver needs nn::pcv. */
|
||||
/* These are the overrides N puts in boot. */
|
||||
namespace sts::boot {
|
||||
|
||||
class Pcv {
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Finalize();
|
||||
static Result SetClockRate(PcvModule module, u32 hz);
|
||||
static Result SetClockEnabled(PcvModule module, bool enabled);
|
||||
static Result SetVoltageEnabled(u32 domain, bool enabled);
|
||||
static Result SetVoltageValue(u32 domain, u32 voltage);
|
||||
static Result SetReset(PcvModule module, bool reset);
|
||||
};
|
||||
/* Boot Reason utilities. */
|
||||
void DetectBootReason();
|
||||
u32 GetBootReason();
|
||||
|
||||
}
|
@ -14,20 +14,19 @@
|
||||
* 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),
|
||||
@ -35,10 +34,10 @@ enum ChargerConfiguration : u8 {
|
||||
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) {
|
||||
inline u8 EncodeChargeVoltageLimit(u32 voltage) {
|
||||
if (voltage < ChargeVoltageLimitMin || voltage > ChargeVoltageLimitMax) {
|
||||
std::abort();
|
||||
}
|
||||
@ -47,14 +46,14 @@ static inline u8 EncodeChargeVoltageLimit(u32 voltage) {
|
||||
return static_cast<u8>(voltage << 2);
|
||||
}
|
||||
|
||||
static inline u32 DecodeChargeVoltageLimit(u8 reg) {
|
||||
inline u32 DecodeChargeVoltageLimit(u8 reg) {
|
||||
return ChargeVoltageLimitMin + (static_cast<u32>(reg & 0xFC) << 2);
|
||||
}
|
||||
|
||||
static constexpr u32 FastChargeCurrentLimitMin = 512;
|
||||
static constexpr u32 FastChargeCurrentLimitMax = 4544;
|
||||
constexpr u32 FastChargeCurrentLimitMin = 512;
|
||||
constexpr u32 FastChargeCurrentLimitMax = 4544;
|
||||
|
||||
static inline u8 EncodeFastChargeCurrentLimit(u32 current) {
|
||||
inline u8 EncodeFastChargeCurrentLimit(u32 current) {
|
||||
if (current < FastChargeCurrentLimitMin || current > FastChargeCurrentLimitMax) {
|
||||
std::abort();
|
||||
}
|
||||
@ -63,7 +62,7 @@ static inline u8 EncodeFastChargeCurrentLimit(u32 current) {
|
||||
return static_cast<u8>(current << 2);
|
||||
}
|
||||
|
||||
static inline u32 DecodeFastChargeCurrentLimit(u8 reg) {
|
||||
inline u32 DecodeFastChargeCurrentLimit(u8 reg) {
|
||||
return FastChargeCurrentLimitMin + (static_cast<u32>(reg & 0xFC) << 4);
|
||||
}
|
||||
|
||||
@ -78,10 +77,10 @@ enum InputCurrentLimit : u8 {
|
||||
InputCurrentLimit_3000mA = 7,
|
||||
};
|
||||
|
||||
static constexpr u32 PreChargeCurrentLimitMin = 128;
|
||||
static constexpr u32 PreChargeCurrentLimitMax = 2048;
|
||||
constexpr u32 PreChargeCurrentLimitMin = 128;
|
||||
constexpr u32 PreChargeCurrentLimitMax = 2048;
|
||||
|
||||
static inline u8 EncodePreChargeCurrentLimit(u32 current) {
|
||||
inline u8 EncodePreChargeCurrentLimit(u32 current) {
|
||||
if (current < PreChargeCurrentLimitMin || current > PreChargeCurrentLimitMax) {
|
||||
std::abort();
|
||||
}
|
||||
@ -90,14 +89,14 @@ static inline u8 EncodePreChargeCurrentLimit(u32 current) {
|
||||
return static_cast<u8>(current << 4);
|
||||
}
|
||||
|
||||
static inline u32 DecodePreChargeCurrentLimit(u8 reg) {
|
||||
inline u32 DecodePreChargeCurrentLimit(u8 reg) {
|
||||
return PreChargeCurrentLimitMin + (static_cast<u32>(reg & 0xF0) << 3);
|
||||
}
|
||||
|
||||
static constexpr u32 TerminationCurrentLimitMin = 128;
|
||||
static constexpr u32 TerminationCurrentLimitMax = 2048;
|
||||
constexpr u32 TerminationCurrentLimitMin = 128;
|
||||
constexpr u32 TerminationCurrentLimitMax = 2048;
|
||||
|
||||
static inline u8 EncodeTerminationCurrentLimit(u32 current) {
|
||||
inline u8 EncodeTerminationCurrentLimit(u32 current) {
|
||||
if (current < TerminationCurrentLimitMin || current > TerminationCurrentLimitMax) {
|
||||
std::abort();
|
||||
}
|
||||
@ -106,14 +105,14 @@ static inline u8 EncodeTerminationCurrentLimit(u32 current) {
|
||||
return static_cast<u8>(current);
|
||||
}
|
||||
|
||||
static inline u32 DecodeTerminationCurrentLimit(u8 reg) {
|
||||
inline u32 DecodeTerminationCurrentLimit(u8 reg) {
|
||||
return TerminationCurrentLimitMin + (static_cast<u32>(reg & 0xF) << 7);
|
||||
}
|
||||
|
||||
static constexpr u32 MinimumSystemVoltageLimitMin = 3000;
|
||||
static constexpr u32 MinimumSystemVoltageLimitMax = 3700;
|
||||
constexpr u32 MinimumSystemVoltageLimitMin = 3000;
|
||||
constexpr u32 MinimumSystemVoltageLimitMax = 3700;
|
||||
|
||||
static inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
|
||||
inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
|
||||
if (voltage < MinimumSystemVoltageLimitMin || voltage > MinimumSystemVoltageLimitMax) {
|
||||
std::abort();
|
||||
}
|
||||
@ -122,7 +121,7 @@ static inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
|
||||
return static_cast<u8>(voltage << 1);
|
||||
}
|
||||
|
||||
static inline u32 DecodeMinimumSystemVoltageLimit(u8 reg) {
|
||||
inline u32 DecodeMinimumSystemVoltageLimit(u8 reg) {
|
||||
return MinimumSystemVoltageLimitMin + (static_cast<u32>(reg & 0x0E) * 50);
|
||||
}
|
||||
|
||||
@ -137,3 +136,5 @@ enum BoostModeCurrentLimit : u8 {
|
||||
BoostModeCurrentLimit_500mA = 0,
|
||||
BoostModeCurrentLimit_1300mA = 1,
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -14,20 +14,26 @@
|
||||
* 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] = {
|
||||
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
|
||||
};
|
||||
@ -45,15 +51,15 @@ u16 Boot::GetCrc16(const void *data, size_t size) {
|
||||
return crc16;
|
||||
}
|
||||
|
||||
static Result ValidateCalibrationCrc16(const void *data, size_t size) {
|
||||
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)]))) {
|
||||
if (GetCrc16(data, size - sizeof(u16)) != *(reinterpret_cast<const u16 *>(&data_u8[size - sizeof(u16)]))) {
|
||||
return ResultCalInvalidCrc;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static Result GetBatteryVendorImpl(u32 *vendor) {
|
||||
Result GetBatteryVendorImpl(u32 *vendor) {
|
||||
FsStorage s;
|
||||
R_TRY(fsOpenBisStorage(&s, FsBisStorageId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(&s); };
|
||||
@ -67,7 +73,7 @@ static Result GetBatteryVendorImpl(u32 *vendor) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static Result GetBatteryVersionImpl(u32 *version) {
|
||||
Result GetBatteryVersionImpl(u32 *version) {
|
||||
FsStorage s;
|
||||
R_TRY(fsOpenBisStorage(&s, FsBisStorageId_CalibrationBinary));
|
||||
ON_SCOPE_EXIT { fsStorageClose(&s); };
|
||||
@ -81,7 +87,9 @@ static Result GetBatteryVersionImpl(u32 *version) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
u32 Boot::GetBatteryVendor() {
|
||||
}
|
||||
|
||||
u32 GetBatteryVendor() {
|
||||
u32 vendor;
|
||||
if (R_FAILED(GetBatteryVendorImpl(&vendor))) {
|
||||
return DefaultBatteryVendor;
|
||||
@ -89,10 +97,12 @@ u32 Boot::GetBatteryVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
u32 Boot::GetBatteryVersion() {
|
||||
u32 GetBatteryVersion() {
|
||||
u32 version;
|
||||
if (R_FAILED(GetBatteryVersionImpl(&version))) {
|
||||
return DefaultBatteryVersion;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
}
|
||||
|
27
stratosphere/boot/source/boot_calibration.hpp
Normal file
27
stratosphere/boot/source/boot_calibration.hpp
Normal 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();
|
||||
|
||||
}
|
@ -14,19 +14,27 @@
|
||||
* 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() {
|
||||
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);
|
||||
@ -34,3 +42,5 @@ void Boot::ChangeGpioVoltageTo1_8v() {
|
||||
/* Sleep for 100 us. */
|
||||
svcSleepThread(100'000ul);
|
||||
}
|
||||
|
||||
}
|
||||
|
25
stratosphere/boot/source/boot_change_voltage.hpp
Normal file
25
stratosphere/boot/source/boot_change_voltage.hpp
Normal 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();;
|
||||
|
||||
}
|
@ -16,14 +16,17 @@
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "boot_charger_driver.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
Result ChargerDriver::Read(u8 addr, u8 *out) {
|
||||
return Boot::ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
|
||||
return ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
Result ChargerDriver::Write(u8 addr, u8 val) {
|
||||
return Boot::WriteI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(&val), sizeof(val), &addr, sizeof(addr));
|
||||
return WriteI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(&val), sizeof(val), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
Result ChargerDriver::ReadWrite(u8 addr, u8 mask, u8 val) {
|
||||
@ -41,7 +44,7 @@ Result ChargerDriver::Initialize() {
|
||||
|
||||
Result ChargerDriver::Initialize(bool set_input_current_limit) {
|
||||
if (set_input_current_limit) {
|
||||
R_TRY(this->SetInputCurrentLimit(InputCurrentLimit_500mA));
|
||||
R_TRY(this->SetInputCurrentLimit(bq24193::InputCurrentLimit_500mA));
|
||||
}
|
||||
|
||||
R_TRY(this->SetChargeVoltageLimit(4208));
|
||||
@ -50,82 +53,84 @@ Result ChargerDriver::Initialize(bool set_input_current_limit) {
|
||||
R_TRY(this->SetPreChargeCurrentLimit(128));
|
||||
R_TRY(this->SetTerminationCurrentLimit(128));
|
||||
R_TRY(this->SetMinimumSystemVoltageLimit(3000));
|
||||
R_TRY(this->SetWatchdogTimerSetting(WatchdogTimerSetting_Disabled));
|
||||
R_TRY(this->SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting_Disabled));
|
||||
R_TRY(this->SetChargingSafetyTimerEnabled(false));
|
||||
R_TRY(this->ResetWatchdogTimer());
|
||||
R_TRY(this->SetBoostModeCurrentLimit(BoostModeCurrentLimit_500mA));
|
||||
R_TRY(this->SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit_500mA));
|
||||
R_TRY(this->SetHiZEnabled(false));
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetChargeEnabled(bool enabled) {
|
||||
Boot::GpioSetValue(GpioPadName_Bq24193Charger, enabled ? GpioValue_Low : GpioValue_High);
|
||||
return this->SetChargerConfiguration(ChargerConfiguration_ChargeBattery);
|
||||
gpio::SetValue(GpioPadName_Bq24193Charger, enabled ? GpioValue_Low : GpioValue_High);
|
||||
return this->SetChargerConfiguration(bq24193::ChargerConfiguration_ChargeBattery);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetChargerConfiguration(ChargerConfiguration config) {
|
||||
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x30, config);
|
||||
Result ChargerDriver::SetChargerConfiguration(bq24193::ChargerConfiguration config) {
|
||||
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x30, config);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetChargeVoltageLimit(u32 voltage) {
|
||||
return this->ReadWrite(Bq24193ChargeVoltageControl, 0xFC, EncodeChargeVoltageLimit(voltage));
|
||||
return this->ReadWrite(bq24193::ChargeVoltageControl, 0xFC, bq24193::EncodeChargeVoltageLimit(voltage));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetFastChargeCurrentLimit(u32 current) {
|
||||
return this->ReadWrite(Bq24193ChargeCurrentControl, 0xFC, EncodeFastChargeCurrentLimit(current));
|
||||
return this->ReadWrite(bq24193::ChargeCurrentControl, 0xFC, bq24193::EncodeFastChargeCurrentLimit(current));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetInputCurrentLimit(InputCurrentLimit current) {
|
||||
return this->ReadWrite(Bq24193InputSourceControl, 0x07, current);
|
||||
Result ChargerDriver::SetInputCurrentLimit(bq24193::InputCurrentLimit current) {
|
||||
return this->ReadWrite(bq24193::InputSourceControl, 0x07, current);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetForce20PercentChargeCurrent(bool force) {
|
||||
return this->ReadWrite(Bq24193ChargeCurrentControl, 0x01, force ? 1 : 0);
|
||||
return this->ReadWrite(bq24193::ChargeCurrentControl, 0x01, force ? 1 : 0);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetPreChargeCurrentLimit(u32 current) {
|
||||
return this->ReadWrite(Bq24193PreChargeTerminationCurrentControl, 0xF0, EncodePreChargeCurrentLimit(current));
|
||||
return this->ReadWrite(bq24193::PreChargeTerminationCurrentControl, 0xF0, bq24193::EncodePreChargeCurrentLimit(current));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetTerminationCurrentLimit(u32 current) {
|
||||
return this->ReadWrite(Bq24193PreChargeTerminationCurrentControl, 0x0F, EncodeTerminationCurrentLimit(current));
|
||||
return this->ReadWrite(bq24193::PreChargeTerminationCurrentControl, 0x0F, bq24193::EncodeTerminationCurrentLimit(current));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetMinimumSystemVoltageLimit(u32 voltage) {
|
||||
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x0E, EncodeMinimumSystemVoltageLimit(voltage));
|
||||
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x0E, bq24193::EncodeMinimumSystemVoltageLimit(voltage));
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetWatchdogTimerSetting(WatchdogTimerSetting setting) {
|
||||
return this->ReadWrite(Bq24193ChargeTerminationTimerControl, 0x30, setting);
|
||||
Result ChargerDriver::SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting setting) {
|
||||
return this->ReadWrite(bq24193::ChargeTerminationTimerControl, 0x30, setting);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetChargingSafetyTimerEnabled(bool enabled) {
|
||||
return this->ReadWrite(Bq24193ChargeTerminationTimerControl, 0x08, enabled ? 0x08 : 0);
|
||||
return this->ReadWrite(bq24193::ChargeTerminationTimerControl, 0x08, enabled ? 0x08 : 0);
|
||||
}
|
||||
|
||||
Result ChargerDriver::ResetWatchdogTimer() {
|
||||
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x40, 0x40);
|
||||
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x40, 0x40);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetBoostModeCurrentLimit(BoostModeCurrentLimit current) {
|
||||
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x01, current);
|
||||
Result ChargerDriver::SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit current) {
|
||||
return this->ReadWrite(bq24193::PowerOnConfiguration, 0x01, current);
|
||||
}
|
||||
|
||||
Result ChargerDriver::SetHiZEnabled(bool enabled) {
|
||||
return this->ReadWrite(Bq24193InputSourceControl, 0x80, enabled ? 0x80 : 0);
|
||||
return this->ReadWrite(bq24193::InputSourceControl, 0x80, enabled ? 0x80 : 0);
|
||||
}
|
||||
|
||||
Result ChargerDriver::GetInputCurrentLimit(InputCurrentLimit *out) {
|
||||
Result ChargerDriver::GetInputCurrentLimit(bq24193::InputCurrentLimit *out) {
|
||||
u8 limit;
|
||||
R_TRY(this->Read(Bq24193InputSourceControl, &limit));
|
||||
*out = static_cast<InputCurrentLimit>(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(Bq24193ChargeVoltageControl, ®));
|
||||
*out = DecodeChargeVoltageLimit(reg);
|
||||
R_TRY(this->Read(bq24193::ChargeVoltageControl, ®));
|
||||
*out = bq24193::DecodeChargeVoltageLimit(reg);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
}
|
@ -18,42 +18,45 @@
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "i2c_driver/i2c_api.hpp"
|
||||
#include "boot_functions.hpp"
|
||||
#include "boot_bq24193_charger.hpp"
|
||||
#include "boot_i2c_utils.hpp"
|
||||
|
||||
#include "gpio/gpio_utils.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
class ChargerDriver {
|
||||
private:
|
||||
static constexpr u32 GpioPadName_Bq24193Charger = 0xA;
|
||||
private:
|
||||
I2cSessionImpl i2c_session;
|
||||
i2c::driver::Session i2c_session;
|
||||
public:
|
||||
ChargerDriver() {
|
||||
I2cDriver::Initialize();
|
||||
I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Bq24193);
|
||||
i2c::driver::Initialize();
|
||||
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Bq24193);
|
||||
|
||||
Boot::GpioConfigure(GpioPadName_Bq24193Charger);
|
||||
Boot::GpioSetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output);
|
||||
gpio::Configure(GpioPadName_Bq24193Charger);
|
||||
gpio::SetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output);
|
||||
}
|
||||
|
||||
~ChargerDriver() {
|
||||
I2cDriver::CloseSession(this->i2c_session);
|
||||
I2cDriver::Finalize();
|
||||
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);
|
||||
|
||||
Result SetInputCurrentLimit(InputCurrentLimit current);
|
||||
Result SetInputCurrentLimit(bq24193::InputCurrentLimit current);
|
||||
Result SetForce20PercentChargeCurrent(bool force);
|
||||
Result SetPreChargeCurrentLimit(u32 current);
|
||||
Result SetTerminationCurrentLimit(u32 current);
|
||||
Result SetMinimumSystemVoltageLimit(u32 voltage);
|
||||
Result SetWatchdogTimerSetting(WatchdogTimerSetting setting);
|
||||
Result SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting setting);
|
||||
Result SetChargingSafetyTimerEnabled(bool enabled);
|
||||
Result ResetWatchdogTimer();
|
||||
Result SetBoostModeCurrentLimit(BoostModeCurrentLimit current);
|
||||
Result SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit current);
|
||||
Result SetHiZEnabled(bool enabled);
|
||||
|
||||
public:
|
||||
@ -62,7 +65,9 @@ class ChargerDriver {
|
||||
Result SetChargeVoltageLimit(u32 voltage);
|
||||
Result SetFastChargeCurrentLimit(u32 current);
|
||||
Result SetChargeEnabled(bool enabled);
|
||||
Result SetChargerConfiguration(ChargerConfiguration config);
|
||||
Result GetInputCurrentLimit(InputCurrentLimit *out);
|
||||
Result SetChargerConfiguration(bq24193::ChargerConfiguration config);
|
||||
Result GetInputCurrentLimit(bq24193::InputCurrentLimit *out);
|
||||
Result GetChargeVoltageLimit(u32 *out);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -14,15 +14,24 @@
|
||||
* 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 {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Types. */
|
||||
enum class CheckBatteryResult {
|
||||
Success,
|
||||
Shutdown,
|
||||
Reboot,
|
||||
};
|
||||
|
||||
struct BatteryChargeParameters {
|
||||
@ -40,7 +49,8 @@ struct BatteryChargeParameters {
|
||||
u32 fast_charge_current_limit_low_voltage;
|
||||
};
|
||||
|
||||
static constexpr BatteryChargeParameters BatteryChargeParameters0 = {
|
||||
/* Battery parameters. */
|
||||
constexpr BatteryChargeParameters BatteryChargeParameters0 = {
|
||||
.temp_min = 4,
|
||||
.temp_low = 17,
|
||||
.temp_high = 51,
|
||||
@ -54,7 +64,7 @@ static constexpr BatteryChargeParameters BatteryChargeParameters0 = {
|
||||
.fast_charge_current_limit_low_voltage = 0x200,
|
||||
};
|
||||
|
||||
static constexpr BatteryChargeParameters BatteryChargeParameters1 = {
|
||||
constexpr BatteryChargeParameters BatteryChargeParameters1 = {
|
||||
.temp_min = 4,
|
||||
.temp_low = 17,
|
||||
.temp_high = 51,
|
||||
@ -68,7 +78,7 @@ static constexpr BatteryChargeParameters BatteryChargeParameters1 = {
|
||||
.fast_charge_current_limit_low_voltage = 0x600,
|
||||
};
|
||||
|
||||
static constexpr BatteryChargeParameters BatteryChargeParameters2 = {
|
||||
constexpr BatteryChargeParameters BatteryChargeParameters2 = {
|
||||
.temp_min = 4,
|
||||
.temp_low = 17,
|
||||
.temp_high = 51,
|
||||
@ -82,7 +92,7 @@ static constexpr BatteryChargeParameters BatteryChargeParameters2 = {
|
||||
.fast_charge_current_limit_low_voltage = 0x680,
|
||||
};
|
||||
|
||||
static const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_version) {
|
||||
constexpr const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_version) {
|
||||
switch (battery_version) {
|
||||
case 0:
|
||||
return &BatteryChargeParameters0;
|
||||
@ -95,7 +105,8 @@ static const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_ver
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateCharger(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit) {
|
||||
/* Helpers. */
|
||||
void UpdateCharger(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit) {
|
||||
double temperature;
|
||||
u32 battery_voltage;
|
||||
|
||||
@ -138,38 +149,38 @@ static void UpdateCharger(PmicDriver *pmic_driver, ChargerDriver *charger_driver
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSufficientBattery(u32 battery_voltage, bool ac_ok) {
|
||||
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) {
|
||||
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();
|
||||
EndShowChargingIcon();
|
||||
}
|
||||
};
|
||||
|
||||
if (can_show_charging_icon) {
|
||||
size_t battery_percentage;
|
||||
if (R_FAILED(battery_driver->GetBatteryPercentage(&battery_percentage))) {
|
||||
return CheckBatteryResult_Shutdown;
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
Boot::StartShowChargingIcon(battery_percentage, true);
|
||||
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;
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
if (succeed_on_sufficient_battery && battery_charge >= 3.0) {
|
||||
return CheckBatteryResult_Success;
|
||||
return CheckBatteryResult::Success;
|
||||
} else if (shutdown_on_full_battery && battery_charge >= 99.0) {
|
||||
return CheckBatteryResult_Shutdown;
|
||||
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? */
|
||||
@ -177,37 +188,37 @@ static CheckBatteryResult LoopCheckBattery(PmicDriver *pmic_driver, ChargerDrive
|
||||
|
||||
bool ac_ok;
|
||||
if (R_FAILED(pmic_driver->GetAcOk(&ac_ok))) {
|
||||
return CheckBatteryResult_Shutdown;
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
|
||||
u32 battery_voltage;
|
||||
if (R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
|
||||
return CheckBatteryResult_Shutdown;
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
|
||||
if (succeed_on_sufficient_battery && IsSufficientBattery(battery_voltage, ac_ok)) {
|
||||
return CheckBatteryResult_Success;
|
||||
return CheckBatteryResult::Success;
|
||||
}
|
||||
|
||||
if (!ac_ok) {
|
||||
if (can_show_battery_icon && !is_showing_charging_icon) {
|
||||
Boot::ShowLowBatteryIcon();
|
||||
ShowLowBatteryIcon();
|
||||
}
|
||||
return CheckBatteryResult_Shutdown;
|
||||
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;
|
||||
return CheckBatteryResult::Shutdown;
|
||||
}
|
||||
if (power_button_pressed) {
|
||||
return CheckBatteryResult_Reboot;
|
||||
return CheckBatteryResult::Reboot;
|
||||
}
|
||||
}
|
||||
|
||||
if (can_show_battery_icon && !is_showing_charging_icon) {
|
||||
Boot::StartShowChargingIcon(1, false);
|
||||
StartShowChargingIcon(1, false);
|
||||
is_showing_charging_icon = true;
|
||||
}
|
||||
|
||||
@ -215,8 +226,9 @@ static CheckBatteryResult LoopCheckBattery(PmicDriver *pmic_driver, ChargerDrive
|
||||
UpdateCharger(pmic_driver, charger_driver, battery_driver, params, charge_voltage_limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Boot::CheckBatteryCharge() {
|
||||
void CheckBatteryCharge() {
|
||||
PmicDriver pmic_driver;
|
||||
BatteryDriver battery_driver;
|
||||
ChargerDriver charger_driver;
|
||||
@ -231,18 +243,18 @@ void Boot::CheckBatteryCharge() {
|
||||
}
|
||||
}
|
||||
|
||||
const u32 boot_reason = Boot::GetBootReason();
|
||||
InputCurrentLimit input_current_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();
|
||||
}
|
||||
|
||||
if (input_current_limit <= InputCurrentLimit_150mA) {
|
||||
charger_driver.SetChargerConfiguration(ChargerConfiguration_ChargeDisable);
|
||||
if (input_current_limit <= bq24193::InputCurrentLimit_150mA) {
|
||||
charger_driver.SetChargerConfiguration(bq24193::ChargerConfiguration_ChargeDisable);
|
||||
pmic_driver.ShutdownSystem();
|
||||
}
|
||||
|
||||
const BatteryChargeParameters *params = GetBatteryChargeParameters(Boot::GetBatteryVersion());
|
||||
const BatteryChargeParameters *params = GetBatteryChargeParameters(GetBatteryVersion());
|
||||
u32 charge_voltage_limit = params->charge_voltage_limit_default;
|
||||
CheckBatteryResult check_result;
|
||||
if (boot_reason == 4) {
|
||||
@ -261,15 +273,17 @@ void Boot::CheckBatteryCharge() {
|
||||
}
|
||||
|
||||
switch (check_result) {
|
||||
case CheckBatteryResult_Success:
|
||||
case CheckBatteryResult::Success:
|
||||
break;
|
||||
case CheckBatteryResult_Shutdown:
|
||||
case CheckBatteryResult::Shutdown:
|
||||
pmic_driver.ShutdownSystem();
|
||||
break;
|
||||
case CheckBatteryResult_Reboot:
|
||||
Boot::RebootSystem();
|
||||
case CheckBatteryResult::Reboot:
|
||||
RebootSystem();
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
25
stratosphere/boot/source/boot_check_battery.hpp
Normal file
25
stratosphere/boot/source/boot_check_battery.hpp
Normal 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();
|
||||
|
||||
}
|
@ -14,35 +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() {
|
||||
u64 _vaddr;
|
||||
if (R_FAILED(svcQueryIoMapping(&_vaddr, 0x60006000ul, 0x1000))) {
|
||||
std::abort();
|
||||
}
|
||||
volatile u32 *car_regs = reinterpret_cast<volatile u32 *>(_vaddr);
|
||||
/* 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];
|
||||
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() {
|
||||
}
|
||||
|
||||
void CheckClock() {
|
||||
if (!IsUsbClockValid()) {
|
||||
/* Sleep for 1s, then reboot. */
|
||||
svcSleepThread(1'000'000'000ul);
|
||||
Boot::RebootSystem();
|
||||
RebootSystem();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
25
stratosphere/boot/source/boot_check_clock.hpp
Normal file
25
stratosphere/boot/source/boot_check_clock.hpp
Normal 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();
|
||||
|
||||
}
|
@ -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 {
|
||||
|
||||
void Boot::SetInitialClockConfiguration() {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
}
|
@ -14,9 +14,29 @@
|
||||
* 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>
|
||||
#include <stratosphere/spl.hpp>
|
||||
|
||||
#include "boot_display.hpp"
|
||||
#include "boot_i2c_utils.hpp"
|
||||
#include "boot_pmc_wrapper.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"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
/* Display configuration included into anonymous namespace. */
|
||||
namespace {
|
||||
|
||||
#include "boot_display_config.inc"
|
||||
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/* Helpful defines. */
|
||||
constexpr size_t DeviceAddressSpaceAlignSize = 0x400000;
|
||||
@ -42,87 +62,37 @@ constexpr size_t MipiCalSize = 0x1000;
|
||||
/* Types. */
|
||||
|
||||
/* 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];
|
||||
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];
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
static inline uintptr_t QueryVirtualAddress(uintptr_t phys, size_t size) {
|
||||
uintptr_t aligned_phys = phys & ~0xFFFul;
|
||||
size_t aligned_size = size + (phys - aligned_phys);
|
||||
uintptr_t aligned_virt;
|
||||
if (R_FAILED(svcQueryIoMapping(&aligned_virt, aligned_phys, aligned_size))) {
|
||||
std::abort();
|
||||
}
|
||||
return aligned_virt + (phys - aligned_phys);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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 = QueryVirtualAddress(Disp1Base, Disp1Size);
|
||||
g_dsi_regs = QueryVirtualAddress(DsiBase, DsiSize);
|
||||
g_clk_rst_regs = QueryVirtualAddress(ClkRstBase, ClkRstSize);
|
||||
g_gpio_regs = QueryVirtualAddress(GpioBase, GpioSize);
|
||||
g_apb_misc_regs = QueryVirtualAddress(ApbMiscBase, ApbMiscSize);
|
||||
g_mipi_cal_regs = QueryVirtualAddress(MipiCalBase, MipiCalSize);
|
||||
}
|
||||
|
||||
static inline void DoRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes, size_t num_writes) {
|
||||
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;
|
||||
reg::Write(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) {
|
||||
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 {
|
||||
@ -130,10 +100,10 @@ static inline void DoSocDependentRegisterWrites(uintptr_t base_address, const Re
|
||||
}
|
||||
}
|
||||
|
||||
static inline void DoDsiSleepOrRegisterWrites(const DsiSleepOrRegisterWrite *reg_writes, size_t num_writes) {
|
||||
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;
|
||||
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 {
|
||||
@ -146,7 +116,7 @@ static inline void DoDsiSleepOrRegisterWrites(const DsiSleepOrRegisterWrite *reg
|
||||
#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() {
|
||||
void InitializeFrameBuffer() {
|
||||
if (g_frame_buffer != nullptr) {
|
||||
std::memset(g_frame_buffer, 0x00, FrameBufferSize);
|
||||
armDCacheFlush(g_frame_buffer, FrameBufferSize);
|
||||
@ -174,7 +144,7 @@ static void InitializeFrameBuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
static void FinalizeFrameBuffer() {
|
||||
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;
|
||||
@ -196,14 +166,14 @@ static void FinalizeFrameBuffer() {
|
||||
}
|
||||
}
|
||||
|
||||
static void WaitDsiTrigger() {
|
||||
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) {
|
||||
if (reg::Read(g_dsi_regs + sizeof(u32) * DSI_TRIGGER) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -211,79 +181,81 @@ static void WaitDsiTrigger() {
|
||||
svcSleepThread(5'000'000ul);
|
||||
}
|
||||
|
||||
static void WaitDsiHostControl() {
|
||||
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) {
|
||||
if ((reg::Read(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL) & DSI_HOST_CONTROL_IMM_BTA) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Boot::InitializeDisplay() {
|
||||
}
|
||||
|
||||
void InitializeDisplay() {
|
||||
/* Setup globals. */
|
||||
InitializeRegisterBaseAddresses();
|
||||
g_is_mariko = Boot::IsMariko();
|
||||
g_is_mariko = spl::IsMariko();
|
||||
InitializeFrameBuffer();
|
||||
|
||||
/* Turn on DSI/voltage rail. */
|
||||
{
|
||||
I2cSessionImpl i2c_session;
|
||||
I2cDriver::Initialize();
|
||||
I2cDriver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic);
|
||||
i2c::driver::Session i2c_session;
|
||||
i2c::driver::Initialize();
|
||||
ON_SCOPE_EXIT { i2c::driver::Finalize(); };
|
||||
|
||||
i2c::driver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic);
|
||||
|
||||
if (g_is_mariko) {
|
||||
Boot::WriteI2cRegister(i2c_session, 0x18, 0x3A);
|
||||
Boot::WriteI2cRegister(i2c_session, 0x1F, 0x71);
|
||||
WriteI2cRegister(i2c_session, 0x18, 0x3A);
|
||||
WriteI2cRegister(i2c_session, 0x1F, 0x71);
|
||||
}
|
||||
Boot::WriteI2cRegister(i2c_session, 0x23, 0xD0);
|
||||
|
||||
I2cDriver::Finalize();
|
||||
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);
|
||||
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. */
|
||||
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);
|
||||
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. */
|
||||
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);
|
||||
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);
|
||||
SetRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2);
|
||||
reg::SetBits(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);
|
||||
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. */
|
||||
WriteRegister(g_mipi_cal_regs + 0x060, 0);
|
||||
reg::Write(g_mipi_cal_regs + 0x060, 0);
|
||||
if (g_is_mariko) {
|
||||
WriteRegister(g_mipi_cal_regs + 0x058, 0);
|
||||
WriteRegister(g_apb_misc_regs + 0xAC0, 0);
|
||||
reg::Write(g_mipi_cal_regs + 0x058, 0);
|
||||
reg::Write(g_apb_misc_regs + 0xAC0, 0);
|
||||
}
|
||||
|
||||
/* Execute configs. */
|
||||
@ -305,19 +277,19 @@ void Boot::InitializeDisplay() {
|
||||
svcSleepThread(10'000'000ul);
|
||||
|
||||
/* Enable backlight reset. */
|
||||
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4);
|
||||
reg::SetBits(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);
|
||||
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();
|
||||
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x406);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
||||
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x406);
|
||||
reg::Write(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);
|
||||
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);
|
||||
|
||||
@ -325,7 +297,7 @@ void Boot::InitializeDisplay() {
|
||||
{
|
||||
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);
|
||||
host_response[i] = reg::Read(g_dsi_regs + sizeof(u32) * DSI_RD_DATA);
|
||||
}
|
||||
|
||||
if ((host_response[2] & 0xFF) == 0x10) {
|
||||
@ -339,47 +311,47 @@ void Boot::InitializeDisplay() {
|
||||
/* 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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
||||
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? */
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
||||
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) {
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
||||
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);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
|
||||
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
||||
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
|
||||
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -390,7 +362,7 @@ void Boot::InitializeDisplay() {
|
||||
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
|
||||
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init09);
|
||||
|
||||
WriteRegister(g_disp1_regs + sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, SHIFT_CLK_DIVIDER(4));
|
||||
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);
|
||||
|
||||
@ -416,7 +388,7 @@ void Boot::InitializeDisplay() {
|
||||
g_is_display_intialized = true;
|
||||
}
|
||||
|
||||
void Boot::ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img) {
|
||||
void ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img) {
|
||||
if (!g_is_display_intialized) {
|
||||
return;
|
||||
}
|
||||
@ -433,31 +405,31 @@ void Boot::ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u3
|
||||
armDCacheFlush(g_frame_buffer, FrameBufferSize);
|
||||
|
||||
/* Enable backlight. */
|
||||
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1);
|
||||
reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1);
|
||||
}
|
||||
|
||||
void Boot::FinalizeDisplay() {
|
||||
void FinalizeDisplay() {
|
||||
if (!g_is_display_intialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disable backlight. */
|
||||
ClearRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, ~0x1);
|
||||
reg::ClearBits(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);
|
||||
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 = QueryVirtualAddress(0x500030a4, 4);
|
||||
const u32 start_val = ReadRegister(host1x_vaddr);
|
||||
while (ReadRegister(host1x_vaddr) < start_val + 5) {
|
||||
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. */
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
@ -477,27 +449,27 @@ void Boot::FinalizeDisplay() {
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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? */
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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:
|
||||
@ -505,32 +477,34 @@ void Boot::FinalizeDisplay() {
|
||||
}
|
||||
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);
|
||||
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. */
|
||||
ClearRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, ~0x4);
|
||||
reg::ClearBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4);
|
||||
svcSleepThread(10'000'000ul);
|
||||
ClearRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, ~0x2);
|
||||
reg::ClearBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2);
|
||||
svcSleepThread(10'000'000ul);
|
||||
ClearRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, ~0x1);
|
||||
reg::ClearBits(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);
|
||||
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 */
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
28
stratosphere/boot/source/boot_display.hpp
Normal file
28
stratosphere/boot/source/boot_display.hpp
Normal 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();
|
||||
|
||||
}
|
@ -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},
|
@ -14,14 +14,27 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "boot_functions.hpp"
|
||||
#include <stratosphere/spl.hpp>
|
||||
|
||||
static constexpr u32 GpioPadName_FanEnable = 0x4B;
|
||||
#include "boot_fan_enable.hpp"
|
||||
|
||||
void Boot::SetFanEnabled() {
|
||||
if (Boot::GetHardwareType() == HardwareType_Copper) {
|
||||
Boot::GpioConfigure(GpioPadName_FanEnable);
|
||||
Boot::GpioSetDirection(GpioPadName_FanEnable, GpioDirection_Output);
|
||||
Boot::GpioSetValue(GpioPadName_FanEnable, GpioValue_High);
|
||||
#include "gpio/gpio_utils.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience definitions. */
|
||||
constexpr u32 GpioPadName_FanEnable = 0x4B;
|
||||
|
||||
}
|
||||
|
||||
void SetFanEnabled() {
|
||||
if (spl::GetHardwareType() == spl::HardwareType::Copper) {
|
||||
gpio::Configure(GpioPadName_FanEnable);
|
||||
gpio::SetDirection(GpioPadName_FanEnable, GpioDirection_Output);
|
||||
gpio::SetValue(GpioPadName_FanEnable, GpioValue_High);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
25
stratosphere/boot/source/boot_fan_enable.hpp
Normal file
25
stratosphere/boot/source/boot_fan_enable.hpp
Normal 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();
|
||||
|
||||
}
|
@ -1,92 +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_driver/i2c_types.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(I2cSessionImpl &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size);
|
||||
static Result WriteI2cRegister(I2cSessionImpl &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size);
|
||||
static Result WriteI2cRegister(I2cSessionImpl &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);
|
||||
};
|
@ -1,82 +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_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"
|
||||
|
||||
void Boot::SetInitialGpioConfiguration() {
|
||||
const GpioInitialConfig *configs = nullptr;
|
||||
size_t num_configs = 0;
|
||||
const HardwareType hw_type = Boot::GetHardwareType();
|
||||
const FirmwareVersion fw_ver = GetRuntimeFirmwareVersion();
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
} 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();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_configs; i++) {
|
||||
/* Configure the GPIO. */
|
||||
Boot::GpioConfigure(configs[i].pad_name);
|
||||
|
||||
/* Set the GPIO's direction. */
|
||||
Boot::GpioSetDirection(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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,119 +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_gpio_map.hpp"
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
static uintptr_t GetGpioBaseAddress() {
|
||||
if (!g_initialized_gpio_vaddr) {
|
||||
u64 vaddr;
|
||||
if (R_FAILED(svcQueryIoMapping(&vaddr, Boot::GpioPhysicalBase, 0x1000))) {
|
||||
std::abort();
|
||||
}
|
||||
g_gpio_vaddr = vaddr;
|
||||
g_initialized_gpio_vaddr = true;
|
||||
}
|
||||
return g_gpio_vaddr;
|
||||
}
|
||||
|
||||
u32 Boot::GpioConfigure(u32 gpio_pad_name) {
|
||||
uintptr_t gpio_base_vaddr = GetGpioBaseAddress();
|
||||
|
||||
/* Fetch this GPIO's pad descriptor */
|
||||
const u32 gpio_pad_desc = GetGpioPadDescriptor(gpio_pad_name);
|
||||
|
||||
/* Discard invalid GPIOs */
|
||||
if (gpio_pad_desc == GpioInvalid) {
|
||||
return GpioInvalid;
|
||||
}
|
||||
|
||||
/* Convert the GPIO pad descriptor into its register offset */
|
||||
u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C));
|
||||
|
||||
/* 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) */
|
||||
*(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset + 0x80)) = gpio_cnf_val;
|
||||
|
||||
/* Do a dummy read from GPIO_CNF_x register (lower offset) */
|
||||
gpio_cnf_val = *(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset));
|
||||
|
||||
return gpio_cnf_val;
|
||||
}
|
||||
|
||||
u32 Boot::GpioSetDirection(u32 gpio_pad_name, GpioDirection dir) {
|
||||
uintptr_t gpio_base_vaddr = GetGpioBaseAddress();
|
||||
|
||||
/* Fetch this GPIO's pad descriptor */
|
||||
const u32 gpio_pad_desc = GetGpioPadDescriptor(gpio_pad_name);
|
||||
|
||||
/* Discard invalid GPIOs */
|
||||
if (gpio_pad_desc == GpioInvalid) {
|
||||
return GpioInvalid;
|
||||
}
|
||||
|
||||
/* 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 direction bit and lock values */
|
||||
u32 gpio_oe_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (static_cast<u32>(dir) << (gpio_pad_desc & 0x07)));
|
||||
|
||||
/* Write to the appropriate GPIO_OE_x register (upper offset) */
|
||||
*(reinterpret_cast<volatile u32 *>(gpio_base_vaddr + gpio_reg_offset + 0x90)) = gpio_oe_val;
|
||||
|
||||
/* 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));
|
||||
|
||||
return gpio_oe_val;
|
||||
}
|
||||
|
||||
u32 Boot::GpioSetValue(u32 gpio_pad_name, GpioValue val) {
|
||||
uintptr_t gpio_base_vaddr = GetGpioBaseAddress();
|
||||
|
||||
/* Fetch this GPIO's pad descriptor */
|
||||
const u32 gpio_pad_desc = GetGpioPadDescriptor(gpio_pad_name);
|
||||
|
||||
/* Discard invalid GPIOs */
|
||||
if (gpio_pad_desc == GpioInvalid) {
|
||||
return GpioInvalid;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
@ -14,11 +14,14 @@
|
||||
* 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"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename F>
|
||||
static Result RetryUntilSuccess(F f) {
|
||||
constexpr Result RetryUntilSuccess(F f) {
|
||||
constexpr u64 timeout = 10'000'000'000ul;
|
||||
constexpr u64 retry_interval = 20'000'000ul;
|
||||
|
||||
@ -35,21 +38,23 @@ static Result RetryUntilSuccess(F f) {
|
||||
}
|
||||
}
|
||||
|
||||
Result Boot::ReadI2cRegister(I2cSessionImpl &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) {
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
u8 cmd_list[I2cCommandListFormatter::MaxCommandListSize];
|
||||
u8 cmd_list[i2c::CommandListFormatter::MaxCommandListSize];
|
||||
|
||||
I2cCommandListFormatter formatter(cmd_list, sizeof(cmd_list));
|
||||
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));
|
||||
|
||||
return RetryUntilSuccess([&]() { return I2cDriver::ExecuteCommandList(session, dst, dst_size, cmd_list, formatter.GetCurrentSize()); });
|
||||
return RetryUntilSuccess([&]() { return i2c::driver::ExecuteCommandList(session, dst, dst_size, cmd_list, formatter.GetCurrentSize()); });
|
||||
}
|
||||
|
||||
Result Boot::WriteI2cRegister(I2cSessionImpl &session, const u8 *src, size_t src_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) {
|
||||
if (src == nullptr || src_size == 0 || cmd == nullptr || cmd_size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
@ -60,9 +65,11 @@ Result Boot::WriteI2cRegister(I2cSessionImpl &session, const u8 *src, size_t src
|
||||
std::memcpy(&cmd_list[0], cmd, cmd_size);
|
||||
std::memcpy(&cmd_list[cmd_size], src, src_size);
|
||||
|
||||
return RetryUntilSuccess([&]() { return I2cDriver::Send(session, cmd_list, src_size + cmd_size, static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop)); });
|
||||
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));
|
||||
}
|
||||
|
||||
Result Boot::WriteI2cRegister(I2cSessionImpl &session, const u8 address, const u8 value) {
|
||||
return Boot::WriteI2cRegister(session, &value, sizeof(value), &address, sizeof(address));
|
||||
}
|
||||
|
@ -18,25 +18,13 @@
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
/* TODO: Better way to do this? */
|
||||
#include "../boot_types.hpp"
|
||||
#include "i2c/driver/i2c_api.hpp"
|
||||
|
||||
enum BootImageUpdateType {
|
||||
BootImageUpdateType_Erista,
|
||||
BootImageUpdateType_Mariko,
|
||||
};
|
||||
namespace sts::boot {
|
||||
|
||||
enum BootModeType {
|
||||
BootModeType_Normal,
|
||||
BootModeType_Safe,
|
||||
};
|
||||
/* 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);
|
||||
|
||||
static constexpr size_t BctSize = 0x4000;
|
||||
static constexpr size_t EksSize = 0x4000;
|
||||
static constexpr size_t EksEntrySize = 0x200;
|
||||
static constexpr size_t EksBlobSize = 0xB0;
|
||||
|
||||
struct VerificationState {
|
||||
bool needs_verify_normal;
|
||||
bool needs_verify_safe;
|
||||
};
|
||||
}
|
@ -22,9 +22,24 @@
|
||||
#include <switch.h>
|
||||
#include <atmosphere.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include <stratosphere/spl.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_repair_boot_images.hpp"
|
||||
#include "boot_splash_screen.hpp"
|
||||
#include "boot_wake_pins.hpp"
|
||||
|
||||
#include "gpio/gpio_initial_configuration.hpp"
|
||||
#include "pinmux/pinmux_initial_configuration.hpp"
|
||||
|
||||
#include "boot_power_utils.hpp"
|
||||
|
||||
using namespace sts;
|
||||
|
||||
extern "C" {
|
||||
extern u32 __start__;
|
||||
@ -53,7 +68,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 +109,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();
|
||||
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 = spl::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();
|
||||
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;
|
||||
}
|
||||
|
77
stratosphere/boot/source/boot_pcv.cpp
Normal file
77
stratosphere/boot/source/boot_pcv.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 "i2c/i2c_types.hpp"
|
||||
#include "i2c/driver/impl/i2c_pcv.hpp"
|
||||
#include "i2c/driver/impl/i2c_registers.hpp"
|
||||
|
||||
using namespace sts::i2c::driver::impl;
|
||||
|
||||
namespace sts::pcv {
|
||||
|
||||
void Initialize() {
|
||||
/* Don't do anything. */
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
/* Don't do anything. */
|
||||
}
|
||||
|
||||
Result SetClockRate(PcvModule module, u32 hz) {
|
||||
/* Get clock/reset registers. */
|
||||
ClkRstRegisters regs;
|
||||
regs.SetBus(ConvertFromPcvModule(module));
|
||||
/* Set clock enabled/source. */
|
||||
reg::SetBits(regs.clk_en_reg, regs.mask);
|
||||
reg::ReadWrite(regs.clk_src_reg, 0x4, 0xFF);
|
||||
svcSleepThread(1000ul);
|
||||
reg::ReadWrite(regs.clk_src_reg, 0, 0xE0000000);
|
||||
svcSleepThread(2000ul);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetClockEnabled(PcvModule module, bool enabled) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetVoltageEnabled(u32 domain, bool enabled) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetVoltageValue(u32 domain, u32 voltage) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SetReset(PcvModule module, bool reset) {
|
||||
/* Get clock/reset registers. */
|
||||
ClkRstRegisters regs;
|
||||
regs.SetBus(ConvertFromPcvModule(module));
|
||||
|
||||
/* Set/clear reset. */
|
||||
if (reset) {
|
||||
reg::SetBits(regs.rst_reg, regs.mask);
|
||||
} else {
|
||||
reg::ClearBits(regs.rst_reg, regs.mask);
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -1,505 +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"
|
||||
|
||||
static bool g_initialized_pinmux_vaddr = false;
|
||||
static uintptr_t g_pinmux_vaddr = 0;
|
||||
|
||||
static inline const PinmuxDefinition *GetPinmuxDefinition(u32 pinmux_name) {
|
||||
if (pinmux_name >= PinmuxPadNameMax) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return &PinmuxMap[pinmux_name];
|
||||
}
|
||||
|
||||
static inline const PinmuxDrivePadDefinition *GetPinmuxDrivePadDefinition(u32 pinmux_name) {
|
||||
if (pinmux_name >= PinmuxDrivePadNameMax) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return &PinmuxDrivePadMap[pinmux_name];
|
||||
}
|
||||
|
||||
static uintptr_t GetPinmuxBaseAddress() {
|
||||
if (!g_initialized_pinmux_vaddr) {
|
||||
u64 vaddr;
|
||||
if (R_FAILED(svcQueryIoMapping(&vaddr, Boot::ApbMiscPhysicalBase, 0x4000))) {
|
||||
std::abort();
|
||||
}
|
||||
g_pinmux_vaddr = vaddr;
|
||||
g_initialized_pinmux_vaddr = true;
|
||||
}
|
||||
return g_pinmux_vaddr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
*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;
|
||||
}
|
@ -14,25 +14,31 @@
|
||||
* 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) {
|
||||
/* Convenience definitions. */
|
||||
constexpr u32 SmcFunctionId_AtmosphereReadWriteRegister = 0xF0000002;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static inline u32 SmcAtmosphereReadWriteRegister(u32 phys_addr, u32 value, u32 mask) {
|
||||
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;
|
||||
svcCallSecureMonitor(&args);
|
||||
R_ASSERT(svcCallSecureMonitor(&args));
|
||||
if (args.X[0] != 0) {
|
||||
std::abort();
|
||||
}
|
||||
@ -40,7 +46,9 @@ static inline u32 SmcAtmosphereReadWriteRegister(u32 phys_addr, u32 value, u32 m
|
||||
return static_cast<u32>(args.X[1]);
|
||||
}
|
||||
|
||||
u32 Boot::ReadPmcRegister(u32 phys_addr) {
|
||||
}
|
||||
|
||||
u32 ReadPmcRegister(u32 phys_addr) {
|
||||
if (!IsValidPmcAddress(phys_addr)) {
|
||||
std::abort();
|
||||
}
|
||||
@ -48,10 +56,12 @@ u32 Boot::ReadPmcRegister(u32 phys_addr) {
|
||||
return SmcAtmosphereReadWriteRegister(phys_addr, 0, 0);
|
||||
}
|
||||
|
||||
void Boot::WritePmcRegister(u32 phys_addr, u32 value, u32 mask) {
|
||||
void WritePmcRegister(u32 phys_addr, u32 value, u32 mask) {
|
||||
if (!IsValidPmcAddress(phys_addr)) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
SmcAtmosphereReadWriteRegister(phys_addr, value, mask);
|
||||
}
|
||||
|
||||
}
|
||||
|
27
stratosphere/boot/source/boot_pmc_wrapper.hpp
Normal file
27
stratosphere/boot/source/boot_pmc_wrapper.hpp
Normal 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);
|
||||
|
||||
}
|
@ -16,19 +16,18 @@
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_functions.hpp"
|
||||
|
||||
#include "boot_i2c_utils.hpp"
|
||||
#include "boot_pmic_driver.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
void PmicDriver::ShutdownSystem() {
|
||||
if (R_FAILED(this->ShutdownSystem(false))) {
|
||||
std::abort();
|
||||
}
|
||||
R_ASSERT(this->ShutdownSystem(false));
|
||||
}
|
||||
|
||||
void PmicDriver::RebootSystem() {
|
||||
if (R_FAILED(this->ShutdownSystem(true))) {
|
||||
std::abort();
|
||||
}
|
||||
R_ASSERT(this->ShutdownSystem(true));
|
||||
}
|
||||
|
||||
Result PmicDriver::GetAcOk(bool *out) {
|
||||
@ -40,17 +39,17 @@ Result PmicDriver::GetAcOk(bool *out) {
|
||||
|
||||
Result PmicDriver::GetPowerIntr(u8 *out) {
|
||||
const u8 addr = 0x0B;
|
||||
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
|
||||
return 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));
|
||||
return 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));
|
||||
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
Result PmicDriver::GetPowerButtonPressed(bool *out) {
|
||||
@ -66,23 +65,17 @@ Result PmicDriver::ShutdownSystem(bool reboot) {
|
||||
|
||||
/* 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();
|
||||
}
|
||||
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;
|
||||
}
|
||||
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)))) {
|
||||
std::abort();
|
||||
}
|
||||
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;
|
||||
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();
|
||||
}
|
||||
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. */
|
||||
@ -92,9 +85,7 @@ Result PmicDriver::ShutdownSystem(bool reboot) {
|
||||
}
|
||||
|
||||
/* 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();
|
||||
}
|
||||
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);
|
||||
@ -123,3 +114,5 @@ void PmicDriver::FinalizeBattery(BatteryDriver *battery_driver) {
|
||||
battery_driver->SetShutdownEnabled(desired_shutdown_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,21 +18,23 @@
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "i2c_driver/i2c_api.hpp"
|
||||
#include "boot_battery_driver.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
/* Driver object. */
|
||||
class PmicDriver {
|
||||
private:
|
||||
I2cSessionImpl i2c_session;
|
||||
i2c::driver::Session i2c_session;
|
||||
public:
|
||||
PmicDriver() {
|
||||
I2cDriver::Initialize();
|
||||
I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic);
|
||||
i2c::driver::Initialize();
|
||||
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic);
|
||||
}
|
||||
|
||||
~PmicDriver() {
|
||||
I2cDriver::CloseSession(this->i2c_session);
|
||||
I2cDriver::Finalize();
|
||||
i2c::driver::CloseSession(this->i2c_session);
|
||||
i2c::driver::Finalize();
|
||||
}
|
||||
private:
|
||||
Result GetPowerStatus(u8 *out);
|
||||
@ -46,3 +48,6 @@ class PmicDriver {
|
||||
Result GetNvErc(u8 *out);
|
||||
Result GetPowerButtonPressed(bool *out);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
79
stratosphere/boot/source/boot_power_utils.cpp
Normal file
79
stratosphere/boot/source/boot_power_utils.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
@ -18,27 +18,13 @@
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
enum HardwareType {
|
||||
HardwareType_Icosa = 0,
|
||||
HardwareType_Copper = 1,
|
||||
HardwareType_Hoag = 2,
|
||||
HardwareType_Iowa = 3,
|
||||
};
|
||||
namespace sts::boot {
|
||||
|
||||
struct GpioInitialConfig {
|
||||
u32 pad_name;
|
||||
GpioDirection direction;
|
||||
GpioValue value;
|
||||
};
|
||||
/* Power utilities. */
|
||||
void RebootSystem();
|
||||
void ShutdownSystem();
|
||||
|
||||
struct PinmuxInitialConfig {
|
||||
u32 name;
|
||||
u32 val;
|
||||
u32 mask;
|
||||
};
|
||||
/* Atmosphere power utilities. */
|
||||
void RebootForFatalError(AtmosphereFatalErrorContext *ctx);
|
||||
|
||||
struct WakePinConfig {
|
||||
u32 index;
|
||||
bool enabled;
|
||||
u32 level;
|
||||
};
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -14,17 +14,29 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "boot_functions.hpp"
|
||||
#include "updater/updater_api.hpp"
|
||||
#include <stratosphere/spl.hpp>
|
||||
#include <stratosphere/updater.hpp>
|
||||
|
||||
static u8 __attribute__((__aligned__(0x1000))) g_boot_image_work_buffer[0x10000];
|
||||
#include "boot_power_utils.hpp"
|
||||
#include "boot_repair_boot_images.hpp"
|
||||
|
||||
void Boot::CheckAndRepairBootImages() {
|
||||
const BootImageUpdateType boot_image_update_type = Updater::GetBootImageUpdateType(Boot::GetHardwareType());
|
||||
namespace sts::boot {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Globals. */
|
||||
u8 __attribute__((aligned(0x1000))) g_boot_image_work_buffer[0x10000];
|
||||
|
||||
}
|
||||
|
||||
void CheckAndRepairBootImages() {
|
||||
const auto boot_image_update_type = updater::GetBootImageUpdateType(spl::GetHardwareType());
|
||||
|
||||
bool repaired_normal, repaired_safe;
|
||||
if (R_SUCCEEDED(Updater::VerifyBootImagesAndRepairIfNeeded(&repaired_normal, &repaired_safe, g_boot_image_work_buffer, sizeof(g_boot_image_work_buffer), boot_image_update_type)) && repaired_normal) {
|
||||
if (R_SUCCEEDED(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();
|
||||
RebootSystem();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
25
stratosphere/boot/source/boot_repair_boot_images.hpp
Normal file
25
stratosphere/boot/source/boot_repair_boot_images.hpp
Normal 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();
|
||||
|
||||
}
|
@ -16,23 +16,27 @@
|
||||
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
#include "boot_functions.hpp"
|
||||
|
||||
#include "boot_rtc_driver.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
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)));
|
||||
R_TRY(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));
|
||||
return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &address, sizeof(address));
|
||||
}
|
||||
|
||||
Result RtcDriver::GetRtcIntr(u8 *out) {
|
||||
const u8 addr = 0x00;
|
||||
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,20 +18,22 @@
|
||||
#include <switch.h>
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
#include "i2c_driver/i2c_api.hpp"
|
||||
#include "boot_i2c_utils.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
class RtcDriver {
|
||||
private:
|
||||
I2cSessionImpl i2c_session;
|
||||
i2c::driver::Session i2c_session;
|
||||
public:
|
||||
RtcDriver() {
|
||||
I2cDriver::Initialize();
|
||||
I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc);
|
||||
i2c::driver::Initialize();
|
||||
i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc);
|
||||
}
|
||||
|
||||
~RtcDriver() {
|
||||
I2cDriver::CloseSession(this->i2c_session);
|
||||
I2cDriver::Finalize();
|
||||
i2c::driver::CloseSession(this->i2c_session);
|
||||
i2c::driver::Finalize();
|
||||
}
|
||||
private:
|
||||
Result ReadRtcRegister(u8 *out, u8 address);
|
||||
@ -39,3 +41,5 @@ class RtcDriver {
|
||||
Result GetRtcIntr(u8 *out);
|
||||
Result GetRtcIntrM(u8 *out);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,47 +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"
|
||||
|
||||
HardwareType Boot::GetHardwareType() {
|
||||
u64 out_val = 0;
|
||||
if (R_FAILED(splGetConfig(SplConfigItem_HardwareType, &out_val))) {
|
||||
std::abort();
|
||||
}
|
||||
return static_cast<HardwareType>(out_val);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
@ -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"
|
||||
/* TODO: Compile-time switch for splash_screen_text.hpp? */
|
||||
#include "boot_boot_reason.hpp"
|
||||
#include "boot_display.hpp"
|
||||
#include "boot_splash_screen.hpp"
|
||||
|
||||
void Boot::ShowSplashScreen() {
|
||||
const u32 boot_reason = Boot::GetBootReason();
|
||||
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 ShowSplashScreen() {
|
||||
const u32 boot_reason = GetBootReason();
|
||||
if (boot_reason == 1 || boot_reason == 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
Boot::InitializeDisplay();
|
||||
InitializeDisplay();
|
||||
{
|
||||
/* Splash screen is shown for 2 seconds. */
|
||||
Boot::ShowDisplay(SplashScreenX, SplashScreenY, SplashScreenW, SplashScreenH, SplashScreen);
|
||||
ShowDisplay(SplashScreenX, SplashScreenY, SplashScreenW, SplashScreenH, SplashScreen);
|
||||
svcSleepThread(2'000'000'000ul);
|
||||
}
|
||||
Boot::FinalizeDisplay();
|
||||
FinalizeDisplay();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
25
stratosphere/boot/source/boot_splash_screen.hpp
Normal file
25
stratosphere/boot/source/boot_splash_screen.hpp
Normal 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
22
stratosphere/boot/source/boot_splash_screen_notext.inc
Normal file
22
stratosphere/boot/source/boot_splash_screen_notext.inc
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
22
stratosphere/boot/source/boot_splash_screen_text.inc
Normal file
22
stratosphere/boot/source/boot_splash_screen_text.inc
Normal file
File diff suppressed because one or more lines are too long
@ -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]);
|
@ -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},
|
@ -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},
|
@ -14,25 +14,46 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "boot_functions.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"
|
||||
#include <stratosphere/spl.hpp>
|
||||
|
||||
#include "boot_pmc_wrapper.hpp"
|
||||
#include "boot_wake_pins.hpp"
|
||||
|
||||
#include "boot_registers_pmc.hpp"
|
||||
|
||||
namespace sts::boot {
|
||||
|
||||
/* Include configuration into anonymous namespace. */
|
||||
namespace {
|
||||
|
||||
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"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void InitializePmcWakeConfiguration(const bool is_blink) {
|
||||
namespace {
|
||||
|
||||
/* 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. */
|
||||
Boot::WritePmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN, 0);
|
||||
Boot::ReadPmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN);
|
||||
WritePmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN, 0);
|
||||
ReadPmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN);
|
||||
|
||||
/* Initialize APBDEV_PMC_BLINK_TIMER, do a dummy read. */
|
||||
Boot::WritePmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER, 0x8008800);
|
||||
Boot::ReadPmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER);
|
||||
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++) {
|
||||
@ -46,7 +67,7 @@ static void InitializePmcWakeConfiguration(const bool is_blink) {
|
||||
UpdatePmcControlBit(APBDEV_PMC_DPD_PADS_ORIDE, 0x100000, is_blink);
|
||||
}
|
||||
|
||||
void Boot::SetWakeEventLevel(u32 index, u32 level) {
|
||||
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) {
|
||||
@ -62,18 +83,20 @@ void Boot::SetWakeEventLevel(u32 index, u32 level) {
|
||||
UpdatePmcControlBit(pmc_wake_level_mask_reg_offset, mask_val, level > 0);
|
||||
}
|
||||
|
||||
void Boot::SetWakeEventEnabled(u32 index, bool enabled) {
|
||||
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::SetInitialWakePinConfiguration() {
|
||||
}
|
||||
|
||||
void SetInitialWakePinConfiguration() {
|
||||
InitializePmcWakeConfiguration(false);
|
||||
|
||||
/* Set wake event levels, wake event enables. */
|
||||
const WakePinConfig *configs;
|
||||
size_t num_configs;
|
||||
if (Boot::GetHardwareType() == HardwareType_Copper) {
|
||||
if (spl::GetHardwareType() == spl::HardwareType::Copper) {
|
||||
configs = WakePinConfigsCopper;
|
||||
num_configs = NumWakePinConfigsCopper;
|
||||
} else {
|
||||
@ -82,7 +105,9 @@ void Boot::SetInitialWakePinConfiguration() {
|
||||
}
|
||||
|
||||
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);
|
||||
SetWakeEventLevel(configs[i].index, configs[i].level);
|
||||
SetWakeEventEnabled(configs[i].index, configs[i].enabled);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
25
stratosphere/boot/source/boot_wake_pins.hpp
Normal file
25
stratosphere/boot/source/boot_wake_pins.hpp
Normal 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();
|
||||
|
||||
}
|
102
stratosphere/boot/source/gpio/gpio_initial_configuration.cpp
Normal file
102
stratosphere/boot/source/gpio/gpio_initial_configuration.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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 <stratosphere/spl.hpp>
|
||||
|
||||
#include "gpio_initial_configuration.hpp"
|
||||
#include "gpio_utils.hpp"
|
||||
|
||||
namespace sts::gpio {
|
||||
|
||||
namespace {
|
||||
|
||||
struct InitialConfig {
|
||||
u32 pad_name;
|
||||
GpioDirection direction;
|
||||
GpioValue value;
|
||||
};
|
||||
|
||||
/* Include all initial configuration definitions. */
|
||||
#include "gpio_initial_configuration_icosa.inc"
|
||||
#include "gpio_initial_configuration_copper.inc"
|
||||
#include "gpio_initial_configuration_hoag.inc"
|
||||
#include "gpio_initial_configuration_iowa.inc"
|
||||
|
||||
}
|
||||
|
||||
void SetInitialConfiguration() {
|
||||
const InitialConfig *configs = nullptr;
|
||||
size_t num_configs = 0;
|
||||
const auto hw_type = spl::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 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;
|
||||
}
|
||||
|
||||
/* Ensure we found an appropriate config. */
|
||||
if (configs == nullptr) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_configs; i++) {
|
||||
/* Configure the GPIO. */
|
||||
Configure(configs[i].pad_name);
|
||||
|
||||
/* Set the GPIO's direction. */
|
||||
SetDirection(configs[i].pad_name, configs[i].direction);
|
||||
|
||||
if (configs[i].direction == GpioDirection_Output) {
|
||||
/* Set the GPIO's value. */
|
||||
SetValue(configs[i].pad_name, configs[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
25
stratosphere/boot/source/gpio/gpio_initial_configuration.hpp
Normal file
25
stratosphere/boot/source/gpio/gpio_initial_configuration.hpp
Normal 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::gpio {
|
||||
|
||||
void SetInitialConfiguration();
|
||||
|
||||
}
|
@ -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]));
|
@ -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]));
|
@ -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]));
|
@ -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]));
|
@ -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]));
|
132
stratosphere/boot/source/gpio/gpio_utils.cpp
Normal file
132
stratosphere/boot/source/gpio/gpio_utils.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 <stratosphere/reg.hpp>
|
||||
|
||||
#include "gpio_utils.hpp"
|
||||
|
||||
namespace sts::gpio {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Pull in GPIO map definitions. */
|
||||
#include "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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
u32 Configure(u32 gpio_pad_name) {
|
||||
uintptr_t gpio_base_vaddr = GetBaseAddress();
|
||||
|
||||
/* Fetch this GPIO's pad descriptor */
|
||||
const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name);
|
||||
|
||||
/* Discard invalid GPIOs */
|
||||
if (gpio_pad_desc == InvalidPadName) {
|
||||
return InvalidPadName;
|
||||
}
|
||||
|
||||
/* Convert the GPIO pad descriptor into its register offset */
|
||||
u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C));
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
u32 SetDirection(u32 gpio_pad_name, GpioDirection dir) {
|
||||
uintptr_t gpio_base_vaddr = GetBaseAddress();
|
||||
|
||||
/* Fetch this GPIO's pad descriptor */
|
||||
const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name);
|
||||
|
||||
/* Discard invalid GPIOs */
|
||||
if (gpio_pad_desc == InvalidPadName) {
|
||||
return InvalidPadName;
|
||||
}
|
||||
|
||||
/* 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 direction bit and lock values */
|
||||
u32 gpio_oe_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (static_cast<u32>(dir) << (gpio_pad_desc & 0x07)));
|
||||
|
||||
/* Write to the appropriate GPIO_OE_x register (upper offset) */
|
||||
reg::Write(gpio_base_vaddr + gpio_reg_offset + 0x90, gpio_oe_val);
|
||||
|
||||
/* Do a dummy read from GPIO_OE_x register (lower offset) */
|
||||
gpio_oe_val = reg::Read(gpio_base_vaddr + gpio_reg_offset + 0x10);
|
||||
|
||||
return gpio_oe_val;
|
||||
}
|
||||
|
||||
u32 SetValue(u32 gpio_pad_name, GpioValue val) {
|
||||
uintptr_t gpio_base_vaddr = GetBaseAddress();
|
||||
|
||||
/* Fetch this GPIO's pad descriptor */
|
||||
const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name);
|
||||
|
||||
/* Discard invalid GPIOs */
|
||||
if (gpio_pad_desc == InvalidPadName) {
|
||||
return InvalidPadName;
|
||||
}
|
||||
|
||||
/* 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) */
|
||||
reg::Write(gpio_base_vaddr + gpio_reg_offset + 0xA0, gpio_out_val);
|
||||
|
||||
/* Do a dummy read from GPIO_OUT_x register (lower offset) */
|
||||
gpio_out_val = reg::Read(gpio_base_vaddr + gpio_reg_offset + 0x20);
|
||||
|
||||
return gpio_out_val;
|
||||
}
|
||||
|
||||
}
|
28
stratosphere/boot/source/gpio/gpio_utils.hpp
Normal file
28
stratosphere/boot/source/gpio/gpio_utils.hpp
Normal 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::gpio {
|
||||
|
||||
/* GPIO Utilities. */
|
||||
u32 Configure(u32 gpio_pad_name);
|
||||
u32 SetDirection(u32 gpio_pad_name, GpioDirection dir);
|
||||
u32 SetValue(u32 gpio_pad_name, GpioValue val);
|
||||
|
||||
}
|
188
stratosphere/boot/source/i2c/driver/i2c_api.cpp
Normal file
188
stratosphere/boot/source/i2c/driver/i2c_api.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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 "i2c_api.hpp"
|
||||
#include "impl/i2c_resource_manager.hpp"
|
||||
|
||||
namespace sts::i2c::driver {
|
||||
|
||||
namespace {
|
||||
|
||||
/* For convenience. */
|
||||
using CommandHandler = Result (*)(const u8 **cur_cmd, u8 **cur_dst, Session& session);
|
||||
|
||||
/* Command handlers. */
|
||||
Result SendHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) {
|
||||
I2cTransactionOption option = static_cast<I2cTransactionOption>(
|
||||
(((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0)
|
||||
);
|
||||
(*cur_cmd)++;
|
||||
|
||||
size_t num_bytes = (**cur_cmd);
|
||||
(*cur_cmd)++;
|
||||
|
||||
R_TRY(Send(session, *cur_cmd, num_bytes, option));
|
||||
(*cur_cmd) += num_bytes;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result ReceiveHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) {
|
||||
I2cTransactionOption option = static_cast<I2cTransactionOption>(
|
||||
(((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0)
|
||||
);
|
||||
(*cur_cmd)++;
|
||||
|
||||
size_t num_bytes = (**cur_cmd);
|
||||
(*cur_cmd)++;
|
||||
|
||||
R_TRY(Receive(session, *cur_dst, num_bytes, option));
|
||||
(*cur_dst) += num_bytes;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result SubCommandHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) {
|
||||
const SubCommand sub_cmd = static_cast<SubCommand>((**cur_cmd) >> 2);
|
||||
(*cur_cmd)++;
|
||||
|
||||
switch (sub_cmd) {
|
||||
case SubCommand::Sleep:
|
||||
{
|
||||
const size_t us = (**cur_cmd);
|
||||
(*cur_cmd)++;
|
||||
svcSleepThread(us * 1'000ul);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
/* Command handler list. */
|
||||
constexpr CommandHandler g_cmd_handlers[static_cast<size_t>(Command::Count)] = {
|
||||
SendHandler,
|
||||
ReceiveHandler,
|
||||
SubCommandHandler,
|
||||
};
|
||||
|
||||
inline impl::ResourceManager &GetResourceManager() {
|
||||
return impl::ResourceManager::GetInstance();
|
||||
}
|
||||
|
||||
inline void CheckInitialized() {
|
||||
if (!GetResourceManager().IsInitialized()) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Initialization. */
|
||||
void Initialize() {
|
||||
GetResourceManager().Initialize();
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
GetResourceManager().Finalize();
|
||||
}
|
||||
|
||||
/* Session management. */
|
||||
void OpenSession(Session *out_session, I2cDevice device) {
|
||||
CheckInitialized();
|
||||
if (!impl::IsDeviceSupported(device)) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
const auto bus = impl::GetDeviceBus(device);
|
||||
const auto slave_address = impl::GetDeviceSlaveAddress(device);
|
||||
const auto addressing_mode = impl::GetDeviceAddressingMode(device);
|
||||
const auto speed_mode = impl::GetDeviceSpeedMode(device);
|
||||
const auto max_retries = impl::GetDeviceMaxRetries(device);
|
||||
const auto retry_wait_time = impl::GetDeviceRetryWaitTime(device);
|
||||
GetResourceManager().OpenSession(out_session, bus, slave_address, addressing_mode, speed_mode, max_retries, retry_wait_time);
|
||||
}
|
||||
|
||||
void CloseSession(Session &session) {
|
||||
CheckInitialized();
|
||||
GetResourceManager().CloseSession(session);
|
||||
}
|
||||
|
||||
/* Communication. */
|
||||
Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option) {
|
||||
CheckInitialized();
|
||||
if (src == nullptr || size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
std::scoped_lock<HosMutex &> lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx)));
|
||||
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, impl::Command::Send);
|
||||
}
|
||||
|
||||
Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option) {
|
||||
CheckInitialized();
|
||||
if (dst == nullptr || size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
std::scoped_lock<HosMutex &> lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx)));
|
||||
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, impl::Command::Receive);
|
||||
}
|
||||
|
||||
Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) {
|
||||
CheckInitialized();
|
||||
if (dst == nullptr || size == 0 || cmd_list == nullptr || cmd_list_size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
u8 *cur_dst = static_cast<u8 *>(dst);
|
||||
const u8 *cur_cmd = static_cast<const u8 *>(cmd_list);
|
||||
const u8 *cmd_list_end = cur_cmd + cmd_list_size;
|
||||
|
||||
while (cur_cmd < cmd_list_end) {
|
||||
Command cmd = static_cast<Command>((*cur_cmd) & 3);
|
||||
if (cmd >= Command::Count) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
R_TRY(g_cmd_handlers[static_cast<size_t>(cmd)](&cur_cmd, &cur_dst, session));
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
/* Power management. */
|
||||
void SuspendBuses() {
|
||||
GetResourceManager().SuspendBuses();
|
||||
}
|
||||
|
||||
void ResumeBuses() {
|
||||
GetResourceManager().ResumeBuses();
|
||||
}
|
||||
|
||||
void SuspendPowerBus() {
|
||||
GetResourceManager().SuspendPowerBus();
|
||||
}
|
||||
|
||||
void ResumePowerBus() {
|
||||
GetResourceManager().ResumePowerBus();
|
||||
}
|
||||
|
||||
}
|
50
stratosphere/boot/source/i2c/driver/i2c_api.hpp
Normal file
50
stratosphere/boot/source/i2c/driver/i2c_api.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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_types.hpp"
|
||||
#include "../i2c_command_list.hpp"
|
||||
|
||||
namespace sts::i2c::driver {
|
||||
|
||||
struct Session {
|
||||
size_t bus_idx;
|
||||
size_t session_id;
|
||||
};
|
||||
|
||||
/* Initialization. */
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
/* Session management. */
|
||||
void OpenSession(Session *out_session, I2cDevice device);
|
||||
void CloseSession(Session &session);
|
||||
|
||||
/* Communication. */
|
||||
Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option);
|
||||
Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option);
|
||||
Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size);
|
||||
|
||||
/* Power management. */
|
||||
void SuspendBuses();
|
||||
void ResumeBuses();
|
||||
void SuspendPowerBus();
|
||||
void ResumePowerBus();
|
||||
|
||||
}
|
478
stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp
Normal file
478
stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp
Normal file
@ -0,0 +1,478 @@
|
||||
/*
|
||||
* 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 "i2c_pcv.hpp"
|
||||
#include "i2c_bus_accessor.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
void BusAccessor::Open(Bus bus, SpeedMode speed_mode) {
|
||||
std::scoped_lock<HosMutex> lk(this->open_mutex);
|
||||
/* Open new session. */
|
||||
this->open_sessions++;
|
||||
|
||||
/* Ensure we're good if this isn't our first session. */
|
||||
if (this->open_sessions > 1) {
|
||||
if (this->speed_mode != speed_mode) {
|
||||
std::abort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set all members for chosen bus. */
|
||||
{
|
||||
std::scoped_lock<HosMutex> lk(this->register_mutex);
|
||||
/* Set bus/registers. */
|
||||
this->SetBus(bus);
|
||||
/* Set pcv module. */
|
||||
this->pcv_module = ConvertToPcvModule(bus);
|
||||
/* Set speed mode. */
|
||||
this->speed_mode = speed_mode;
|
||||
/* Setup interrupt event. */
|
||||
this->CreateInterruptEvent(bus);
|
||||
}
|
||||
}
|
||||
|
||||
void BusAccessor::Close() {
|
||||
std::scoped_lock<HosMutex> lk(this->open_mutex);
|
||||
/* Close current session. */
|
||||
this->open_sessions--;
|
||||
if (this->open_sessions > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Close interrupt event. */
|
||||
eventClose(&this->interrupt_event);
|
||||
|
||||
/* Close PCV. */
|
||||
pcv::Finalize();
|
||||
|
||||
this->suspended = false;
|
||||
}
|
||||
|
||||
void BusAccessor::Suspend() {
|
||||
std::scoped_lock<HosMutex> lk(this->open_mutex);
|
||||
std::scoped_lock<HosMutex> lk_reg(this->register_mutex);
|
||||
|
||||
if (!this->suspended) {
|
||||
this->suspended = true;
|
||||
|
||||
if (this->pcv_module != PcvModule_I2C5) {
|
||||
this->DisableClock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BusAccessor::Resume() {
|
||||
if (this->suspended) {
|
||||
this->DoInitialConfig();
|
||||
this->suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
void BusAccessor::DoInitialConfig() {
|
||||
std::scoped_lock<HosMutex> lk(this->register_mutex);
|
||||
|
||||
if (this->pcv_module != PcvModule_I2C5) {
|
||||
pcv::Initialize();
|
||||
}
|
||||
|
||||
this->ResetController();
|
||||
this->SetClock(this->speed_mode);
|
||||
this->SetPacketMode();
|
||||
this->FlushFifos();
|
||||
}
|
||||
|
||||
size_t BusAccessor::GetOpenSessions() const {
|
||||
return this->open_sessions;
|
||||
}
|
||||
|
||||
bool BusAccessor::GetBusy() const {
|
||||
/* Nintendo has a loop here that calls a member function to check if busy, retrying a few times. */
|
||||
/* This member function does "return false". */
|
||||
/* We will not bother with the loop. */
|
||||
return false;
|
||||
}
|
||||
|
||||
void BusAccessor::OnStartTransaction() const {
|
||||
/* Nothing actually happens here. */
|
||||
}
|
||||
|
||||
void BusAccessor::OnStopTransaction() const {
|
||||
/* Nothing actually happens here. */
|
||||
}
|
||||
|
||||
Result BusAccessor::StartTransaction(Command command, AddressingMode addressing_mode, u32 slave_address) {
|
||||
/* Nothing actually happens here... */
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result BusAccessor::Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) {
|
||||
std::scoped_lock<HosMutex> lk(this->register_mutex);
|
||||
const u8 *cur_src = data;
|
||||
size_t remaining = num_bytes;
|
||||
|
||||
/* Set interrupt enable, clear interrupt status. */
|
||||
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(); };
|
||||
|
||||
/* Send header. */
|
||||
this->WriteTransferHeader(TransferMode::Send, option, addressing_mode, slave_address, num_bytes);
|
||||
|
||||
/* Send bytes. */
|
||||
while (true) {
|
||||
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++) {
|
||||
const size_t cur_bytes = std::min(remaining, sizeof(u32));
|
||||
u32 val = 0;
|
||||
for (size_t i = 0; i < cur_bytes; i++) {
|
||||
val |= cur_src[i] << (8 * i);
|
||||
}
|
||||
reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, val);
|
||||
|
||||
cur_src += cur_bytes;
|
||||
remaining -= cur_bytes;
|
||||
}
|
||||
|
||||
if (remaining == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
eventClear(&this->interrupt_event);
|
||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||
eventClear(&this->interrupt_event);
|
||||
return ResultI2cTimedOut;
|
||||
}
|
||||
|
||||
R_TRY(this->GetAndHandleTransactionResult());
|
||||
}
|
||||
|
||||
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 = reg::Read(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0);
|
||||
if (interrupt_status & 0x80) {
|
||||
R_TRY(this->GetAndHandleTransactionResult());
|
||||
break;
|
||||
}
|
||||
|
||||
eventClear(&this->interrupt_event);
|
||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||
eventClear(&this->interrupt_event);
|
||||
return ResultI2cTimedOut;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result BusAccessor::Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) {
|
||||
std::scoped_lock<HosMutex> lk(this->register_mutex);
|
||||
u8 *cur_dst = out_data;
|
||||
size_t remaining = num_bytes;
|
||||
|
||||
/* Set interrupt enable, clear interrupt status. */
|
||||
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);
|
||||
|
||||
/* Receive bytes. */
|
||||
while (remaining > 0) {
|
||||
eventClear(&this->interrupt_event);
|
||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||
this->ClearInterruptMask();
|
||||
eventClear(&this->interrupt_event);
|
||||
return ResultI2cTimedOut;
|
||||
}
|
||||
|
||||
R_TRY(this->GetAndHandleTransactionResult());
|
||||
|
||||
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 = 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);
|
||||
}
|
||||
|
||||
cur_dst += cur_bytes;
|
||||
remaining -= cur_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* N doesn't do ClearInterruptMask. */
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void BusAccessor::SetBus(Bus bus) {
|
||||
this->bus = bus;
|
||||
this->i2c_registers = GetRegisters(bus);
|
||||
this->clkrst_registers.SetBus(bus);
|
||||
}
|
||||
|
||||
void BusAccessor::CreateInterruptEvent(Bus bus) {
|
||||
static constexpr u64 s_interrupts[] = {
|
||||
0x46, 0x74, 0x7C, 0x98, 0x55, 0x5F
|
||||
};
|
||||
if (ConvertToIndex(bus) >= sizeof(s_interrupts) / sizeof(s_interrupts[0])) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
Handle evt_h;
|
||||
if (R_FAILED(svcCreateInterruptEvent(&evt_h, s_interrupts[ConvertToIndex(bus)], 1))) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
eventLoadRemote(&this->interrupt_event, evt_h, false);
|
||||
}
|
||||
|
||||
void BusAccessor::SetClock(SpeedMode speed_mode) {
|
||||
u32 t_high, t_low;
|
||||
u32 clk_div, src_div;
|
||||
u32 debounce;
|
||||
|
||||
switch (speed_mode) {
|
||||
case SpeedMode::Normal:
|
||||
t_high = 2;
|
||||
t_low = 4;
|
||||
clk_div = 0x19;
|
||||
src_div = 0x13;
|
||||
debounce = 2;
|
||||
break;
|
||||
case SpeedMode::Fast:
|
||||
t_high = 2;
|
||||
t_low = 4;
|
||||
clk_div = 0x19;
|
||||
src_div = 0x04;
|
||||
debounce = 2;
|
||||
break;
|
||||
case SpeedMode::FastPlus:
|
||||
t_high = 2;
|
||||
t_low = 4;
|
||||
clk_div = 0x10;
|
||||
src_div = 0x02;
|
||||
debounce = 0;
|
||||
break;
|
||||
case SpeedMode::HighSpeed:
|
||||
t_high = 3;
|
||||
t_low = 8;
|
||||
clk_div = 0x02;
|
||||
src_div = 0x02;
|
||||
debounce = 0;
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (speed_mode == SpeedMode::HighSpeed) {
|
||||
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 {
|
||||
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));
|
||||
}
|
||||
|
||||
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))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(pcv::SetClockRate(this->pcv_module, (408'000'000) / (src_div + 1)))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(pcv::SetReset(this->pcv_module, false))) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BusAccessor::ResetController() const {
|
||||
if (this->pcv_module != PcvModule_I2C5) {
|
||||
if (R_FAILED(pcv::SetReset(this->pcv_module, true))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(pcv::SetClockRate(this->pcv_module, 81'600'000))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(pcv::SetReset(this->pcv_module, false))) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BusAccessor::ClearBus() const {
|
||||
bool success = false;
|
||||
for (size_t i = 0; i < 3 && !success; i++) {
|
||||
success = true;
|
||||
|
||||
this->ResetController();
|
||||
|
||||
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);
|
||||
|
||||
reg::SetBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1);
|
||||
{
|
||||
u64 start_tick = armGetSystemTick();
|
||||
while (reg::Read(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0) & 1) {
|
||||
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reg::SetBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x1);
|
||||
{
|
||||
u64 start_tick = armGetSystemTick();
|
||||
while (reg::Read(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0) & 1) {
|
||||
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
u64 start_tick = armGetSystemTick();
|
||||
while (reg::Read(&this->i2c_registers->I2C_I2C_BUS_CLEAR_STATUS_0) & 1) {
|
||||
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BusAccessor::DisableClock() {
|
||||
if (R_FAILED(pcv::SetClockEnabled(this->pcv_module, false))) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void BusAccessor::SetPacketMode() {
|
||||
/* Set PACKET_MODE_EN, MSTR_CONFIG_LOAD */
|
||||
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 */
|
||||
reg::Write(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFC);
|
||||
}
|
||||
|
||||
Result BusAccessor::FlushFifos() {
|
||||
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 (!(reg::Read(&this->i2c_registers->I2C_FIFO_CONTROL_0) & 3)) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
svcSleepThread(1'000'000ul);
|
||||
}
|
||||
|
||||
return ResultI2cBusBusy;
|
||||
}
|
||||
|
||||
Result BusAccessor::GetTransactionResult() const {
|
||||
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)) {
|
||||
return ResultI2cNoAck;
|
||||
}
|
||||
|
||||
/* Check for arb lost. */
|
||||
if ((packet_status & 0x2) || (interrupt_status & 0x4)) {
|
||||
this->ClearBus();
|
||||
return ResultI2cBusBusy;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void BusAccessor::HandleTransactionResult(Result result) {
|
||||
if (R_FAILED(result)) {
|
||||
if (result == ResultI2cNoAck || result == ResultI2cBusBusy) {
|
||||
this->ResetController();
|
||||
this->SetClock(this->speed_mode);
|
||||
this->SetPacketMode();
|
||||
this->FlushFifos();
|
||||
} else {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result BusAccessor::GetAndHandleTransactionResult() {
|
||||
const Result transaction_res = this->GetTransactionResult();
|
||||
R_TRY_CLEANUP(transaction_res, {
|
||||
this->HandleTransactionResult(transaction_res);
|
||||
this->ClearInterruptMask();
|
||||
eventClear(&this->interrupt_event);
|
||||
});
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void BusAccessor::WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes) {
|
||||
this->FlushFifos();
|
||||
|
||||
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;
|
||||
hdr_val |= ((this->speed_mode == SpeedMode::HighSpeed) & 1) << 22;
|
||||
hdr_val |= ((transfer_mode == TransferMode::Receive) & 1) << 19;
|
||||
hdr_val |= ((addressing_mode != AddressingMode::SevenBit) & 1) << 18;
|
||||
hdr_val |= (1 << 17);
|
||||
hdr_val |= (((option & I2cTransactionOption_Stop) == 0) & 1) << 16;
|
||||
hdr_val |= slave_addr_val;
|
||||
|
||||
reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, hdr_val);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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_types.hpp"
|
||||
#include "i2c_registers.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
class BusAccessor {
|
||||
private:
|
||||
enum class TransferMode {
|
||||
Send = 0,
|
||||
Receive = 1,
|
||||
};
|
||||
static constexpr u64 InterruptTimeout = 100'000'000ul;
|
||||
private:
|
||||
Event interrupt_event;
|
||||
HosMutex open_mutex;
|
||||
HosMutex register_mutex;
|
||||
Registers *i2c_registers = nullptr;
|
||||
ClkRstRegisters clkrst_registers;
|
||||
SpeedMode speed_mode = SpeedMode::Fast;
|
||||
size_t open_sessions = 0;
|
||||
Bus bus = Bus::I2C1;
|
||||
PcvModule pcv_module = PcvModule_I2C1;
|
||||
bool suspended = false;
|
||||
public:
|
||||
BusAccessor() { /* ... */ }
|
||||
private:
|
||||
inline void ClearInterruptMask() const {
|
||||
reg::Write(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0);
|
||||
reg::Read(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0);
|
||||
}
|
||||
|
||||
void SetBus(Bus bus);
|
||||
void CreateInterruptEvent(Bus bus);
|
||||
void SetClock(SpeedMode speed_mode);
|
||||
|
||||
void ResetController() const;
|
||||
void ClearBus() const;
|
||||
void DisableClock();
|
||||
void SetPacketMode();
|
||||
Result FlushFifos();
|
||||
|
||||
Result GetTransactionResult() const;
|
||||
void HandleTransactionResult(Result result);
|
||||
Result GetAndHandleTransactionResult();
|
||||
|
||||
void WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes);
|
||||
public:
|
||||
void Open(Bus bus, SpeedMode speed_mode);
|
||||
void Close();
|
||||
void Suspend();
|
||||
void Resume();
|
||||
void DoInitialConfig();
|
||||
|
||||
size_t GetOpenSessions() const;
|
||||
bool GetBusy() const;
|
||||
|
||||
void OnStartTransaction() const;
|
||||
Result StartTransaction(Command command, AddressingMode addressing_mode, u32 slave_address);
|
||||
Result Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address);
|
||||
Result Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address);
|
||||
void OnStopTransaction() const;
|
||||
};
|
||||
|
||||
|
||||
}
|
125
stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp
Normal file
125
stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 "i2c_driver_types.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
struct DeviceConfig {
|
||||
I2cDevice device;
|
||||
Bus bus;
|
||||
u32 slave_address;
|
||||
AddressingMode addressing_mode;
|
||||
SpeedMode speed_mode;
|
||||
u32 max_retries;
|
||||
u64 retry_wait_time;
|
||||
};
|
||||
|
||||
constexpr DeviceConfig g_device_configs[I2cDevice_Count] = {
|
||||
{I2cDevice_DebugPad, Bus::I2C1, 0x52, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0},
|
||||
{I2cDevice_TouchPanel, Bus::I2C3, 0x49, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0},
|
||||
{I2cDevice_Tmp451, Bus::I2C1, 0x4c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0},
|
||||
{I2cDevice_Nct72, Bus::I2C1, 0x4c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0},
|
||||
{I2cDevice_Alc5639, Bus::I2C1, 0x1c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0},
|
||||
{I2cDevice_Max77620Rtc, Bus::I2C5, 0x68, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Max77620Pmic, Bus::I2C5, 0x3c, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Max77621Cpu, Bus::I2C5, 0x1b, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Max77621Gpu, Bus::I2C5, 0x1c, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Bq24193, Bus::I2C1, 0x6b, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000},
|
||||
{I2cDevice_Max17050, Bus::I2C1, 0x36, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000},
|
||||
{I2cDevice_Bm92t30mwv, Bus::I2C1, 0x18, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Vdd15v0Hb, Bus::I2C2, 0x40, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysCpuDs, Bus::I2C2, 0x41, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysGpuDs, Bus::I2C2, 0x44, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysDdrDs, Bus::I2C2, 0x45, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysAp, Bus::I2C2, 0x46, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysBlDs, Bus::I2C2, 0x47, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Bh1730, Bus::I2C2, 0x29, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysCore, Bus::I2C2, 0x48, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Soc1V8, Bus::I2C2, 0x49, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Lpddr1V8, Bus::I2C2, 0x4a, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Reg1V32, Bus::I2C2, 0x4b, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Vdd3V3Sys, Bus::I2C2, 0x4d, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
{I2cDevice_HdmiDdc, Bus::I2C4, 0x50, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0},
|
||||
{I2cDevice_HdmiScdc, Bus::I2C4, 0x54, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0},
|
||||
{I2cDevice_HdmiHdcp, Bus::I2C4, 0x3a, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0},
|
||||
{I2cDevice_Fan53528, Bus::I2C5, 0xa4, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0},
|
||||
{I2cDevice_Max77812_3, Bus::I2C5, 0x31, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0},
|
||||
{I2cDevice_Max77812_2, Bus::I2C5, 0x33, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0},
|
||||
{I2cDevice_Ina226VddDdr0V6, Bus::I2C2, 0x4e, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000},
|
||||
};
|
||||
|
||||
constexpr size_t NumDeviceConfigs = sizeof(g_device_configs) / sizeof(g_device_configs[0]);
|
||||
|
||||
constexpr size_t DeviceInvalidIndex = static_cast<size_t>(-1);
|
||||
|
||||
size_t GetDeviceIndex(I2cDevice dev) {
|
||||
for (size_t i = 0; i < NumDeviceConfigs; i++) {
|
||||
if (g_device_configs[i].device == dev) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return DeviceInvalidIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IsDeviceSupported(I2cDevice dev) {
|
||||
return GetDeviceIndex(dev) != DeviceInvalidIndex;
|
||||
}
|
||||
|
||||
Bus GetDeviceBus(I2cDevice dev) {
|
||||
const size_t dev_idx = GetDeviceIndex(dev);
|
||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].bus;
|
||||
}
|
||||
|
||||
u32 GetDeviceSlaveAddress(I2cDevice dev) {
|
||||
const size_t dev_idx = GetDeviceIndex(dev);
|
||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].slave_address;
|
||||
}
|
||||
|
||||
AddressingMode GetDeviceAddressingMode(I2cDevice dev) {
|
||||
const size_t dev_idx = GetDeviceIndex(dev);
|
||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].addressing_mode;
|
||||
}
|
||||
|
||||
SpeedMode GetDeviceSpeedMode(I2cDevice dev) {
|
||||
const size_t dev_idx = GetDeviceIndex(dev);
|
||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].speed_mode;
|
||||
}
|
||||
|
||||
u32 GetDeviceMaxRetries(I2cDevice dev) {
|
||||
const size_t dev_idx = GetDeviceIndex(dev);
|
||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].max_retries;
|
||||
}
|
||||
|
||||
u64 GetDeviceRetryWaitTime(I2cDevice dev) {
|
||||
const size_t dev_idx = GetDeviceIndex(dev);
|
||||
if (dev_idx == DeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].retry_wait_time;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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_types.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
enum class Command {
|
||||
Send = 0,
|
||||
Receive = 1,
|
||||
};
|
||||
|
||||
enum class Bus {
|
||||
I2C1 = 0,
|
||||
I2C2 = 1,
|
||||
I2C3 = 2,
|
||||
I2C4 = 3,
|
||||
I2C5 = 4,
|
||||
I2C6 = 5,
|
||||
Count,
|
||||
};
|
||||
|
||||
/* Bus helpers. */
|
||||
constexpr inline size_t ConvertToIndex(Bus bus) {
|
||||
return static_cast<size_t>(bus);
|
||||
}
|
||||
|
||||
constexpr inline Bus ConvertFromIndex(size_t idx) {
|
||||
if (idx >= static_cast<size_t>(Bus::Count)) {
|
||||
std::abort();
|
||||
}
|
||||
return static_cast<Bus>(idx);
|
||||
}
|
||||
|
||||
constexpr inline PcvModule ConvertToPcvModule(Bus bus) {
|
||||
switch (bus) {
|
||||
case Bus::I2C1:
|
||||
return PcvModule_I2C1;
|
||||
case Bus::I2C2:
|
||||
return PcvModule_I2C2;
|
||||
case Bus::I2C3:
|
||||
return PcvModule_I2C3;
|
||||
case Bus::I2C4:
|
||||
return PcvModule_I2C4;
|
||||
case Bus::I2C5:
|
||||
return PcvModule_I2C5;
|
||||
case Bus::I2C6:
|
||||
return PcvModule_I2C6;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline Bus ConvertFromPcvModule(PcvModule module) {
|
||||
switch (module) {
|
||||
case PcvModule_I2C1:
|
||||
return Bus::I2C1;
|
||||
case PcvModule_I2C2:
|
||||
return Bus::I2C2;
|
||||
case PcvModule_I2C3:
|
||||
return Bus::I2C3;
|
||||
case PcvModule_I2C4:
|
||||
return Bus::I2C4;
|
||||
case PcvModule_I2C5:
|
||||
return Bus::I2C5;
|
||||
case PcvModule_I2C6:
|
||||
return Bus::I2C6;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* Global type functions. */
|
||||
bool IsDeviceSupported(I2cDevice dev);
|
||||
Bus GetDeviceBus(I2cDevice dev);
|
||||
u32 GetDeviceSlaveAddress(I2cDevice dev);
|
||||
AddressingMode GetDeviceAddressingMode(I2cDevice dev);
|
||||
SpeedMode GetDeviceSpeedMode(I2cDevice dev);
|
||||
u32 GetDeviceMaxRetries(I2cDevice dev);
|
||||
u64 GetDeviceRetryWaitTime(I2cDevice dev);
|
||||
|
||||
}
|
33
stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp
Normal file
33
stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp
Normal 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>
|
||||
|
||||
/* This forward declares the functionality from pcv that i2c::driver uses. */
|
||||
/* This allows for overriding at compile-time (e.g., for boot sysmodule). */
|
||||
namespace sts::pcv {
|
||||
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
Result SetClockRate(PcvModule module, u32 hz);
|
||||
Result SetClockEnabled(PcvModule module, bool enabled);
|
||||
Result SetVoltageEnabled(u32 domain, bool enabled);
|
||||
Result SetVoltageValue(u32 domain, u32 voltage);
|
||||
Result SetReset(PcvModule module, bool reset);
|
||||
|
||||
}
|
109
stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp
Normal file
109
stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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 <stratosphere/reg.hpp>
|
||||
|
||||
#include "i2c_driver_types.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
struct Registers {
|
||||
volatile u32 I2C_I2C_CNFG_0;
|
||||
volatile u32 I2C_I2C_CMD_ADDR0_0;
|
||||
volatile u32 I2C_I2C_CMD_ADDR1_0;
|
||||
volatile u32 I2C_I2C_CMD_DATA1_0;
|
||||
volatile u32 I2C_I2C_CMD_DATA2_0;
|
||||
volatile u32 _0x14;
|
||||
volatile u32 _0x18;
|
||||
volatile u32 I2C_I2C_STATUS_0;
|
||||
volatile u32 I2C_I2C_SL_CNFG_0;
|
||||
volatile u32 I2C_I2C_SL_RCVD_0;
|
||||
volatile u32 I2C_I2C_SL_STATUS_0;
|
||||
volatile u32 I2C_I2C_SL_ADDR1_0;
|
||||
volatile u32 I2C_I2C_SL_ADDR2_0;
|
||||
volatile u32 I2C_I2C_TLOW_SEXT_0;
|
||||
volatile u32 _0x38;
|
||||
volatile u32 I2C_I2C_SL_DELAY_COUNT_0;
|
||||
volatile u32 I2C_I2C_SL_INT_MASK_0;
|
||||
volatile u32 I2C_I2C_SL_INT_SOURCE_0;
|
||||
volatile u32 I2C_I2C_SL_INT_SET_0;
|
||||
volatile u32 _0x4C;
|
||||
volatile u32 I2C_I2C_TX_PACKET_FIFO_0;
|
||||
volatile u32 I2C_I2C_RX_FIFO_0;
|
||||
volatile u32 I2C_PACKET_TRANSFER_STATUS_0;
|
||||
volatile u32 I2C_FIFO_CONTROL_0;
|
||||
volatile u32 I2C_FIFO_STATUS_0;
|
||||
volatile u32 I2C_INTERRUPT_MASK_REGISTER_0;
|
||||
volatile u32 I2C_INTERRUPT_STATUS_REGISTER_0;
|
||||
volatile u32 I2C_I2C_CLK_DIVISOR_REGISTER_0;
|
||||
volatile u32 I2C_I2C_INTERRUPT_SOURCE_REGISTER_0;
|
||||
volatile u32 I2C_I2C_INTERRUPT_SET_REGISTER_0;
|
||||
volatile u32 I2C_I2C_SLV_TX_PACKET_FIFO_0;
|
||||
volatile u32 I2C_I2C_SLV_RX_FIFO_0;
|
||||
volatile u32 I2C_I2C_SLV_PACKET_STATUS_0;
|
||||
volatile u32 I2C_I2C_BUS_CLEAR_CONFIG_0;
|
||||
volatile u32 I2C_I2C_BUS_CLEAR_STATUS_0;
|
||||
volatile u32 I2C_I2C_CONFIG_LOAD_0;
|
||||
volatile u32 _0x90;
|
||||
volatile u32 I2C_I2C_INTERFACE_TIMING_0_0;
|
||||
volatile u32 I2C_I2C_INTERFACE_TIMING_1_0;
|
||||
volatile u32 I2C_I2C_HS_INTERFACE_TIMING_0_0;
|
||||
volatile u32 I2C_I2C_HS_INTERFACE_TIMING_1_0;
|
||||
};
|
||||
|
||||
struct ClkRstRegisters {
|
||||
public:
|
||||
uintptr_t clk_src_reg;
|
||||
uintptr_t clk_en_reg;
|
||||
uintptr_t rst_reg;
|
||||
u32 mask;
|
||||
public:
|
||||
void SetBus(Bus bus) {
|
||||
static constexpr uintptr_t s_clk_src_offsets[ConvertToIndex(Bus::Count)] = {
|
||||
0x124, 0x198, 0x1b8, 0x3c4, 0x128, 0x65c
|
||||
};
|
||||
static constexpr uintptr_t s_clk_en_offsets[ConvertToIndex(Bus::Count)] = {
|
||||
0x010, 0x014, 0x018, 0x360, 0x014, 0x280
|
||||
};
|
||||
static constexpr uintptr_t s_rst_offsets[ConvertToIndex(Bus::Count)] = {
|
||||
0x004, 0x008, 0x00c, 0x358, 0x008, 0x28c
|
||||
};
|
||||
static constexpr size_t s_bit_shifts[ConvertToIndex(Bus::Count)] = {
|
||||
12, 22, 3, 7, 15, 6
|
||||
};
|
||||
|
||||
const uintptr_t registers = GetIoMapping(0x60006000ul, 0x1000);
|
||||
const size_t idx = ConvertToIndex(bus);
|
||||
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]);
|
||||
}
|
||||
};
|
||||
|
||||
inline Registers *GetRegisters(Bus bus) {
|
||||
static constexpr uintptr_t s_offsets[ConvertToIndex(Bus::Count)] = {
|
||||
0x0000, 0x0400, 0x0500, 0x0700, 0x1000, 0x1100
|
||||
};
|
||||
|
||||
const uintptr_t registers = GetIoMapping(0x7000c000ul, 0x2000) + s_offsets[ConvertToIndex(bus)];
|
||||
return reinterpret_cast<Registers *>(registers);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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 "i2c_pcv.hpp"
|
||||
#include "i2c_resource_manager.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
void ResourceManager::Initialize() {
|
||||
std::scoped_lock<HosMutex> lk(this->initialize_mutex);
|
||||
this->ref_cnt++;
|
||||
}
|
||||
|
||||
void ResourceManager::Finalize() {
|
||||
std::scoped_lock<HosMutex> lk(this->initialize_mutex);
|
||||
if (this->ref_cnt == 0) {
|
||||
std::abort();
|
||||
}
|
||||
this->ref_cnt--;
|
||||
if (this->ref_cnt > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock<HosMutex> sess_lk(this->session_open_mutex);
|
||||
for (size_t i = 0; i < MaxDriverSessions; i++) {
|
||||
this->sessions[i].Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t ResourceManager::GetFreeSessionId() const {
|
||||
for (size_t i = 0; i < MaxDriverSessions; i++) {
|
||||
if (!this->sessions[i].IsOpen()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return InvalidSessionId;
|
||||
}
|
||||
|
||||
void ResourceManager::OpenSession(driver::Session *out_session, Bus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time) {
|
||||
bool need_enable_ldo6 = false;
|
||||
size_t session_id = InvalidSessionId;
|
||||
/* Get, open session. */
|
||||
{
|
||||
std::scoped_lock<HosMutex> lk(this->session_open_mutex);
|
||||
if (out_session == nullptr || bus >= Bus::Count) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
session_id = GetFreeSessionId();
|
||||
if (session_id == InvalidSessionId) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
|
||||
if ((bus == Bus::I2C2 || bus == Bus::I2C3) && (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() == 0 && this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() == 0)) {
|
||||
need_enable_ldo6 = true;
|
||||
}
|
||||
|
||||
out_session->session_id = session_id;
|
||||
out_session->bus_idx = ConvertToIndex(bus);
|
||||
this->sessions[session_id].Open(bus, slave_address, addressing_mode, speed_mode, &this->bus_accessors[ConvertToIndex(bus)], max_retries, retry_wait_time);
|
||||
}
|
||||
|
||||
this->sessions[session_id].Start();
|
||||
if (need_enable_ldo6) {
|
||||
pcv::Initialize();
|
||||
if (R_FAILED(pcv::SetVoltageValue(10, 2'900'000))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(pcv::SetVoltageEnabled(10, true))) {
|
||||
std::abort();
|
||||
}
|
||||
pcv::Finalize();
|
||||
svcSleepThread(560'000ul);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::CloseSession(const driver::Session &session) {
|
||||
bool need_disable_ldo6 = false;
|
||||
/* Get, open session. */
|
||||
{
|
||||
std::scoped_lock<HosMutex> lk(this->session_open_mutex);
|
||||
if (!this->sessions[session.session_id].IsOpen()) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
this->sessions[session.session_id].Close();
|
||||
|
||||
if ((ConvertFromIndex(session.bus_idx) == Bus::I2C2 || ConvertFromIndex(session.bus_idx) == Bus::I2C3) &&
|
||||
(this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() == 0 && this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() == 0)) {
|
||||
need_disable_ldo6 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_disable_ldo6) {
|
||||
pcv::Initialize();
|
||||
if (R_FAILED(pcv::SetVoltageEnabled(10, false))) {
|
||||
std::abort();
|
||||
}
|
||||
pcv::Finalize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ResourceManager::SuspendBuses() {
|
||||
if (this->ref_cnt == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (!this->suspended) {
|
||||
{
|
||||
std::scoped_lock<HosMutex> lk(this->session_open_mutex);
|
||||
this->suspended = true;
|
||||
for (size_t i = 0; i < ConvertToIndex(Bus::Count); i++) {
|
||||
if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) {
|
||||
this->bus_accessors[i].Suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
pcv::Initialize();
|
||||
if (R_FAILED(pcv::SetVoltageEnabled(10, false))) {
|
||||
std::abort();
|
||||
}
|
||||
pcv::Finalize();
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::ResumeBuses() {
|
||||
if (this->ref_cnt == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
if (this->suspended) {
|
||||
if (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() > 0 || this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() > 0) {
|
||||
pcv::Initialize();
|
||||
if (R_FAILED(pcv::SetVoltageValue(10, 2'900'000))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(pcv::SetVoltageEnabled(10, true))) {
|
||||
std::abort();
|
||||
}
|
||||
pcv::Finalize();
|
||||
svcSleepThread(1'560'000ul);
|
||||
}
|
||||
{
|
||||
std::scoped_lock<HosMutex> lk(this->session_open_mutex);
|
||||
for (size_t i = 0; i < ConvertToIndex(Bus::Count); i++) {
|
||||
if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) {
|
||||
this->bus_accessors[i].Resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
this->suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::SuspendPowerBus() {
|
||||
if (this->ref_cnt == 0) {
|
||||
std::abort();
|
||||
}
|
||||
std::scoped_lock<HosMutex> lk(this->session_open_mutex);
|
||||
|
||||
if (!this->power_bus_suspended) {
|
||||
this->power_bus_suspended = true;
|
||||
if (this->bus_accessors[PowerBusId].GetOpenSessions() > 0) {
|
||||
this->bus_accessors[PowerBusId].Suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::ResumePowerBus() {
|
||||
if (this->ref_cnt == 0) {
|
||||
std::abort();
|
||||
}
|
||||
std::scoped_lock<HosMutex> lk(this->session_open_mutex);
|
||||
|
||||
if (this->power_bus_suspended) {
|
||||
if (this->bus_accessors[PowerBusId].GetOpenSessions() > 0) {
|
||||
this->bus_accessors[PowerBusId].Resume();
|
||||
}
|
||||
this->power_bus_suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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_api.hpp"
|
||||
#include "i2c_driver_types.hpp"
|
||||
#include "i2c_bus_accessor.hpp"
|
||||
#include "i2c_session.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
class ResourceManager {
|
||||
public:
|
||||
static constexpr size_t MaxDriverSessions = 40;
|
||||
static constexpr size_t PowerBusId = ConvertToIndex(Bus::I2C5);
|
||||
static constexpr size_t InvalidSessionId = static_cast<size_t>(-1);
|
||||
private:
|
||||
HosMutex initialize_mutex;
|
||||
HosMutex session_open_mutex;
|
||||
size_t ref_cnt = 0;
|
||||
bool suspended = false;
|
||||
bool power_bus_suspended = false;
|
||||
Session sessions[MaxDriverSessions];
|
||||
BusAccessor bus_accessors[ConvertToIndex(Bus::Count)];
|
||||
HosMutex transaction_mutexes[ConvertToIndex(Bus::Count)];
|
||||
public:
|
||||
ResourceManager() {
|
||||
/* ... */
|
||||
}
|
||||
private:
|
||||
size_t GetFreeSessionId() const;
|
||||
public:
|
||||
/* N uses a singleton here, we'll oblige. */
|
||||
static ResourceManager &GetInstance() {
|
||||
static ResourceManager s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
bool IsInitialized() const {
|
||||
return this->ref_cnt > 0;
|
||||
}
|
||||
|
||||
Session& GetSession(size_t id) {
|
||||
return this->sessions[id];
|
||||
}
|
||||
|
||||
HosMutex& GetTransactionMutex(Bus bus) {
|
||||
return this->transaction_mutexes[ConvertToIndex(bus)];
|
||||
}
|
||||
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
void OpenSession(driver::Session *out_session, Bus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time);
|
||||
void CloseSession(const driver::Session &session);
|
||||
void SuspendBuses();
|
||||
void ResumeBuses();
|
||||
void SuspendPowerBus();
|
||||
void ResumePowerBus();
|
||||
};
|
||||
|
||||
}
|
103
stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp
Normal file
103
stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 "i2c_session.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
void Session::Open(Bus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, BusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time) {
|
||||
std::scoped_lock<HosMutex> lk(this->bus_accessor_mutex);
|
||||
if (!this->open) {
|
||||
this->bus_accessor = bus_accessor;
|
||||
this->bus = bus;
|
||||
this->slave_address = slave_address;
|
||||
this->addressing_mode = addr_mode;
|
||||
this->max_retries = max_retries;
|
||||
this->retry_wait_time = retry_wait_time;
|
||||
this->bus_accessor->Open(this->bus, speed_mode);
|
||||
this->open = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Session::Start() {
|
||||
std::scoped_lock<HosMutex> lk(this->bus_accessor_mutex);
|
||||
if (this->open) {
|
||||
if (this->bus_accessor->GetOpenSessions() == 1) {
|
||||
this->bus_accessor->DoInitialConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Session::Close() {
|
||||
std::scoped_lock<HosMutex> lk(this->bus_accessor_mutex);
|
||||
if (this->open) {
|
||||
this->bus_accessor->Close();
|
||||
this->bus_accessor = nullptr;
|
||||
this->open = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Session::IsOpen() const {
|
||||
return this->open;
|
||||
}
|
||||
|
||||
Result Session::DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command) {
|
||||
std::scoped_lock<HosMutex> lk(this->bus_accessor_mutex);
|
||||
|
||||
if (this->bus_accessor->GetBusy()) {
|
||||
return ResultI2cBusBusy;
|
||||
}
|
||||
|
||||
this->bus_accessor->OnStartTransaction();
|
||||
ON_SCOPE_EXIT { this->bus_accessor->OnStopTransaction(); };
|
||||
|
||||
R_TRY(this->bus_accessor->StartTransaction(command, this->addressing_mode, this->slave_address));
|
||||
|
||||
switch (command) {
|
||||
case Command::Send:
|
||||
R_TRY(this->bus_accessor->Send(reinterpret_cast<const u8 *>(src), num_bytes, option, this->addressing_mode, this->slave_address));
|
||||
break;
|
||||
case Command::Receive:
|
||||
R_TRY(this->bus_accessor->Receive(reinterpret_cast<u8 *>(dst), num_bytes, option, this->addressing_mode, this->slave_address));
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Session::DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command) {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
R_TRY_CATCH(this->DoTransaction(dst, src, num_bytes, option, command)) {
|
||||
R_CATCH(ResultI2cTimedOut) {
|
||||
i++;
|
||||
if (i <= this->max_retries) {
|
||||
svcSleepThread(this->retry_wait_time);
|
||||
continue;
|
||||
}
|
||||
return ResultI2cBusBusy;
|
||||
}
|
||||
} R_END_TRY_CATCH;
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
51
stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp
Normal file
51
stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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_types.hpp"
|
||||
#include "i2c_bus_accessor.hpp"
|
||||
|
||||
namespace sts::i2c::driver::impl {
|
||||
|
||||
class Session {
|
||||
private:
|
||||
HosMutex bus_accessor_mutex;
|
||||
BusAccessor *bus_accessor = nullptr;
|
||||
Bus bus = Bus::I2C1;
|
||||
u32 slave_address = 0;
|
||||
AddressingMode addressing_mode = AddressingMode::SevenBit;
|
||||
u32 max_retries = 0;
|
||||
u64 retry_wait_time = 0;
|
||||
bool open = false;
|
||||
public:
|
||||
Session() { /* ... */ }
|
||||
public:
|
||||
void Open(Bus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, BusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time);
|
||||
void Start();
|
||||
void Close();
|
||||
|
||||
bool IsOpen() const;
|
||||
|
||||
Result DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command);
|
||||
Result DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
83
stratosphere/boot/source/i2c/i2c_command_list.cpp
Normal file
83
stratosphere/boot/source/i2c/i2c_command_list.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 "i2c_types.hpp"
|
||||
#include "i2c_command_list.hpp"
|
||||
|
||||
namespace sts::i2c {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Useful definitions. */
|
||||
constexpr size_t SendCommandSize = 2;
|
||||
constexpr size_t ReceiveCommandSize = 2;
|
||||
constexpr size_t SleepCommandSize = 2;
|
||||
|
||||
}
|
||||
|
||||
Result CommandListFormatter::CanEnqueue(size_t size) const {
|
||||
if (this->cmd_list_size - this->cur_index < size) {
|
||||
return ResultI2cFullCommandList;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result CommandListFormatter::EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size) {
|
||||
R_TRY(this->CanEnqueue(SendCommandSize + size));
|
||||
|
||||
this->cmd_list[this->cur_index] = static_cast<u8>(Command::Send);
|
||||
this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6;
|
||||
this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7;
|
||||
this->cur_index++;
|
||||
|
||||
this->cmd_list[this->cur_index++] = size;
|
||||
|
||||
const u8 *src_u8 = reinterpret_cast<const u8 *>(src);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
this->cmd_list[this->cur_index++] = src_u8[i];
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result CommandListFormatter::EnqueueReceiveCommand(I2cTransactionOption option, size_t size) {
|
||||
R_TRY(this->CanEnqueue(ReceiveCommandSize));
|
||||
|
||||
this->cmd_list[this->cur_index] = static_cast<u8>(Command::Receive);
|
||||
this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6;
|
||||
this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7;
|
||||
this->cur_index++;
|
||||
|
||||
this->cmd_list[this->cur_index++] = size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result CommandListFormatter::EnqueueSleepCommand(size_t us) {
|
||||
R_TRY(this->CanEnqueue(SleepCommandSize));
|
||||
|
||||
this->cmd_list[this->cur_index] = static_cast<u8>(Command::SubCommand);
|
||||
this->cmd_list[this->cur_index] |= static_cast<u8>(SubCommand::Sleep) << 2;
|
||||
this->cur_index++;
|
||||
|
||||
this->cmd_list[this->cur_index++] = us;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
66
stratosphere/boot/source/i2c/i2c_command_list.hpp
Normal file
66
stratosphere/boot/source/i2c/i2c_command_list.hpp
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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_types.hpp"
|
||||
|
||||
namespace sts::i2c {
|
||||
|
||||
enum class Command {
|
||||
Send = 0,
|
||||
Receive = 1,
|
||||
SubCommand = 2,
|
||||
Count,
|
||||
};
|
||||
|
||||
enum class SubCommand {
|
||||
Sleep = 0,
|
||||
Count,
|
||||
};
|
||||
|
||||
class CommandListFormatter {
|
||||
public:
|
||||
static constexpr size_t MaxCommandListSize = 0x100;
|
||||
private:
|
||||
u8 *cmd_list = nullptr;
|
||||
size_t cmd_list_size = 0;
|
||||
size_t cur_index = 0;
|
||||
public:
|
||||
CommandListFormatter(void *cmd_list, size_t cmd_list_size) : cmd_list(static_cast<u8 *>(cmd_list)), cmd_list_size(cmd_list_size) {
|
||||
if (cmd_list_size > MaxCommandListSize) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
~CommandListFormatter() {
|
||||
this->cmd_list = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Result CanEnqueue(size_t size) const;
|
||||
public:
|
||||
size_t GetCurrentSize() const {
|
||||
return this->cur_index;
|
||||
}
|
||||
|
||||
Result EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size);
|
||||
Result EnqueueReceiveCommand(I2cTransactionOption option, size_t size);
|
||||
Result EnqueueSleepCommand(size_t us);
|
||||
};
|
||||
|
||||
}
|
34
stratosphere/boot/source/i2c/i2c_types.hpp
Normal file
34
stratosphere/boot/source/i2c/i2c_types.hpp
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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::i2c {
|
||||
|
||||
enum class AddressingMode {
|
||||
SevenBit = 0,
|
||||
};
|
||||
|
||||
enum class SpeedMode {
|
||||
Normal = 100000,
|
||||
Fast = 400000,
|
||||
FastPlus = 1000000,
|
||||
HighSpeed = 3400000,
|
||||
};
|
||||
|
||||
}
|
@ -1,90 +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 "i2c_types.hpp"
|
||||
#include "i2c_registers.hpp"
|
||||
#include "boot_pcv.hpp"
|
||||
|
||||
static I2cBus GetI2cBus(PcvModule module) {
|
||||
switch (module) {
|
||||
case PcvModule_I2C1:
|
||||
return I2cBus_I2c1;
|
||||
case PcvModule_I2C2:
|
||||
return I2cBus_I2c2;
|
||||
case PcvModule_I2C3:
|
||||
return I2cBus_I2c3;
|
||||
case PcvModule_I2C4:
|
||||
return I2cBus_I2c4;
|
||||
case PcvModule_I2C5:
|
||||
return I2cBus_I2c5;
|
||||
case PcvModule_I2C6:
|
||||
return I2cBus_I2c6;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void Pcv::Initialize() {
|
||||
/* Don't do anything. */
|
||||
}
|
||||
|
||||
void Pcv::Finalize() {
|
||||
/* Don't do anything. */
|
||||
}
|
||||
|
||||
Result Pcv::SetClockRate(PcvModule module, u32 hz) {
|
||||
/* Get clock/reset registers. */
|
||||
ClkRstRegisters regs;
|
||||
regs.SetBus(GetI2cBus(module));
|
||||
/* Set clock enabled/source. */
|
||||
SetRegisterBits(regs.clk_en_reg, regs.mask);
|
||||
ReadWriteRegisterBits(regs.clk_src_reg, 0x4, 0xFF);
|
||||
svcSleepThread(1000ul);
|
||||
ReadWriteRegisterBits(regs.clk_src_reg, 0, 0xE0000000);
|
||||
svcSleepThread(2000ul);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Pcv::SetClockEnabled(PcvModule module, bool enabled) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Pcv::SetVoltageEnabled(u32 domain, bool enabled) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Pcv::SetVoltageValue(u32 domain, u32 voltage) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Pcv::SetReset(PcvModule module, bool reset) {
|
||||
/* Get clock/reset registers. */
|
||||
ClkRstRegisters regs;
|
||||
regs.SetBus(GetI2cBus(module));
|
||||
|
||||
/* Set/clear reset. */
|
||||
if (reset) {
|
||||
SetRegisterBits(regs.rst_reg, regs.mask);
|
||||
} else {
|
||||
ClearRegisterBits(regs.rst_reg, ~regs.mask);
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
@ -1,173 +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 "i2c_api.hpp"
|
||||
#include "i2c_resource_manager.hpp"
|
||||
|
||||
typedef Result (*CommandHandler)(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session);
|
||||
|
||||
static Result I2cSendHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) {
|
||||
I2cTransactionOption option = static_cast<I2cTransactionOption>(
|
||||
(((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0)
|
||||
);
|
||||
(*cur_cmd)++;
|
||||
|
||||
size_t num_bytes = (**cur_cmd);
|
||||
(*cur_cmd)++;
|
||||
|
||||
R_TRY(I2cDriver::Send(session, *cur_cmd, num_bytes, option));
|
||||
(*cur_cmd) += num_bytes;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static Result I2cReceiveHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) {
|
||||
I2cTransactionOption option = static_cast<I2cTransactionOption>(
|
||||
(((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0)
|
||||
);
|
||||
(*cur_cmd)++;
|
||||
|
||||
size_t num_bytes = (**cur_cmd);
|
||||
(*cur_cmd)++;
|
||||
|
||||
R_TRY(I2cDriver::Receive(session, *cur_dst, num_bytes, option));
|
||||
(*cur_dst) += num_bytes;
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static Result I2cSubCommandHandler(const u8 **cur_cmd, u8 **cur_dst, I2cSessionImpl& session) {
|
||||
const I2cSubCommand sub_cmd = static_cast<I2cSubCommand>((**cur_cmd) >> 2);
|
||||
(*cur_cmd)++;
|
||||
|
||||
switch (sub_cmd) {
|
||||
case I2cSubCommand_Sleep:
|
||||
{
|
||||
const size_t us = (**cur_cmd);
|
||||
(*cur_cmd)++;
|
||||
svcSleepThread(us * 1'000ul);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
static constexpr CommandHandler g_cmd_handlers[I2cCommand_Count] = {
|
||||
I2cSendHandler,
|
||||
I2cReceiveHandler,
|
||||
I2cSubCommandHandler,
|
||||
};
|
||||
|
||||
static inline I2cResourceManager &GetResourceManager() {
|
||||
return I2cResourceManager::GetInstance();
|
||||
}
|
||||
|
||||
static inline void CheckInitialized() {
|
||||
if (!GetResourceManager().IsInitialized()) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void I2cDriver::Initialize() {
|
||||
GetResourceManager().Initialize();
|
||||
}
|
||||
|
||||
void I2cDriver::Finalize() {
|
||||
GetResourceManager().Finalize();
|
||||
}
|
||||
|
||||
void I2cDriver::OpenSession(I2cSessionImpl *out_session, I2cDevice device) {
|
||||
CheckInitialized();
|
||||
if (!IsI2cDeviceSupported(device)) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
const I2cBus bus = GetI2cDeviceBus(device);
|
||||
const u32 slave_address = GetI2cDeviceSlaveAddress(device);
|
||||
const AddressingMode addressing_mode = GetI2cDeviceAddressingMode(device);
|
||||
const SpeedMode speed_mode = GetI2cDeviceSpeedMode(device);
|
||||
const u32 max_retries = GetI2cDeviceMaxRetries(device);
|
||||
const u64 retry_wait_time = GetI2cDeviceRetryWaitTime(device);
|
||||
GetResourceManager().OpenSession(out_session, bus, slave_address, addressing_mode, speed_mode, max_retries, retry_wait_time);
|
||||
}
|
||||
|
||||
void I2cDriver::CloseSession(I2cSessionImpl &session) {
|
||||
CheckInitialized();
|
||||
GetResourceManager().CloseSession(session);
|
||||
}
|
||||
|
||||
Result I2cDriver::Send(I2cSessionImpl &session, const void *src, size_t size, I2cTransactionOption option) {
|
||||
CheckInitialized();
|
||||
if (src == nullptr || size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
std::scoped_lock<HosMutex &> lk(GetResourceManager().GetTransactionMutex(session.bus));
|
||||
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, DriverCommand_Send);
|
||||
}
|
||||
|
||||
Result I2cDriver::Receive(I2cSessionImpl &session, void *dst, size_t size, I2cTransactionOption option) {
|
||||
CheckInitialized();
|
||||
if (dst == nullptr || size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
std::scoped_lock<HosMutex &> lk(GetResourceManager().GetTransactionMutex(session.bus));
|
||||
return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, DriverCommand_Receive);
|
||||
}
|
||||
|
||||
Result I2cDriver::ExecuteCommandList(I2cSessionImpl &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) {
|
||||
CheckInitialized();
|
||||
if (dst == nullptr || size == 0 || cmd_list == nullptr || cmd_list_size == 0) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
u8 *cur_dst = static_cast<u8 *>(dst);
|
||||
const u8 *cur_cmd = static_cast<const u8 *>(cmd_list);
|
||||
const u8 *cmd_list_end = cur_cmd + cmd_list_size;
|
||||
|
||||
while (cur_cmd < cmd_list_end) {
|
||||
I2cCommand cmd = static_cast<I2cCommand>((*cur_cmd) & 3);
|
||||
if (cmd >= I2cCommand_Count) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
R_TRY(g_cmd_handlers[cmd](&cur_cmd, &cur_dst, session));
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void I2cDriver::SuspendBuses() {
|
||||
GetResourceManager().SuspendBuses();
|
||||
}
|
||||
|
||||
void I2cDriver::ResumeBuses() {
|
||||
GetResourceManager().ResumeBuses();
|
||||
}
|
||||
|
||||
void I2cDriver::SuspendPowerBus() {
|
||||
GetResourceManager().SuspendPowerBus();
|
||||
}
|
||||
|
||||
void I2cDriver::ResumePowerBus() {
|
||||
GetResourceManager().ResumePowerBus();
|
||||
}
|
@ -1,38 +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 "i2c_types.hpp"
|
||||
#include "i2c_command_list.hpp"
|
||||
|
||||
class I2cDriver {
|
||||
public:
|
||||
static void Initialize();
|
||||
static void Finalize();
|
||||
static void OpenSession(I2cSessionImpl *out_session, I2cDevice device);
|
||||
static void CloseSession(I2cSessionImpl &session);
|
||||
static Result Send(I2cSessionImpl &session, const void *src, size_t size, I2cTransactionOption option);
|
||||
static Result Receive(I2cSessionImpl &session, void *dst, size_t size, I2cTransactionOption option);
|
||||
static Result ExecuteCommandList(I2cSessionImpl &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size);
|
||||
|
||||
static void SuspendBuses();
|
||||
static void ResumeBuses();
|
||||
static void SuspendPowerBus();
|
||||
static void ResumePowerBus();
|
||||
};
|
@ -1,495 +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 "i2c_bus_accessor.hpp"
|
||||
#include "boot_pcv.hpp"
|
||||
|
||||
void I2cBusAccessor::Open(I2cBus bus, SpeedMode speed_mode) {
|
||||
std::scoped_lock<HosMutex> lk(this->open_mutex);
|
||||
/* Open new session. */
|
||||
this->open_sessions++;
|
||||
|
||||
/* Ensure we're good if this isn't our first session. */
|
||||
if (this->open_sessions > 1) {
|
||||
if (this->speed_mode != speed_mode) {
|
||||
std::abort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set all members for chosen bus. */
|
||||
{
|
||||
std::scoped_lock<HosMutex> lk(this->register_mutex);
|
||||
/* Set bus/registers. */
|
||||
this->SetBus(bus);
|
||||
/* Set pcv module. */
|
||||
switch (bus) {
|
||||
case I2cBus_I2c1:
|
||||
this->pcv_module = PcvModule_I2C1;
|
||||
break;
|
||||
case I2cBus_I2c2:
|
||||
this->pcv_module = PcvModule_I2C2;
|
||||
break;
|
||||
case I2cBus_I2c3:
|
||||
this->pcv_module = PcvModule_I2C3;
|
||||
break;
|
||||
case I2cBus_I2c4:
|
||||
this->pcv_module = PcvModule_I2C4;
|
||||
break;
|
||||
case I2cBus_I2c5:
|
||||
this->pcv_module = PcvModule_I2C5;
|
||||
break;
|
||||
case I2cBus_I2c6:
|
||||
this->pcv_module = PcvModule_I2C6;
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
/* Set speed mode. */
|
||||
this->speed_mode = speed_mode;
|
||||
/* Setup interrupt event. */
|
||||
this->CreateInterruptEvent(bus);
|
||||
}
|
||||
}
|
||||
|
||||
void I2cBusAccessor::Close() {
|
||||
std::scoped_lock<HosMutex> lk(this->open_mutex);
|
||||
/* Close current session. */
|
||||
this->open_sessions--;
|
||||
if (this->open_sessions > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Close interrupt event. */
|
||||
eventClose(&this->interrupt_event);
|
||||
|
||||
/* Close PCV. */
|
||||
Pcv::Finalize();
|
||||
|
||||
this->suspended = false;
|
||||
}
|
||||
|
||||
void I2cBusAccessor::Suspend() {
|
||||
std::scoped_lock<HosMutex> lk(this->open_mutex);
|
||||
std::scoped_lock<HosMutex> lk_reg(this->register_mutex);
|
||||
|
||||
if (!this->suspended) {
|
||||
this->suspended = true;
|
||||
|
||||
if (this->pcv_module != PcvModule_I2C5) {
|
||||
this->DisableClock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2cBusAccessor::Resume() {
|
||||
if (this->suspended) {
|
||||
this->DoInitialConfig();
|
||||
this->suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
void I2cBusAccessor::DoInitialConfig() {
|
||||
std::scoped_lock<HosMutex> lk(this->register_mutex);
|
||||
|
||||
if (this->pcv_module != PcvModule_I2C5) {
|
||||
Pcv::Initialize();
|
||||
}
|
||||
|
||||
this->ResetController();
|
||||
this->SetClock(this->speed_mode);
|
||||
this->SetPacketMode();
|
||||
this->FlushFifos();
|
||||
}
|
||||
|
||||
size_t I2cBusAccessor::GetOpenSessions() const {
|
||||
return this->open_sessions;
|
||||
}
|
||||
|
||||
bool I2cBusAccessor::GetBusy() const {
|
||||
/* Nintendo has a loop here that calls a member function to check if busy, retrying a few times. */
|
||||
/* This member function does "return false". */
|
||||
/* We will not bother with the loop. */
|
||||
return false;
|
||||
}
|
||||
|
||||
void I2cBusAccessor::OnStartTransaction() const {
|
||||
/* Nothing actually happens here. */
|
||||
}
|
||||
|
||||
void I2cBusAccessor::OnStopTransaction() const {
|
||||
/* Nothing actually happens here. */
|
||||
}
|
||||
|
||||
Result I2cBusAccessor::StartTransaction(DriverCommand command, AddressingMode addressing_mode, u32 slave_address) {
|
||||
/* Nothing actually happens here... */
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result I2cBusAccessor::Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) {
|
||||
std::scoped_lock<HosMutex> lk(this->register_mutex);
|
||||
const u8 *cur_src = data;
|
||||
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);
|
||||
|
||||
ON_SCOPE_EXIT { this->ClearInterruptMask(); };
|
||||
|
||||
/* Send header. */
|
||||
this->WriteTransferHeader(TransferMode_Send, option, addressing_mode, slave_address, num_bytes);
|
||||
|
||||
/* Send bytes. */
|
||||
while (true) {
|
||||
const u32 fifo_status = ReadRegister(&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++) {
|
||||
const size_t cur_bytes = std::min(remaining, sizeof(u32));
|
||||
u32 val = 0;
|
||||
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);
|
||||
|
||||
cur_src += cur_bytes;
|
||||
remaining -= cur_bytes;
|
||||
}
|
||||
|
||||
if (remaining == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
eventClear(&this->interrupt_event);
|
||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||
eventClear(&this->interrupt_event);
|
||||
return ResultI2cTimedOut;
|
||||
}
|
||||
|
||||
R_TRY(this->GetAndHandleTransactionResult());
|
||||
}
|
||||
|
||||
WriteRegister(&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);
|
||||
if (interrupt_status & 0x80) {
|
||||
R_TRY(this->GetAndHandleTransactionResult());
|
||||
break;
|
||||
}
|
||||
|
||||
eventClear(&this->interrupt_event);
|
||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||
eventClear(&this->interrupt_event);
|
||||
return ResultI2cTimedOut;
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result I2cBusAccessor::Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) {
|
||||
std::scoped_lock<HosMutex> lk(this->register_mutex);
|
||||
u8 *cur_dst = out_data;
|
||||
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);
|
||||
|
||||
/* Send header. */
|
||||
this->WriteTransferHeader(TransferMode_Receive, option, addressing_mode, slave_address, num_bytes);
|
||||
|
||||
/* Receive bytes. */
|
||||
while (remaining > 0) {
|
||||
eventClear(&this->interrupt_event);
|
||||
if (R_FAILED(eventWait(&this->interrupt_event, InterruptTimeout))) {
|
||||
this->HandleTransactionResult(ResultI2cBusBusy);
|
||||
this->ClearInterruptMask();
|
||||
eventClear(&this->interrupt_event);
|
||||
return ResultI2cTimedOut;
|
||||
}
|
||||
|
||||
R_TRY(this->GetAndHandleTransactionResult());
|
||||
|
||||
const u32 fifo_status = ReadRegister(&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 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);
|
||||
}
|
||||
|
||||
cur_dst += cur_bytes;
|
||||
remaining -= cur_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/* N doesn't do ClearInterruptMask. */
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void I2cBusAccessor::SetBus(I2cBus bus) {
|
||||
this->bus = bus;
|
||||
this->i2c_registers = GetI2cRegisters(bus);
|
||||
this->clkrst_registers.SetBus(bus);
|
||||
}
|
||||
|
||||
void I2cBusAccessor::CreateInterruptEvent(I2cBus bus) {
|
||||
static constexpr u64 s_interrupts[] = {
|
||||
0x46, 0x74, 0x7C, 0x98, 0x55, 0x5F
|
||||
};
|
||||
if (static_cast<size_t>(bus) >= sizeof(s_interrupts) / sizeof(s_interrupts[0])) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
Handle evt_h;
|
||||
if (R_FAILED(svcCreateInterruptEvent(&evt_h, s_interrupts[static_cast<size_t>(bus)], 1))) {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
eventLoadRemote(&this->interrupt_event, evt_h, false);
|
||||
}
|
||||
|
||||
void I2cBusAccessor::SetClock(SpeedMode speed_mode) {
|
||||
u32 t_high, t_low;
|
||||
u32 clk_div, src_div;
|
||||
u32 debounce;
|
||||
|
||||
switch (speed_mode) {
|
||||
case SpeedMode_Normal:
|
||||
t_high = 2;
|
||||
t_low = 4;
|
||||
clk_div = 0x19;
|
||||
src_div = 0x13;
|
||||
debounce = 2;
|
||||
break;
|
||||
case SpeedMode_Fast:
|
||||
t_high = 2;
|
||||
t_low = 4;
|
||||
clk_div = 0x19;
|
||||
src_div = 0x04;
|
||||
debounce = 2;
|
||||
break;
|
||||
case SpeedMode_FastPlus:
|
||||
t_high = 2;
|
||||
t_low = 4;
|
||||
clk_div = 0x10;
|
||||
src_div = 0x02;
|
||||
debounce = 0;
|
||||
break;
|
||||
case SpeedMode_HighSpeed:
|
||||
t_high = 3;
|
||||
t_low = 8;
|
||||
clk_div = 0x02;
|
||||
src_div = 0x02;
|
||||
debounce = 0;
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
|
||||
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);
|
||||
} 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));
|
||||
}
|
||||
|
||||
WriteRegister(&this->i2c_registers->I2C_I2C_CNFG_0, debounce);
|
||||
ReadRegister(&this->i2c_registers->I2C_I2C_CNFG_0);
|
||||
|
||||
if (this->pcv_module != PcvModule_I2C5) {
|
||||
if (R_FAILED(Pcv::SetReset(this->pcv_module, true))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(Pcv::SetClockRate(this->pcv_module, (408'000'000) / (src_div + 1)))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(Pcv::SetReset(this->pcv_module, false))) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2cBusAccessor::ResetController() const {
|
||||
if (this->pcv_module != PcvModule_I2C5) {
|
||||
if (R_FAILED(Pcv::SetReset(this->pcv_module, true))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(Pcv::SetClockRate(this->pcv_module, 81'600'000))) {
|
||||
std::abort();
|
||||
}
|
||||
if (R_FAILED(Pcv::SetReset(this->pcv_module, false))) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2cBusAccessor::ClearBus() const {
|
||||
bool success = false;
|
||||
for (size_t i = 0; i < 3 && !success; i++) {
|
||||
success = true;
|
||||
|
||||
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);
|
||||
|
||||
SetRegisterBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1);
|
||||
{
|
||||
u64 start_tick = armGetSystemTick();
|
||||
while (ReadRegister(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0) & 1) {
|
||||
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SetRegisterBits(&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) {
|
||||
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
u64 start_tick = armGetSystemTick();
|
||||
while (ReadRegister(&this->i2c_registers->I2C_I2C_BUS_CLEAR_STATUS_0) & 1) {
|
||||
if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2cBusAccessor::DisableClock() {
|
||||
if (R_FAILED(Pcv::SetClockEnabled(this->pcv_module, false))) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void I2cBusAccessor::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);
|
||||
|
||||
/* Set TX_FIFO_TRIGGER, RX_FIFO_TRIGGER */
|
||||
WriteRegister(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFC);
|
||||
}
|
||||
|
||||
Result I2cBusAccessor::FlushFifos() {
|
||||
WriteRegister(&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)) {
|
||||
return ResultSuccess;
|
||||
}
|
||||
svcSleepThread(1'000'000ul);
|
||||
}
|
||||
|
||||
return ResultI2cBusBusy;
|
||||
}
|
||||
|
||||
Result I2cBusAccessor::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);
|
||||
|
||||
/* Check for no ack. */
|
||||
if ((packet_status & 0xC) || (interrupt_status & 0x8)) {
|
||||
return ResultI2cNoAck;
|
||||
}
|
||||
|
||||
/* Check for arb lost. */
|
||||
if ((packet_status & 0x2) || (interrupt_status & 0x4)) {
|
||||
this->ClearBus();
|
||||
return ResultI2cBusBusy;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void I2cBusAccessor::HandleTransactionResult(Result result) {
|
||||
if (R_FAILED(result)) {
|
||||
if (result == ResultI2cNoAck || result == ResultI2cBusBusy) {
|
||||
this->ResetController();
|
||||
this->SetClock(this->speed_mode);
|
||||
this->SetPacketMode();
|
||||
this->FlushFifos();
|
||||
} else {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result I2cBusAccessor::GetAndHandleTransactionResult() {
|
||||
const Result transaction_res = this->GetTransactionResult();
|
||||
R_TRY_CLEANUP(transaction_res, {
|
||||
this->HandleTransactionResult(transaction_res);
|
||||
this->ClearInterruptMask();
|
||||
eventClear(&this->interrupt_event);
|
||||
});
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void I2cBusAccessor::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);
|
||||
|
||||
const u32 slave_addr_val = ((transfer_mode == TransferMode_Receive) & 1) | ((slave_address & 0x7F) << 1);
|
||||
u32 hdr_val = 0;
|
||||
hdr_val |= ((this->speed_mode == SpeedMode_HighSpeed) & 1) << 22;
|
||||
hdr_val |= ((transfer_mode == TransferMode_Receive) & 1) << 19;
|
||||
hdr_val |= ((addressing_mode != AddressingMode_7Bit) & 1) << 18;
|
||||
hdr_val |= (1 << 17);
|
||||
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);
|
||||
}
|
@ -1,82 +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 "i2c_types.hpp"
|
||||
#include "i2c_registers.hpp"
|
||||
|
||||
class I2cBusAccessor {
|
||||
private:
|
||||
enum TransferMode {
|
||||
TransferMode_Send = 0,
|
||||
TransferMode_Receive = 1,
|
||||
};
|
||||
static constexpr u64 InterruptTimeout = 100'000'000ul;
|
||||
private:
|
||||
Event interrupt_event;
|
||||
HosMutex open_mutex;
|
||||
HosMutex register_mutex;
|
||||
I2cRegisters *i2c_registers = nullptr;
|
||||
ClkRstRegisters clkrst_registers;
|
||||
SpeedMode speed_mode = SpeedMode_Fast;
|
||||
size_t open_sessions = 0;
|
||||
I2cBus bus = I2cBus_I2c1;
|
||||
PcvModule pcv_module = PcvModule_I2C1;
|
||||
bool suspended = false;
|
||||
public:
|
||||
I2cBusAccessor() {
|
||||
/* ... */
|
||||
}
|
||||
private:
|
||||
inline void ClearInterruptMask() const {
|
||||
WriteRegister(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0);
|
||||
ReadRegister(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0);
|
||||
}
|
||||
|
||||
void SetBus(I2cBus bus);
|
||||
void CreateInterruptEvent(I2cBus bus);
|
||||
void SetClock(SpeedMode speed_mode);
|
||||
|
||||
void ResetController() const;
|
||||
void ClearBus() const;
|
||||
void DisableClock();
|
||||
void SetPacketMode();
|
||||
Result FlushFifos();
|
||||
|
||||
Result GetTransactionResult() const;
|
||||
void HandleTransactionResult(Result result);
|
||||
Result GetAndHandleTransactionResult();
|
||||
|
||||
void WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes);
|
||||
public:
|
||||
void Open(I2cBus bus, SpeedMode speed_mode);
|
||||
void Close();
|
||||
void Suspend();
|
||||
void Resume();
|
||||
void DoInitialConfig();
|
||||
|
||||
size_t GetOpenSessions() const;
|
||||
bool GetBusy() const;
|
||||
|
||||
void OnStartTransaction() const;
|
||||
Result StartTransaction(DriverCommand command, AddressingMode addressing_mode, u32 slave_address);
|
||||
Result Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address);
|
||||
Result Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address);
|
||||
void OnStopTransaction() const;
|
||||
};
|
@ -1,67 +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 "i2c_command_list.hpp"
|
||||
|
||||
Result I2cCommandListFormatter::CanEnqueue(size_t size) const {
|
||||
if (this->cmd_list_size - this->cur_index < size) {
|
||||
return ResultI2cFullCommandList;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result I2cCommandListFormatter::EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size) {
|
||||
R_TRY(this->CanEnqueue(SendCommandSize + size));
|
||||
|
||||
this->cmd_list[this->cur_index] = I2cCommand_Send;
|
||||
this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6;
|
||||
this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7;
|
||||
this->cur_index++;
|
||||
|
||||
this->cmd_list[this->cur_index++] = size;
|
||||
|
||||
const u8 *src_u8 = reinterpret_cast<const u8 *>(src);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
this->cmd_list[this->cur_index++] = src_u8[i];
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result I2cCommandListFormatter::EnqueueReceiveCommand(I2cTransactionOption option, size_t size) {
|
||||
R_TRY(this->CanEnqueue(ReceiveCommandSize));
|
||||
|
||||
this->cmd_list[this->cur_index] = I2cCommand_Receive;
|
||||
this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6;
|
||||
this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7;
|
||||
this->cur_index++;
|
||||
|
||||
this->cmd_list[this->cur_index++] = size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result I2cCommandListFormatter::EnqueueSleepCommand(size_t us) {
|
||||
R_TRY(this->CanEnqueue(SleepCommandSize));
|
||||
|
||||
this->cmd_list[this->cur_index] = I2cCommand_SubCommand;
|
||||
this->cmd_list[this->cur_index] |= I2cSubCommand_Sleep << 2;
|
||||
this->cur_index++;
|
||||
|
||||
this->cmd_list[this->cur_index++] = us;
|
||||
return ResultSuccess;
|
||||
}
|
@ -1,53 +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 "i2c_types.hpp"
|
||||
|
||||
class I2cCommandListFormatter {
|
||||
public:
|
||||
static constexpr size_t MaxCommandListSize = 0x100;
|
||||
static constexpr size_t SendCommandSize = 2;
|
||||
static constexpr size_t ReceiveCommandSize = 2;
|
||||
static constexpr size_t SleepCommandSize = 2;
|
||||
private:
|
||||
u8 *cmd_list = nullptr;
|
||||
size_t cmd_list_size = 0;
|
||||
size_t cur_index = 0;
|
||||
public:
|
||||
I2cCommandListFormatter(void *cmd_list, size_t cmd_list_size) : cmd_list(static_cast<u8 *>(cmd_list)), cmd_list_size(cmd_list_size) {
|
||||
if (cmd_list_size > MaxCommandListSize) {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
~I2cCommandListFormatter() {
|
||||
this->cmd_list = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
Result CanEnqueue(size_t size) const;
|
||||
public:
|
||||
size_t GetCurrentSize() const {
|
||||
return this->cur_index;
|
||||
}
|
||||
|
||||
Result EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size);
|
||||
Result EnqueueReceiveCommand(I2cTransactionOption option, size_t size);
|
||||
Result EnqueueSleepCommand(size_t us);
|
||||
};
|
@ -1,117 +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 "i2c_types.hpp"
|
||||
|
||||
struct DeviceConfig {
|
||||
I2cDevice device;
|
||||
I2cBus bus;
|
||||
u32 slave_address;
|
||||
AddressingMode addressing_mode;
|
||||
SpeedMode speed_mode;
|
||||
u32 max_retries;
|
||||
u64 retry_wait_time;
|
||||
};
|
||||
|
||||
static constexpr DeviceConfig g_device_configs[I2cDevice_Count] = {
|
||||
{I2cDevice_DebugPad, I2cBus_I2c1, 0x52, AddressingMode_7Bit, SpeedMode_Normal, 0, 0},
|
||||
{I2cDevice_TouchPanel, I2cBus_I2c3, 0x49, AddressingMode_7Bit, SpeedMode_Fast, 0, 0},
|
||||
{I2cDevice_Tmp451, I2cBus_I2c1, 0x4c, AddressingMode_7Bit, SpeedMode_Normal, 0, 0},
|
||||
{I2cDevice_Nct72, I2cBus_I2c1, 0x4c, AddressingMode_7Bit, SpeedMode_Normal, 0, 0},
|
||||
{I2cDevice_Alc5639, I2cBus_I2c1, 0x1c, AddressingMode_7Bit, SpeedMode_Normal, 0, 0},
|
||||
{I2cDevice_Max77620Rtc, I2cBus_I2c5, 0x68, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Max77620Pmic, I2cBus_I2c5, 0x3c, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Max77621Cpu, I2cBus_I2c5, 0x1b, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Max77621Gpu, I2cBus_I2c5, 0x1c, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Bq24193, I2cBus_I2c1, 0x6b, AddressingMode_7Bit, SpeedMode_Normal, 3, 5'000'000},
|
||||
{I2cDevice_Max17050, I2cBus_I2c1, 0x36, AddressingMode_7Bit, SpeedMode_Normal, 3, 5'000'000},
|
||||
{I2cDevice_Bm92t30mwv, I2cBus_I2c1, 0x18, AddressingMode_7Bit, SpeedMode_Normal, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Vdd15v0Hb, I2cBus_I2c2, 0x40, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysCpuDs, I2cBus_I2c2, 0x41, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysGpuDs, I2cBus_I2c2, 0x44, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysDdrDs, I2cBus_I2c2, 0x45, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysAp, I2cBus_I2c2, 0x46, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysBlDs, I2cBus_I2c2, 0x47, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Bh1730, I2cBus_I2c2, 0x29, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226VsysCore, I2cBus_I2c2, 0x48, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Soc1V8, I2cBus_I2c2, 0x49, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Lpddr1V8, I2cBus_I2c2, 0x4a, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Reg1V32, I2cBus_I2c2, 0x4b, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_Ina226Vdd3V3Sys, I2cBus_I2c2, 0x4d, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
{I2cDevice_HdmiDdc, I2cBus_I2c4, 0x50, AddressingMode_7Bit, SpeedMode_Normal, 0, 0},
|
||||
{I2cDevice_HdmiScdc, I2cBus_I2c4, 0x54, AddressingMode_7Bit, SpeedMode_Normal, 0, 0},
|
||||
{I2cDevice_HdmiHdcp, I2cBus_I2c4, 0x3a, AddressingMode_7Bit, SpeedMode_Normal, 0, 0},
|
||||
{I2cDevice_Fan53528, I2cBus_I2c5, 0xa4, AddressingMode_7Bit, SpeedMode_Fast, 0, 0},
|
||||
{I2cDevice_Max77812_3, I2cBus_I2c5, 0x31, AddressingMode_7Bit, SpeedMode_Fast, 0, 0},
|
||||
{I2cDevice_Max77812_2, I2cBus_I2c5, 0x33, AddressingMode_7Bit, SpeedMode_Fast, 0, 0},
|
||||
{I2cDevice_Ina226VddDdr0V6, I2cBus_I2c2, 0x4e, AddressingMode_7Bit, SpeedMode_Fast, 3, 5'000'000},
|
||||
};
|
||||
|
||||
static constexpr size_t NumDeviceConfigs = sizeof(g_device_configs) / sizeof(g_device_configs[0]);
|
||||
|
||||
static constexpr size_t I2cDeviceInvalidIndex = static_cast<size_t>(-1);
|
||||
|
||||
static size_t GetI2cDeviceIndex(I2cDevice dev) {
|
||||
for (size_t i = 0; i < NumDeviceConfigs; i++) {
|
||||
if (g_device_configs[i].device == dev) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return I2cDeviceInvalidIndex;
|
||||
}
|
||||
|
||||
bool IsI2cDeviceSupported(I2cDevice dev) {
|
||||
return GetI2cDeviceIndex(dev) != I2cDeviceInvalidIndex;
|
||||
}
|
||||
|
||||
I2cBus GetI2cDeviceBus(I2cDevice dev) {
|
||||
const size_t dev_idx = GetI2cDeviceIndex(dev);
|
||||
if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].bus;
|
||||
}
|
||||
|
||||
u32 GetI2cDeviceSlaveAddress(I2cDevice dev) {
|
||||
const size_t dev_idx = GetI2cDeviceIndex(dev);
|
||||
if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].slave_address;
|
||||
}
|
||||
|
||||
AddressingMode GetI2cDeviceAddressingMode(I2cDevice dev) {
|
||||
const size_t dev_idx = GetI2cDeviceIndex(dev);
|
||||
if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].addressing_mode;
|
||||
}
|
||||
|
||||
SpeedMode GetI2cDeviceSpeedMode(I2cDevice dev) {
|
||||
const size_t dev_idx = GetI2cDeviceIndex(dev);
|
||||
if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].speed_mode;
|
||||
}
|
||||
|
||||
u32 GetI2cDeviceMaxRetries(I2cDevice dev) {
|
||||
const size_t dev_idx = GetI2cDeviceIndex(dev);
|
||||
if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].max_retries;
|
||||
}
|
||||
|
||||
u64 GetI2cDeviceRetryWaitTime(I2cDevice dev) {
|
||||
const size_t dev_idx = GetI2cDeviceIndex(dev);
|
||||
if (dev_idx == I2cDeviceInvalidIndex) { std::abort(); }
|
||||
return g_device_configs[dev_idx].retry_wait_time;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user