Merge pull request #536 from Atmosphere-NX/boot_dev

Boot: reimplement the boot sysmodule
This commit is contained in:
SciresM authored and GitHub committed 2019-05-10 02:37:03 -07:00
commit 27cde7da7a
94 files changed
+10813 -2681

No files matched your search

+7 -8
View File
@@ -38,11 +38,6 @@
#include "sc7.h"
#include "exocfg.h"
#define SMC_USER_HANDLERS 0x13
#define SMC_PRIV_HANDLERS 0x9
#define SMC_AMS_HANDLERS 0x2
#define DEBUG_LOG_SMCS 0
#define DEBUG_PANIC_ON_FAILURE 0
@@ -97,7 +92,7 @@ typedef struct {
uint32_t num_handlers;
} smc_table_t;
static smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = {
static smc_table_entry_t g_smc_user_table[] = {
{0, 4, NULL},
{0xC3000401, 4, smc_set_config_user},
{0xC3000002, 1, smc_get_config_user},
@@ -118,8 +113,9 @@ static smc_table_entry_t g_smc_user_table[SMC_USER_HANDLERS] = {
{0xC3000011, 4, smc_load_titlekey},
{0xC3000012, 4, smc_unwrap_aes_wrapped_titlekey}
};
#define SMC_USER_HANDLERS (sizeof(g_smc_user_table) / sizeof(g_smc_user_table[0]))
static smc_table_entry_t g_smc_priv_table[SMC_PRIV_HANDLERS] = {
static smc_table_entry_t g_smc_priv_table[] = {
{0, 4, NULL},
{0xC4000001, 4, smc_cpu_suspend},
{0x84000002, 4, smc_cpu_off},
@@ -130,12 +126,15 @@ static smc_table_entry_t g_smc_priv_table[SMC_PRIV_HANDLERS] = {
{0xC3000007, 1, smc_configure_carveout},
{0xC3000008, 1, smc_read_write_register}
};
#define SMC_PRIV_HANDLERS (sizeof(g_smc_priv_table) / sizeof(g_smc_priv_table[0]))
/* This is a table used for atmosphere-specific SMCs. */
static smc_table_entry_t g_smc_ams_table[SMC_AMS_HANDLERS] = {
static smc_table_entry_t g_smc_ams_table[] = {
{0, 4, NULL},
{0xF0000201, 0, smc_ams_iram_copy},
{0xF0000002, 0, smc_read_write_register},
};
#define SMC_AMS_HANDLERS (sizeof(g_smc_ams_table) / sizeof(g_smc_ams_table[0]))
static smc_table_t g_smc_tables[SMC_HANDLER_COUNT + 1] = {
{ /* SMC_HANDLER_USER */
+1 -1
View File
@@ -96,7 +96,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot_100.kip boot_200.kip
KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \
exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \
sept-primary.bin sept-secondary.enc \
+2 -4
View File
@@ -220,10 +220,8 @@ SECTIONS
======================= */
PROVIDE(__ams_mitm_kip_start__ = ams_mitm_kip - __start__);
PROVIDE(__ams_mitm_kip_size__ = ams_mitm_kip_end - ams_mitm_kip);
PROVIDE(__boot_100_kip_start__ = boot_100_kip - __start__);
PROVIDE(__boot_100_kip_size__ = boot_100_kip_end - boot_100_kip);
PROVIDE(__boot_200_kip_start__ = boot_200_kip - __start__);
PROVIDE(__boot_200_kip_size__ = boot_200_kip_end - boot_200_kip);
PROVIDE(__boot_kip_start__ = boot_kip - __start__);
PROVIDE(__boot_kip_size__ = boot_kip_end - boot_kip);
PROVIDE(__exosphere_bin_start__ = exosphere_bin - __start__);
PROVIDE(__exosphere_bin_size__ = exosphere_bin_end - exosphere_bin);
PROVIDE(__fusee_primary_bin_start__ = fusee_primary_bin - __start__);
+4 -12
View File
@@ -103,20 +103,12 @@ _content_headers:
.asciz "ams_mitm"
.align 5
/* boot_100 content header */
.word __boot_100_kip_start__
.word __boot_100_kip_size__
/* boot content header */
.word __boot_kip_start__
.word __boot_kip_size__
.word CONTENT_TYPE_KIP
.word 0xCCCCCCCC
.asciz "boot_100"
.align 5
/* boot_200 content header */
.word __boot_200_kip_start__
.word __boot_200_kip_size__
.word CONTENT_TYPE_KIP
.word 0xCCCCCCCC
.asciz "boot_200"
.asciz "boot"
.align 5
/* exosphere content header */
+4 -17
View File
@@ -33,8 +33,7 @@
#include "pm_kip.h"
#include "sm_kip.h"
#include "ams_mitm_kip.h"
#include "boot_100_kip.h"
#include "boot_200_kip.h"
#include "boot_kip.h"
#include "spl_kip.h"
#undef u8
#undef u32
@@ -47,18 +46,14 @@ static bool g_stratosphere_sm_enabled = true;
static bool g_stratosphere_pm_enabled = true;
static bool g_stratosphere_ams_mitm_enabled = true;
static bool g_stratosphere_spl_enabled = true;
static bool g_stratosphere_boot_enabled = false;
static bool g_stratosphere_boot_enabled = true;
extern const uint8_t boot_100_kip[], boot_200_kip[];
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], ams_mitm_kip[];
extern const uint32_t boot_100_kip_size, boot_200_kip_size;
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, ams_mitm_kip_size;
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ams_mitm_kip[];
extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, boot_kip_size, ams_mitm_kip_size;
/* GCC doesn't consider the size as const... we have to write it ourselves. */
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
const uint8_t *boot_kip = NULL;
uint32_t boot_kip_size = 0;
uint32_t num_processes = 0;
uint8_t *data;
@@ -66,14 +61,6 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
return g_stratosphere_ini1;
}
if (target_firmware <= ATMOSPHERE_TARGET_FIRMWARE_100) {
boot_kip = boot_100_kip;
boot_kip_size = boot_100_kip_size;
} else {
boot_kip = boot_200_kip;
boot_kip_size = boot_200_kip_size;
}
size_t size = sizeof(ini1_header_t);
/* Calculate our processes' sizes. */
+24 -14
View File
@@ -16,6 +16,12 @@ ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
AMSREV := $(AMSREV)-dirty
endif
define _bin2o
bin2s $< | $(AS) -o $(@)
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _ | tr - _)`"_end[];" > `(echo $(<F) | tr . _ | tr - _)`.h
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _ | tr - _)`"[];" >> `(echo $(<F) | tr . _ | tr - _)`.h
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _ | tr - _)`_size";" >> `(echo $(<F) | tr . _ | tr - _)`.h
endef
#---------------------------------------------------------------------------------
# TARGET is the name of the output
@@ -27,7 +33,7 @@ endif
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
SOURCES := source source/i2c_driver source/updater
DATA := data
INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src
@@ -69,14 +75,14 @@ export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) $(TOPDIR)/../../fusee/fusee-primary
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
@@ -116,19 +122,23 @@ else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
.PHONY: $(BUILD) clean all
.PHONY: $(BUILD) check_fusee clean all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
check_fusee:
@$(MAKE) -C $(TOPDIR)/../../fusee/fusee-primary all
$(BUILD): check_fusee
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET)_100.kip $(TARGET)_100.elf $(TARGET)_200.kip $(TARGET)_200.elf
@$(MAKE) -C $(TOPDIR)/../../fusee/fusee-primary clean
@rm -fr $(BUILD) $(TARGET).kip $(TARGET).elf
#---------------------------------------------------------------------------------
@@ -140,20 +150,20 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT)_100.kip $(OUTPUT)_200.kip
all : $(OUTPUT).kip
$(OUTPUT)_100.kip : $(OUTPUT)_100.elf
$(OUTPUT)_200.kip : $(OUTPUT)_200.elf
$(OUTPUT).kip : $(OUTPUT).elf
$(OUTPUT)_100.kip : APP_JSON = $(TOPDIR)/boot_100.json
$(OUTPUT)_200.kip : APP_JSON = $(TOPDIR)/boot_200.json
$(OUTPUT)_100.elf : $(OFILES)
$(OUTPUT)_200.elf : $(OFILES)
$(OUTPUT).elf : $(OFILES)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
fusee_primary.bin.o fusee_primary_bin.h: fusee-primary.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(_bin2o)
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@@ -1,7 +1,7 @@
{
"name": "boot",
"title_id": "0x0100000000000005",
"main_thread_stack_size": "0x1000",
"main_thread_stack_size": "0x8000",
"main_thread_priority": 27,
"default_cpu_id": 3,
"process_category": 1,
-171
View File
@@ -1,171 +0,0 @@
{
"name": "boot",
"title_id": "0x0100000000000005",
"main_thread_stack_size": "0x1000",
"main_thread_priority": 27,
"default_cpu_id": 3,
"process_category": 1,
"kernel_capabilities": [
{
"type": "handle_table_size",
"value": 128
},
{
"type": "syscalls",
"value": {
"svcSetHeapSize": "0x01",
"svcSetMemoryPermission": "0x02",
"svcSetMemoryAttribute": "0x03",
"svcMapMemory": "0x04",
"svcUnmapMemory": "0x05",
"svcQueryMemory": "0x06",
"svcExitProcess": "0x07",
"svcCreateThread": "0x08",
"svcStartThread": "0x09",
"svcExitThread": "0x0A",
"svcSleepThread": "0x0B",
"svcGetThreadPriority": "0x0C",
"svcSetThreadPriority": "0x0D",
"svcGetThreadCoreMask": "0x0E",
"svcSetThreadCoreMask": "0x0F",
"svcGetCurrentProcessorNumber": "0x10",
"svcSignalEvent": "0x11",
"svcClearEvent": "0x12",
"svcMapSharedMemory": "0x13",
"svcUnmapSharedMemory": "0x14",
"svcCreateTransferMemory": "0x15",
"svcCloseHandle": "0x16",
"svcResetSignal": "0x17",
"svcWaitSynchronization": "0x18",
"svcCancelSynchronization": "0x19",
"svcArbitrateLock": "0x1A",
"svcArbitrateUnlock": "0x1B",
"svcWaitProcessWideKeyAtomic": "0x1C",
"svcSignalProcessWideKey": "0x1D",
"svcGetSystemTick": "0x1E",
"svcConnectToNamedPort": "0x1F",
"svcSendSyncRequestLight": "0x20",
"svcSendSyncRequest": "0x21",
"svcSendSyncRequestWithUserBuffer": "0x22",
"svcSendAsyncRequestWithUserBuffer": "0x23",
"svcGetProcessId": "0x24",
"svcGetThreadId": "0x25",
"svcBreak": "0x26",
"svcOutputDebugString": "0x27",
"svcReturnFromException": "0x28",
"svcGetInfo": "0x29",
"svcCreateInterruptEvent": "0x53",
"svcQueryIoMapping": "0x55",
"svcCreateDeviceAddressSpace": "0x56",
"svcAttachDeviceAddressSpace": "0x57",
"svcDetachDeviceAddressSpace": "0x58",
"svcMapDeviceAddressSpaceAligned": "0x5A",
"svcUnmapDeviceAddressSpace": "0x5C",
"svcFlushProcessDataCache": "0x5F",
"svcCallSecureMonitor": "0x7F"
}
},
{
"type": "map",
"value": {
"address": "0x50003000",
"size": "0x1000",
"is_ro": false,
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x54200000",
"size": "0x3000",
"is_ro": false,
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x54300000",
"size": "0x1000",
"is_ro": false,
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x60006000",
"size": "0x1000",
"is_ro": false,
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x6000D000",
"size": "0x1000",
"is_ro": false,
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x70000000",
"size": "0x4000",
"is_ro": false,
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x7000C000",
"size": "0x2000",
"is_ro": false,
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x7000E000",
"size": "0x4000",
"is_ro": false,
"is_io": true
}
},
{
"type": "map",
"value": {
"address": "0x700E3000",
"size": "0x1000",
"is_ro": false,
"is_io": true
}
},
{
"type": "irq_pair",
"value": [
70,
116
]
},
{
"type": "irq_pair",
"value": [
124,
152
]
},
{
"type": "irq_pair",
"value": [
85,
95
]
}
]
}
@@ -0,0 +1,374 @@
/*
* 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 "boot_functions.hpp"
#include "boot_battery_driver.hpp"
const Max17050Parameters *BatteryDriver::GetBatteryParameters() {
const u32 battery_version = Boot::GetBatteryVersion();
const u32 battery_vendor = Boot::GetBatteryVendor();
if (battery_version == 2) {
if (battery_vendor == 'M') {
return &Max17050Params2M;
} else {
return &Max17050Params2;
}
} else if (battery_version == 1) {
return &Max17050Params1;
} else {
switch (battery_vendor) {
case 'M':
return &Max17050ParamsM;
case 'R':
return &Max17050ParamsR;
case 'A':
default:
return &Max17050ParamsA;
}
}
}
Result BatteryDriver::Read(u8 addr, u16 *out) {
return Boot::ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
}
Result BatteryDriver::Write(u8 addr, u16 val) {
return Boot::WriteI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(&val), sizeof(val), &addr, sizeof(addr));
}
Result BatteryDriver::ReadWrite(u8 addr, u16 mask, u16 val) {
Result rc;
u16 cur_val;
if (R_FAILED((rc = this->Read(addr, &cur_val)))) {
return rc;
}
const u16 new_val = (cur_val & ~mask) | val;
if (R_FAILED((rc = this->Write(addr, new_val)))) {
return rc;
}
return ResultSuccess;
}
bool BatteryDriver::WriteValidate(u8 addr, u16 val) {
/* Nintendo doesn't seem to check errors when doing this? */
/* It's probably okay, since the value does get validated. */
/* That said, we will validate the read to avoid uninit data problems. */
this->Write(addr, val);
svcSleepThread(3'000'000ul);
u16 new_val;
return R_SUCCEEDED(this->Read(addr, &new_val)) && new_val == val;
}
bool BatteryDriver::IsPowerOnReset() {
/* N doesn't check result... */
u16 val = 0;
this->Read(Max17050Status, &val);
return (val & 0x0002) == 0x0002;
}
Result BatteryDriver::LockVfSoc() {
return this->Write(Max17050SocVfAccess, 0x0000);
}
Result BatteryDriver::UnlockVfSoc() {
return this->Write(Max17050SocVfAccess, 0x0080);
}
Result BatteryDriver::LockModelTable() {
Result rc;
if (R_FAILED((rc = this->Write(Max17050ModelAccess0, 0x0000)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050ModelAccess1, 0x0000)))) {
return rc;
}
return ResultSuccess;
}
Result BatteryDriver::UnlockModelTable() {
Result rc;
if (R_FAILED((rc = this->Write(Max17050ModelAccess0, 0x0059)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050ModelAccess1, 0x00C4)))) {
return rc;
}
return ResultSuccess;
}
Result BatteryDriver::SetModelTable(const u16 *model_table) {
Result rc;
for (size_t i = 0; i < Max17050ModelChrTblSize; i++) {
if (R_FAILED((rc = this->Write(Max17050ModelChrTblStart + i, model_table[i])))) {
return rc;
}
}
return ResultSuccess;
}
bool BatteryDriver::IsModelTableLocked() {
bool locked = true;
u16 cur_val = 0;
for (size_t i = 0; i < Max17050ModelChrTblSize; i++) {
this->Read(Max17050ModelChrTblStart + i, &cur_val);
locked &= (cur_val == 0);
}
return locked;
}
bool BatteryDriver::IsModelTableSet(const u16 *model_table) {
bool set = true;
u16 cur_val = 0;
for (size_t i = 0; i < Max17050ModelChrTblSize; i++) {
this->Read(Max17050ModelChrTblStart + i, &cur_val);
set &= (cur_val == model_table[i]);
}
return set;
}
Result BatteryDriver::InitializeBatteryParameters() {
const Max17050Parameters *params = GetBatteryParameters();
Result rc = ResultSuccess;
if (IsPowerOnReset()) {
/* Do initial config. */
if (R_FAILED((rc = this->ReadWrite(Max17050MiscCfg, 0x8000, 0x8000)))) {
return rc;
}
svcSleepThread(500'000'000ul);
if (R_FAILED((rc = this->Write(Max17050Config, 0x7210)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050FilterCfg, 0x8784)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050RelaxCfg, params->relaxcfg)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050LearnCfg, 0x2603)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050FullSocThr, params->fullsocthr)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050IAvgEmpty, params->iavgempty)))) {
return rc;
}
/* Unlock model table, write model table. */
do {
if (R_FAILED((rc = this->UnlockModelTable()))) {
return rc;
}
if (R_FAILED((rc = this->SetModelTable(params->modeltbl)))) {
return rc;
}
} while (!this->IsModelTableSet(params->modeltbl));
/* Lock model table. */
size_t lock_i = 0;
while (true) {
lock_i++;
if (R_FAILED((rc = this->LockModelTable()))) {
return rc;
}
if (this->IsModelTableLocked()) {
break;
}
if (lock_i >= 8) {
/* This is regarded as guaranteed success. */
return ResultSuccess;
}
}
/* Write custom parameters. */
while (!this->WriteValidate(Max17050RComp0, params->rcomp0)) { /* ... */ }
while (!this->WriteValidate(Max17050TempCo, params->tempco)) { /* ... */ }
if (R_FAILED((rc = this->Write(Max17050IChgTerm, params->ichgterm)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050TGain, params->tgain)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050TOff, params->toff)))) {
return rc;
}
while (!this->WriteValidate(Max17050VEmpty, params->vempty)) { /* ... */ }
while (!this->WriteValidate(Max17050QResidual00, params->qresidual00)) { /* ... */ }
while (!this->WriteValidate(Max17050QResidual10, params->qresidual10)) { /* ... */ }
while (!this->WriteValidate(Max17050QResidual20, params->qresidual20)) { /* ... */ }
while (!this->WriteValidate(Max17050QResidual30, params->qresidual30)) { /* ... */ }
/* Write full capacity parameters. */
while (!this->WriteValidate(Max17050FullCap, params->fullcap)) { /* ... */ }
if (R_FAILED((rc = this->Write(Max17050DesignCap, params->vffullcap)))) {
return rc;
}
while (!this->WriteValidate(Max17050FullCapNom, params->vffullcap)) { /* ... */ }
svcSleepThread(350'000'000ul);
/* Write VFSOC to VFSOC 0. */
u16 vfsoc, qh;
{
if (R_FAILED((rc = this->Read(Max17050SocVf, &vfsoc)))) {
return rc;
}
if (R_FAILED((rc = this->UnlockVfSoc()))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050SocVf0, vfsoc)))) {
return rc;
}
if (R_FAILED((rc = this->Read(Max17050Qh, &qh)))) {
return rc;
}
if (R_FAILED((rc = this->Write(Max17050Qh0, qh)))) {
return rc;
}
if (R_FAILED((rc = this->LockVfSoc()))) {
return rc;
}
}
/* Write cycles. */
while (!this->WriteValidate(Max17050Cycles, 0x0060)) { /* ... */ }
/* Load new capacity parameters. */
const u16 remcap = static_cast<u16>((vfsoc * params->vffullcap) / 0x6400);
const u16 repcap = static_cast<u16>(remcap * (params->fullcap / params->vffullcap));
const u16 dqacc = params->vffullcap / 0x10;
while (!this->WriteValidate(Max17050RemCapMix, remcap)) { /* ... */ }
while (!this->WriteValidate(Max17050RemCapRep, repcap)) { /* ... */ }
while (!this->WriteValidate(Max17050DPAcc, 0x0C80)) { /* ... */ }
while (!this->WriteValidate(Max17050DQAcc, dqacc)) { /* ... */ }
while (!this->WriteValidate(Max17050FullCap, params->fullcap)) { /* ... */ }
if (R_FAILED((rc = this->Write(Max17050DesignCap, params->vffullcap)))) {
return rc;
}
while (!this->WriteValidate(Max17050FullCapNom, params->vffullcap)) { /* ... */ }
if (R_FAILED((rc = this->Write(Max17050SocRep, vfsoc)))) {
return rc;
}
/* Finish initialization. */
{
u16 status;
if (R_FAILED((rc = this->Read(Max17050Status, &status)))) {
return rc;
}
while (!this->WriteValidate(Max17050Status, status & 0xFFFD)) { /* ... */ }
}
if (R_FAILED((rc = this->Write(Max17050CGain, 0x7FFF)))) {
return rc;
}
}
return ResultSuccess;
}
Result BatteryDriver::IsBatteryRemoved(bool *out) {
/* N doesn't check result, but we will. */
u16 val = 0;
Result rc = this->Read(Max17050Status, &val);
if (R_FAILED(rc)) {
return rc;
}
*out = (val & 0x0008) == 0x0008;
return ResultSuccess;
}
Result BatteryDriver::GetTemperature(double *out) {
u16 val = 0;
Result rc = this->Read(Max17050Temperature, &val);
if (R_FAILED(rc)) {
return rc;
}
*out = static_cast<double>(val) * double(0.00390625);
return ResultSuccess;
}
Result BatteryDriver::GetAverageVCell(u32 *out) {
u16 val = 0;
Result rc = this->Read(Max17050AverageVCell, &val);
if (R_FAILED(rc)) {
return rc;
}
*out = (625 * u32(val >> 3)) / 1000;
return ResultSuccess;
}
Result BatteryDriver::GetSocRep(double *out) {
u16 val = 0;
Result rc = this->Read(Max17050SocRep, &val);
if (R_FAILED(rc)) {
return rc;
}
*out = static_cast<double>(val) * double(0.00390625);
return ResultSuccess;
}
Result BatteryDriver::GetBatteryPercentage(size_t *out) {
double raw_charge;
Result rc = this->GetSocRep(&raw_charge);
if (R_FAILED(rc)) {
return rc;
}
int converted_percentage = (((raw_charge - 3.93359375) * 98.0) / 94.2304688) + 2.0;
if (converted_percentage < 1) {
*out = 1;
} else if (converted_percentage > 100) {
*out = 100;
} else {
*out = static_cast<size_t>(converted_percentage);
}
return ResultSuccess;
}
Result BatteryDriver::SetShutdownTimer() {
return this->Write(Max17050ShdnTimer, 0xE000);
}
Result BatteryDriver::GetShutdownEnabled(bool *out) {
u16 val = 0;
Result rc = this->Read(Max17050Config, &val);
if (R_FAILED(rc)) {
return rc;
}
*out = (val & 0x0040) != 0;
return ResultSuccess;
}
Result BatteryDriver::SetShutdownEnabled(bool enabled) {
return this->ReadWrite(Max17050Config, 0x0040, enabled ? 0x0040 : 0x0000);
}
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c_driver/i2c_api.hpp"
#include "boot_battery_parameters.hpp"
class BatteryDriver {
private:
I2cSessionImpl i2c_session;
public:
BatteryDriver() {
I2cDriver::Initialize();
I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max17050);
}
~BatteryDriver() {
I2cDriver::CloseSession(this->i2c_session);
I2cDriver::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);
bool WriteValidate(u8 addr, u16 val);
bool IsPowerOnReset();
Result LockVfSoc();
Result UnlockVfSoc();
Result LockModelTable();
Result UnlockModelTable();
bool IsModelTableLocked();
Result SetModelTable(const u16 *model_table);
bool IsModelTableSet(const u16 *model_table);
public:
Result InitializeBatteryParameters();
Result IsBatteryRemoved(bool *out);
Result GetTemperature(double *out);
Result GetAverageVCell(u32 *out);
Result GetSocRep(double *out);
Result GetBatteryPercentage(size_t *out);
Result SetShutdownTimer();
Result GetShutdownEnabled(bool *out);
Result SetShutdownEnabled(bool enabled);
};
@@ -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 "boot_types.hpp"
static constexpr size_t ChargingBatteryX = 26;
static constexpr size_t ChargingBatteryY = 28;
static constexpr size_t ChargingBatteryW = 76;
static constexpr size_t ChargingBatteryH = 28;
static constexpr size_t ChargingBatteryMeterX = 7;
static constexpr size_t ChargingBatteryMeterY = 8;
static constexpr size_t ChargingBatteryMeterW = 31;
static constexpr size_t ChargingBatteryMeterH = 12;
static_assert(ChargingBatteryMeterX + ChargingBatteryMeterW < ChargingBatteryW && ChargingBatteryMeterY + ChargingBatteryMeterH < ChargingBatteryH, "Incorrect ChargingBatteryMeter definition!");
static constexpr u32 ChargingBattery[] = {0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF3D3D3D, 0xFFB2B2B2, 0xFFE6E6E6, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE6E6E6, 0xFFB2B2B2, 0xFF3D3D3D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF7A7A7A, 0xFFFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFF7A7A7A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF3D3D3D, 0xFFFDFDFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFF3D3D3D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB2B2B2, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFB2B2B2, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF494949, 0xFFE2E2E2, 0xFFE3E3E3, 0xFF494949, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFE6E6E6, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF4A4A4A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0Line truncated
static_assert(sizeof(ChargingBattery) == sizeof(u32) * ChargingBatteryW * ChargingBatteryH, "Incorrect ChargingBattery definition!");
@@ -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 "boot_types.hpp"
static constexpr size_t ChargingRedBatteryX = 26;
static constexpr size_t ChargingRedBatteryY = 28;
static constexpr size_t ChargingRedBatteryW = 76;
static constexpr size_t ChargingRedBatteryH = 28;
static constexpr size_t ChargingRedBatteryMeterX = 7;
static constexpr size_t ChargingRedBatteryMeterY = 8;
static constexpr size_t ChargingRedBatteryMeterW = 31;
static constexpr size_t ChargingRedBatteryMeterH = 12;
static_assert(ChargingRedBatteryMeterX + ChargingRedBatteryMeterW < ChargingRedBatteryW && ChargingRedBatteryMeterY + ChargingRedBatteryMeterH < ChargingRedBatteryH, "Incorrect ChargingRedBatteryMeter definition!");
static constexpr u32 ChargingRedBattery[] = {0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF3D3D3D, 0xFFB2B2B2, 0xFFE6E6E6, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE6E6E6, 0xFFB2B2B2, 0xFF3D3D3D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF7A7A7A, 0xFFFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFF7A7A7A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF3D3D3D, 0xFFFDFDFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFF3D3D3D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB2B2B2, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFB2B2B2, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF494949, 0xFFE2E2E2, 0xFFE3E3E3, 0xFF494949, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFE6E6E6, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF4A4A4A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000Line truncated
static_assert(sizeof(ChargingRedBattery) == sizeof(u32) * ChargingRedBatteryW * ChargingRedBatteryH, "Incorrect ChargingRedBattery definition!");
@@ -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 "boot_types.hpp"
static constexpr size_t LowBatteryX = 26;
static constexpr size_t LowBatteryY = 28;
static constexpr size_t LowBatteryW = 52;
static constexpr size_t LowBatteryH = 28;
static constexpr u32 LowBattery[] = {0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF3D3D3D, 0xFFB2B2B2, 0xFFE6E6E6, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE6E6E6, 0xFFB2B2B2, 0xFF3D3D3D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF7A7A7A, 0xFFFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFF7A7A7A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF3D3D3D, 0xFFFDFDFD, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFF3D3D3D, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFB2B2B2, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFB2B2B2, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFE6E6E6, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF4A4A4A, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF4B4B4B, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE6E6E6, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF00Line truncated
static_assert(sizeof(LowBattery) == sizeof(u32) * LowBatteryW * LowBatteryH, "Incorrect LowBattery definition!");
@@ -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 "boot_functions.hpp"
#include "boot_battery_icon_low.hpp"
#include "boot_battery_icon_charging.hpp"
#include "boot_battery_icon_charging_red.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();
}
static void FillBatteryMeter(u32 *icon, const size_t icon_w, const size_t icon_h, const size_t meter_x, const size_t meter_y, const size_t meter_w, const size_t meter_h, const size_t fill_w) {
const size_t fill_x = meter_x + meter_w - fill_w;
if (fill_x + fill_w > icon_w || meter_y + meter_h > icon_h || fill_x == 0) {
return;
}
u32 *cur_row = icon + meter_y * icon_w + fill_x;
for (size_t y = 0; y < meter_h; y++) {
/* Make last column of meter identical to first column of meter. */
cur_row[-1] = icon[(meter_y + y) * icon_w + meter_x];
/* Black out further pixels. */
for (size_t x = 0; x < fill_w; x++) {
cur_row[x] = 0xFF000000;
}
cur_row += icon_w;
}
}
void Boot::StartShowChargingIcon(size_t battery_percentage, bool wait) {
const bool is_red = battery_percentage <= 15;
const size_t IconX = is_red ? ChargingRedBatteryX : ChargingBatteryX;
const size_t IconY = is_red ? ChargingRedBatteryY : ChargingBatteryY;
const size_t IconW = is_red ? ChargingRedBatteryW : ChargingBatteryW;
const size_t IconH = is_red ? ChargingRedBatteryH : ChargingBatteryH;
const size_t IconMeterX = is_red ? ChargingRedBatteryMeterX : ChargingBatteryMeterX;
const size_t IconMeterY = is_red ? ChargingRedBatteryMeterY : ChargingBatteryMeterY;
const size_t IconMeterW = is_red ? ChargingRedBatteryMeterW : ChargingBatteryMeterW;
const size_t IconMeterH = is_red ? ChargingRedBatteryMeterH : ChargingBatteryMeterH;
const size_t MeterFillW = static_cast<size_t>(IconMeterW * (1.0 - (0.0404 + 0.0096 * battery_percentage)) + 0.5);
/* Create stack buffer, copy icon into it, draw fill meter, draw. */
{
u32 Icon[IconW * IconH];
std::memcpy(Icon, is_red ? ChargingRedBattery : ChargingBattery, sizeof(Icon));
FillBatteryMeter(Icon, IconW, IconH, IconMeterX, IconMeterY, IconMeterW, IconMeterH, MeterFillW);
Boot::InitializeDisplay();
Boot::ShowDisplay(IconX, IconY, IconW, IconH, Icon);
}
/* Wait for 2 seconds if we're supposed to. */
if (wait) {
svcSleepThread(2'000'000'000ul);
}
}
void Boot::EndShowChargingIcon() {
Boot::FinalizeDisplay();
}
@@ -0,0 +1,286 @@
/*
* 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>
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;
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;
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;
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;
static 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;
static constexpr u8 Max17050FStat = 0x3D;
static constexpr u8 Max17050Timer = 0x3E;
static constexpr u8 Max17050ShdnTimer = 0x3F;
static constexpr u8 Max17050QResidual30 = 0x42;
static constexpr u8 Max17050DQAcc = 0x45;
static constexpr u8 Max17050DPAcc = 0x46;
static constexpr u8 Max17050SocVf0 = 0x48;
static constexpr u8 Max17050Qh0 = 0x4C;
static constexpr u8 Max17050Qh = 0x4D;
static constexpr u8 Max17050SocVfAccess = 0x60;
static constexpr u8 Max17050ModelAccess0 = 0x62;
static constexpr u8 Max17050ModelAccess1 = 0x63;
static constexpr u8 Max17050ModelChrTblStart = 0x80;
static constexpr u8 Max17050ModelChrTblEnd = 0xB0;
static constexpr u8 Max17050VFocV = 0xFB;
static constexpr u8 Max17050SocVf = 0xFF;
static constexpr size_t Max17050ModelChrTblSize = Max17050ModelChrTblEnd - Max17050ModelChrTblStart;
struct Max17050Parameters {
u16 relaxcfg;
u16 rcomp0;
u16 tempco;
u16 ichgterm;
u16 tgain;
u16 toff;
u16 vempty;
u16 qresidual00;
u16 qresidual10;
u16 qresidual20;
u16 qresidual30;
u16 fullcap;
u16 vffullcap;
u16 modeltbl[Max17050ModelChrTblSize];
u16 fullsocthr;
u16 iavgempty;
};
static_assert(sizeof(Max17050Parameters) == 0x7E, "Max17050Parameters definition!");
static constexpr Max17050Parameters Max17050ParamsA = {
0x203B, /* relaxcfg */
0x0053, /* rcomp0 */
0x1C22, /* tempco */
0x0333, /* ichgterm */
0xE1F6, /* tgain */
0x2BF2, /* toff */
0xA05F, /* vempty */
0x5786, /* qresidual00 */
0x3184, /* qresidual10 */
0x1E00, /* qresidual20 */
0x1602, /* qresidual30 */
0x2476, /* fullcap */
0x2476, /* vffullcap */
{ /* modeltbl */
0x9FF0, 0xAD30, 0xB5D0, 0xB9C0, 0xBAD0, 0xBBE0, 0xBC30, 0xBC90,
0xBCE0, 0xBD40, 0xBE70, 0xC0E0, 0xC4E0, 0xC890, 0xCC90, 0xD0F0,
0x0170, 0x0480, 0x0590, 0x0BE0, 0x0A00, 0x3C00, 0x3810, 0x3A00,
0x3A30, 0x19F0, 0x0EF0, 0x0AF0, 0x0BD0, 0x07F0, 0x06F0, 0x06F0,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
},
0x5F00, /* fullsocthr */
0x1D2A /* iavgempty */
};
static constexpr Max17050Parameters Max17050ParamsM = {
0x203B, /* relaxcfg */
0x0085, /* rcomp0 */
0x1625, /* tempco */
0x0333, /* ichgterm */
0xE1F6, /* tgain */
0x2BF2, /* toff */
0xA05F, /* vempty */
0x3100, /* qresidual00 */
0x1B00, /* qresidual10 */
0x1000, /* qresidual20 */
0x0C81, /* qresidual30 */
0x227A, /* fullcap */
0x227A, /* vffullcap */
{ /* modeltbl */
0xA340, 0xB840, 0xB900, 0xBB70, 0xBC90, 0xBD20, 0xBDC0, 0xBEA0,
0xBF70, 0xC030, 0xC210, 0xC3F0, 0xC800, 0xC9E0, 0xCCA0, 0xD090,
0x0160, 0x3800, 0x0800, 0x1E00, 0x2550, 0x3060, 0x15D0, 0x1810,
0x1490, 0x0B80, 0x0BF0, 0x0AF0, 0x0CB0, 0x06F0, 0x09D0, 0x09D0,
0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
},
0x5F00, /* fullsocthr */
0x1D2A /* iavgempty */
};
static constexpr Max17050Parameters Max17050ParamsR = {
0x203B, /* relaxcfg */
0x0048, /* rcomp0 */
0x2034, /* tempco */
0x0333, /* ichgterm */
0xE1F6, /* tgain */
0x2BF2, /* toff */
0xA05F, /* vempty */
0x5A00, /* qresidual00 */
0x3B00, /* qresidual10 */
0x0F80, /* qresidual20 */
0x0B02, /* qresidual30 */
0x2466, /* fullcap */
0x2466, /* vffullcap */
{ /* modeltbl */
0x9C50, 0xAD90, 0xB270, 0xB6A0, 0xB8F0, 0xBB10, 0xBC00, 0xBD00,
0xBD70, 0xBE70, 0xBF50, 0xC1F0, 0xC380, 0xC590, 0xC8E0, 0xD0B0,
0x00D0, 0x0150, 0x0300, 0x0D00, 0x0E00, 0x1900, 0x2AC0, 0x2830,
0x1760, 0x18F0, 0x0DF0, 0x0BC0, 0x0DF0, 0x0BF0, 0x06F0, 0x06F0,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
},
0x5F00, /* fullsocthr */
0x1D2A /* iavgempty */
};
static constexpr Max17050Parameters Max17050Params1 = {
0x203B, /* relaxcfg */
0x0040, /* rcomp0 */
0x1624, /* tempco */
0x0333, /* ichgterm */
0xE1F6, /* tgain */
0x2BF2, /* toff */
0xA05F, /* vempty */
0x4690, /* qresidual00 */
0x2605, /* qresidual10 */
0x1605, /* qresidual20 */
0x0F05, /* qresidual30 */
0x1AE4, /* fullcap */
0x1AE4, /* vffullcap */
{ /* modeltbl */
0x8B50, 0x9C20, 0xACF0, 0xB160, 0xB3A0, 0xB5B0, 0xB950, 0xBBE0,
0xBDC0, 0xBEF0, 0xC140, 0xC250, 0xC600, 0xC960, 0xCCE0, 0xD060,
0x0070, 0x00F0, 0x0440, 0x0400, 0x0500, 0x0400, 0x0D00, 0x3270,
0x0FB0, 0x0AF0, 0x10F0, 0x0CE0, 0x09E0, 0x07F0, 0x06F0, 0x06F0,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
},
0x5F00, /* fullsocthr */
0x1584 /* iavgempty */
};
static constexpr Max17050Parameters Max17050Params2 = {
0x203B, /* relaxcfg */
0x004A, /* rcomp0 */
0x1D23, /* tempco */
0x0333, /* ichgterm */
0xE1F6, /* tgain */
0x2BF2, /* toff */
0xA05F, /* vempty */
0x4000, /* qresidual00 */
0x1E80, /* qresidual10 */
0x0D83, /* qresidual20 */
0x0783, /* qresidual30 */
0x1C20, /* fullcap */
0x1C20, /* vffullcap */
{ /* modeltbl */
0x8040, 0x9A30, 0xB430, 0xB770, 0xBAB0, 0xBBC0, 0xBD00, 0xBE50,
0xBF70, 0xC0D0, 0xC300, 0xC590, 0xC960, 0xCD40, 0xD1F0, 0xD5C0,
0x0040, 0x0060, 0x0510, 0x0D30, 0x16C0, 0x2160, 0x1380, 0x1A10,
0x0EC0, 0x0CE0, 0x08F0, 0x0940, 0x0920, 0x06F0, 0x06C0, 0x06C0,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
},
0x5500, /* fullsocthr */
0x1680 /* iavgempty */
};
static constexpr Max17050Parameters Max17050Params2M = {
0x203B, /* relaxcfg */
0x0049, /* rcomp0 */
0x222A, /* tempco */
0x0333, /* ichgterm */
0xE1F6, /* tgain */
0x2BF2, /* toff */
0xA05F, /* vempty */
0x4F00, /* qresidual00 */
0x2680, /* qresidual10 */
0x1205, /* qresidual20 */
0x0C87, /* qresidual30 */
0x1C68, /* fullcap */
0x1C68, /* vffullcap */
{ /* modeltbl */
0x8E40, 0xB570, 0xB8F0, 0xBB00, 0xBC20, 0xBCC0, 0xBE30, 0xBFE0,
0xC200, 0xC400, 0xC720, 0xCB50, 0xCF00, 0xD100, 0xD480, 0xD5C0,
0x00C0, 0x0C00, 0x0A10, 0x1800, 0x2C00, 0x1C10, 0x12D0, 0x09F0,
0x0AF0, 0x0850, 0x09F0, 0x06F0, 0x06B0, 0x07E0, 0x01D0, 0x01D0,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
},
0x5500, /* fullsocthr */
0x16B9 /* iavgempty */
};
@@ -0,0 +1,113 @@
/*
* 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_pmic_driver.hpp"
#include "boot_rtc_driver.hpp"
static u32 g_boot_reason = 0;
static bool g_detected_boot_reason = false;
struct BootReasonValue {
union {
struct {
u8 power_intr;
u8 rtc_intr;
u8 nv_erc;
u8 boot_reason;
};
u32 value;
};
};
static u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) {
if (power_intr & 0x08) {
return 2;
}
if (rtc_intr & 0x02) {
return 3;
}
if (power_intr & 0x80) {
return 1;
}
if (rtc_intr & 0x04) {
if (nv_erc != 0x80 && !Boot::IsRecoveryBoot()) {
return 4;
}
}
if ((nv_erc & 0x40) && ac_ok) {
return 1;
}
return 0;
}
void Boot::DetectBootReason() {
u8 power_intr;
u8 rtc_intr;
u8 rtc_intr_m;
u8 nv_erc;
bool ac_ok;
/* Get values from PMIC. */
{
PmicDriver pmic_driver;
if (R_FAILED(pmic_driver.GetPowerIntr(&power_intr))) {
std::abort();
}
if (R_FAILED(pmic_driver.GetNvErc(&nv_erc))) {
std::abort();
}
if (R_FAILED(pmic_driver.GetAcOk(&ac_ok))) {
std::abort();
}
}
/* Get values from RTC. */
{
RtcDriver rtc_driver;
if (R_FAILED(rtc_driver.GetRtcIntr(&rtc_intr))) {
std::abort();
}
if (R_FAILED(rtc_driver.GetRtcIntrM(&rtc_intr_m))) {
std::abort();
}
}
/* Set global derived boot reason. */
g_boot_reason = MakeBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok);
/* Set boot reason for SPL. */
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) {
BootReasonValue boot_reason_value;
boot_reason_value.power_intr = power_intr;
boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m;
boot_reason_value.nv_erc = nv_erc;
boot_reason_value.boot_reason = g_boot_reason;
if (R_FAILED(splSetBootReason(boot_reason_value.value))) {
std::abort();
}
}
g_detected_boot_reason = true;
}
u32 Boot::GetBootReason() {
if (!g_detected_boot_reason) {
std::abort();
}
return g_boot_reason;
}
@@ -0,0 +1,139 @@
/*
* 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>
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;
enum ChargerConfiguration : u8 {
ChargerConfiguration_ChargeDisable = (0 << 4),
ChargerConfiguration_ChargeBattery = (1 << 4),
ChargerConfiguration_Otg = (2 << 4),
};
static constexpr u32 ChargeVoltageLimitMin = 3504;
static constexpr u32 ChargeVoltageLimitMax = 4208;
static inline u8 EncodeChargeVoltageLimit(u32 voltage) {
if (voltage < ChargeVoltageLimitMin || voltage > ChargeVoltageLimitMax) {
std::abort();
}
voltage -= ChargeVoltageLimitMin;
voltage >>= 4;
return static_cast<u8>(voltage << 2);
}
static inline u32 DecodeChargeVoltageLimit(u8 reg) {
return ChargeVoltageLimitMin + (static_cast<u32>(reg & 0xFC) << 2);
}
static constexpr u32 FastChargeCurrentLimitMin = 512;
static constexpr u32 FastChargeCurrentLimitMax = 4544;
static inline u8 EncodeFastChargeCurrentLimit(u32 current) {
if (current < FastChargeCurrentLimitMin || current > FastChargeCurrentLimitMax) {
std::abort();
}
current -= FastChargeCurrentLimitMin;
current >>= 6;
return static_cast<u8>(current << 2);
}
static inline u32 DecodeFastChargeCurrentLimit(u8 reg) {
return FastChargeCurrentLimitMin + (static_cast<u32>(reg & 0xFC) << 4);
}
enum InputCurrentLimit : u8 {
InputCurrentLimit_100mA = 0,
InputCurrentLimit_150mA = 1,
InputCurrentLimit_500mA = 2,
InputCurrentLimit_900mA = 3,
InputCurrentLimit_1200mA = 4,
InputCurrentLimit_1500mA = 5,
InputCurrentLimit_2000mA = 6,
InputCurrentLimit_3000mA = 7,
};
static constexpr u32 PreChargeCurrentLimitMin = 128;
static constexpr u32 PreChargeCurrentLimitMax = 2048;
static inline u8 EncodePreChargeCurrentLimit(u32 current) {
if (current < PreChargeCurrentLimitMin || current > PreChargeCurrentLimitMax) {
std::abort();
}
current -= PreChargeCurrentLimitMin;
current >>= 7;
return static_cast<u8>(current << 4);
}
static inline u32 DecodePreChargeCurrentLimit(u8 reg) {
return PreChargeCurrentLimitMin + (static_cast<u32>(reg & 0xF0) << 3);
}
static constexpr u32 TerminationCurrentLimitMin = 128;
static constexpr u32 TerminationCurrentLimitMax = 2048;
static inline u8 EncodeTerminationCurrentLimit(u32 current) {
if (current < TerminationCurrentLimitMin || current > TerminationCurrentLimitMax) {
std::abort();
}
current -= TerminationCurrentLimitMin;
current >>= 7;
return static_cast<u8>(current);
}
static inline u32 DecodeTerminationCurrentLimit(u8 reg) {
return TerminationCurrentLimitMin + (static_cast<u32>(reg & 0xF) << 7);
}
static constexpr u32 MinimumSystemVoltageLimitMin = 3000;
static constexpr u32 MinimumSystemVoltageLimitMax = 3700;
static inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) {
if (voltage < MinimumSystemVoltageLimitMin || voltage > MinimumSystemVoltageLimitMax) {
std::abort();
}
voltage -= MinimumSystemVoltageLimitMin;
voltage /= 100;
return static_cast<u8>(voltage << 1);
}
static inline u32 DecodeMinimumSystemVoltageLimit(u8 reg) {
return MinimumSystemVoltageLimitMin + (static_cast<u32>(reg & 0x0E) * 50);
}
enum WatchdogTimerSetting : u8 {
WatchdogTimerSetting_Disabled = (0 << 4),
WatchdogTimerSetting_40s = (1 << 4),
WatchdogTimerSetting_80s = (2 << 4),
WatchdogTimerSetting_160s = (3 << 4),
};
enum BoostModeCurrentLimit : u8 {
BoostModeCurrentLimit_500mA = 0,
BoostModeCurrentLimit_1300mA = 1,
};
@@ -0,0 +1,114 @@
/*
* 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"
static constexpr size_t BatteryLotOffset = 0x2CE0;
static constexpr size_t BatteryLotSize = 0x20;
static constexpr size_t BatteryVersionOffset = 0x4310;
static constexpr size_t BatteryVersionSize = 0x10;
static constexpr u32 BisStorageId_Prodinfo = 27;
static constexpr u32 DefaultBatteryVendor = static_cast<u32>('A');
static constexpr u32 DefaultBatteryVersion = 0;
static constexpr Result ResultCalInvalidCrc = 0xCAC6; /* TODO: Verify this really is cal, move to libstrat results. */
u16 Boot::GetCrc16(const void *data, size_t size) {
static constexpr u16 s_crc_table[0x10] = {
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401,
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400
};
if (data == nullptr) {
std::abort();
}
u16 crc16 = 0x55AA;
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
for (size_t i = 0; i < size; i++) {
crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[data_u8[i] & 0xF]);
crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[(data_u8[i] >> 4) & 0xF]);
}
return crc16;
}
static Result ValidateCalibrationCrc16(const void *data, size_t size) {
const u8 *data_u8 = reinterpret_cast<const u8 *>(data);
if (Boot::GetCrc16(data, size - sizeof(u16)) != *(reinterpret_cast<const u16 *>(&data_u8[size - sizeof(u16)]))) {
return ResultCalInvalidCrc;
}
return ResultSuccess;
}
static Result GetBatteryVendorImpl(u32 *vendor) {
FsStorage s;
Result rc = fsOpenBisStorage(&s, BisStorageId_Prodinfo);
if (R_FAILED(rc)) {
return rc;
}
ON_SCOPE_EXIT { fsStorageClose(&s); };
u8 battery_lot[BatteryLotSize];
if (R_FAILED((rc = fsStorageRead(&s, BatteryLotOffset, battery_lot, sizeof(battery_lot))))) {
return rc;
}
if (R_FAILED((rc = ValidateCalibrationCrc16(battery_lot, sizeof(battery_lot))))) {
return rc;
}
*vendor = battery_lot[7];
return ResultSuccess;
}
static Result GetBatteryVersionImpl(u32 *version) {
FsStorage s;
Result rc = fsOpenBisStorage(&s, BisStorageId_Prodinfo);
if (R_FAILED(rc)) {
return rc;
}
ON_SCOPE_EXIT { fsStorageClose(&s); };
u8 battery_version[BatteryVersionSize];
if (R_FAILED((rc = fsStorageRead(&s, BatteryVersionOffset, battery_version, sizeof(battery_version))))) {
return rc;
}
if (R_FAILED((rc = ValidateCalibrationCrc16(battery_version, sizeof(battery_version))))) {
return rc;
}
*version = battery_version[0];
return ResultSuccess;
}
u32 Boot::GetBatteryVendor() {
u32 vendor;
if (R_FAILED(GetBatteryVendorImpl(&vendor))) {
return DefaultBatteryVendor;
}
return vendor;
}
u32 Boot::GetBatteryVersion() {
u32 version;
if (R_FAILED(GetBatteryVersionImpl(&version))) {
return DefaultBatteryVersion;
}
return version;
}
@@ -0,0 +1,36 @@
/*
* 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"
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 */
static constexpr u32 VoltageChangeMask = SpiVoltageBit | GpioVoltageBit | AudioVoltageBit | Sdmmc3VoltageBit;
static constexpr u32 PmcPwrDet = 0x7000E448;
static constexpr u32 PmcPwrDetVal = 0x7000E4E4;
void Boot::ChangeGpioVoltageTo1_8v() {
/* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */
WritePmcRegister(PmcPwrDet, VoltageChangeMask, VoltageChangeMask);
WritePmcRegister(PmcPwrDetVal, 0, VoltageChangeMask);
/* Sleep for 100 us. */
svcSleepThread(100'000ul);
}
@@ -0,0 +1,177 @@
/*
* 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 "boot_charger_driver.hpp"
Result ChargerDriver::Read(u8 addr, u8 *out) {
return Boot::ReadI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(out), sizeof(*out), &addr, sizeof(addr));
}
Result ChargerDriver::Write(u8 addr, u8 val) {
return Boot::WriteI2cRegister(this->i2c_session, reinterpret_cast<u8 *>(&val), sizeof(val), &addr, sizeof(addr));
}
Result ChargerDriver::ReadWrite(u8 addr, u8 mask, u8 val) {
Result rc;
u8 cur_val;
if (R_FAILED((rc = this->Read(addr, &cur_val)))) {
return rc;
}
const u8 new_val = (cur_val & ~mask) | val;
if (R_FAILED((rc = this->Write(addr, new_val)))) {
return rc;
}
return ResultSuccess;
}
Result ChargerDriver::Initialize() {
return this->Initialize(true);
}
Result ChargerDriver::Initialize(bool set_input_current_limit) {
Result rc;
if (set_input_current_limit) {
if (R_FAILED((rc = this->SetInputCurrentLimit(InputCurrentLimit_500mA)))) {
return rc;
}
}
if (R_FAILED((rc = this->SetChargeVoltageLimit(4208)))) {
return rc;
}
if (R_FAILED((rc = this->SetFastChargeCurrentLimit(512)))) {
return rc;
}
if (R_FAILED((rc = this->SetForce20PercentChargeCurrent(false)))) {
return rc;
}
if (R_FAILED((rc = this->SetPreChargeCurrentLimit(128)))) {
return rc;
}
if (R_FAILED((rc = this->SetTerminationCurrentLimit(128)))) {
return rc;
}
if (R_FAILED((rc = this->SetMinimumSystemVoltageLimit(3000)))) {
return rc;
}
if (R_FAILED((rc = this->SetWatchdogTimerSetting(WatchdogTimerSetting_Disabled)))) {
return rc;
}
if (R_FAILED((rc = this->SetChargingSafetyTimerEnabled(false)))) {
return rc;
}
if (R_FAILED((rc = this->ResetWatchdogTimer()))) {
return rc;
}
if (R_FAILED((rc = this->SetBoostModeCurrentLimit(BoostModeCurrentLimit_500mA)))) {
return rc;
}
if (R_FAILED((rc = this->SetHiZEnabled(false)))) {
return rc;
}
return ResultSuccess;
}
Result ChargerDriver::SetChargeEnabled(bool enabled) {
Boot::GpioSetValue(GpioPadName_Bq24193Charger, enabled ? GpioValue_Low : GpioValue_High);
return this->SetChargerConfiguration(ChargerConfiguration_ChargeBattery);
}
Result ChargerDriver::SetChargerConfiguration(ChargerConfiguration config) {
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x30, config);
}
Result ChargerDriver::SetChargeVoltageLimit(u32 voltage) {
return this->ReadWrite(Bq24193ChargeVoltageControl, 0xFC, EncodeChargeVoltageLimit(voltage));
}
Result ChargerDriver::SetFastChargeCurrentLimit(u32 current) {
return this->ReadWrite(Bq24193ChargeCurrentControl, 0xFC, EncodeFastChargeCurrentLimit(current));
}
Result ChargerDriver::SetInputCurrentLimit(InputCurrentLimit current) {
return this->ReadWrite(Bq24193InputSourceControl, 0x07, current);
}
Result ChargerDriver::SetForce20PercentChargeCurrent(bool force) {
return this->ReadWrite(Bq24193ChargeCurrentControl, 0x01, force ? 1 : 0);
}
Result ChargerDriver::SetPreChargeCurrentLimit(u32 current) {
return this->ReadWrite(Bq24193PreChargeTerminationCurrentControl, 0xF0, EncodePreChargeCurrentLimit(current));
}
Result ChargerDriver::SetTerminationCurrentLimit(u32 current) {
return this->ReadWrite(Bq24193PreChargeTerminationCurrentControl, 0x0F, EncodeTerminationCurrentLimit(current));
}
Result ChargerDriver::SetMinimumSystemVoltageLimit(u32 voltage) {
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x0E, EncodeMinimumSystemVoltageLimit(voltage));
}
Result ChargerDriver::SetWatchdogTimerSetting(WatchdogTimerSetting setting) {
return this->ReadWrite(Bq24193ChargeTerminationTimerControl, 0x30, setting);
}
Result ChargerDriver::SetChargingSafetyTimerEnabled(bool enabled) {
return this->ReadWrite(Bq24193ChargeTerminationTimerControl, 0x08, enabled ? 0x08 : 0);
}
Result ChargerDriver::ResetWatchdogTimer() {
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x40, 0x40);
}
Result ChargerDriver::SetBoostModeCurrentLimit(BoostModeCurrentLimit current) {
return this->ReadWrite(Bq24193PowerOnConfiguration, 0x01, current);
}
Result ChargerDriver::SetHiZEnabled(bool enabled) {
return this->ReadWrite(Bq24193InputSourceControl, 0x80, enabled ? 0x80 : 0);
}
Result ChargerDriver::GetInputCurrentLimit(InputCurrentLimit *out) {
u8 limit;
Result rc = this->Read(Bq24193InputSourceControl, &limit);
if (R_FAILED(rc)) {
return rc;
}
*out = static_cast<InputCurrentLimit>(limit);
return ResultSuccess;
}
Result ChargerDriver::GetChargeVoltageLimit(u32 *out) {
u8 reg;
Result rc = this->Read(Bq24193ChargeVoltageControl, &reg);
if (R_FAILED(rc)) {
return rc;
}
*out = DecodeChargeVoltageLimit(reg);
return ResultSuccess;
}
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c_driver/i2c_api.hpp"
#include "boot_functions.hpp"
#include "boot_bq24193_charger.hpp"
class ChargerDriver {
private:
static constexpr u32 GpioPadName_Bq24193Charger = 0xA;
private:
I2cSessionImpl i2c_session;
public:
ChargerDriver() {
I2cDriver::Initialize();
I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Bq24193);
Boot::GpioSetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output);
}
~ChargerDriver() {
I2cDriver::CloseSession(this->i2c_session);
I2cDriver::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 SetForce20PercentChargeCurrent(bool force);
Result SetPreChargeCurrentLimit(u32 current);
Result SetTerminationCurrentLimit(u32 current);
Result SetMinimumSystemVoltageLimit(u32 voltage);
Result SetWatchdogTimerSetting(WatchdogTimerSetting setting);
Result SetChargingSafetyTimerEnabled(bool enabled);
Result ResetWatchdogTimer();
Result SetBoostModeCurrentLimit(BoostModeCurrentLimit current);
Result SetHiZEnabled(bool enabled);
public:
Result Initialize();
Result Initialize(bool set_input_current_limit);
Result SetChargeVoltageLimit(u32 voltage);
Result SetFastChargeCurrentLimit(u32 current);
Result SetChargeEnabled(bool enabled);
Result SetChargerConfiguration(ChargerConfiguration config);
Result GetInputCurrentLimit(InputCurrentLimit *out);
Result GetChargeVoltageLimit(u32 *out);
};
@@ -0,0 +1,275 @@
/*
* 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_pmic_driver.hpp"
#include "boot_battery_driver.hpp"
#include "boot_charger_driver.hpp"
enum CheckBatteryResult {
CheckBatteryResult_Success = 0,
CheckBatteryResult_Shutdown = 1,
CheckBatteryResult_Reboot = 2,
};
struct BatteryChargeParameters {
u32 temp_min;
u32 temp_low;
u32 temp_high;
u32 temp_max;
u32 allow_high_temp_charge_max_voltage;
u32 charge_voltage_limit_default;
u32 charge_voltage_limit_high_temp;
u32 allow_fast_charge_min_temp;
u32 allow_fast_charge_min_voltage;
u32 fast_charge_current_limit_default;
u32 fast_charge_current_limit_low_temp;
u32 fast_charge_current_limit_low_voltage;
};
static constexpr BatteryChargeParameters BatteryChargeParameters0 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 60,
.allow_high_temp_charge_max_voltage = 4050,
.charge_voltage_limit_default = 4208,
.charge_voltage_limit_high_temp = 3952,
.allow_fast_charge_min_voltage = 3320,
.fast_charge_current_limit_default = 0x800,
.fast_charge_current_limit_low_temp = 0x300,
.fast_charge_current_limit_low_voltage = 0x200,
};
static constexpr BatteryChargeParameters BatteryChargeParameters1 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 59,
.allow_high_temp_charge_max_voltage = 3984,
.charge_voltage_limit_default = 4208,
.charge_voltage_limit_high_temp = 3984,
.allow_fast_charge_min_voltage = 0,
.fast_charge_current_limit_default = 0x600,
.fast_charge_current_limit_low_temp = 0x240,
.fast_charge_current_limit_low_voltage = 0x600,
};
static constexpr BatteryChargeParameters BatteryChargeParameters2 = {
.temp_min = 4,
.temp_low = 17,
.temp_high = 51,
.temp_max = 59,
.allow_high_temp_charge_max_voltage = 4080,
.charge_voltage_limit_default = 4320,
.charge_voltage_limit_high_temp = 4080,
.allow_fast_charge_min_voltage = 0,
.fast_charge_current_limit_default = 0x680,
.fast_charge_current_limit_low_temp = 0x280,
.fast_charge_current_limit_low_voltage = 0x680,
};
static const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_version) {
switch (battery_version) {
case 0:
return &BatteryChargeParameters0;
case 1:
return &BatteryChargeParameters1;
case 2:
return &BatteryChargeParameters2;
default:
std::abort();
}
}
static void UpdateCharger(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit) {
double temperature;
u32 battery_voltage;
if (R_FAILED(battery_driver->GetTemperature(&temperature)) || R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
pmic_driver->ShutdownSystem();
}
bool enable_charge = true;
if (temperature < double(params->temp_min)) {
enable_charge = false;
} else if (double(params->temp_high) <= temperature && temperature < double(params->temp_max)) {
if (battery_voltage < params->allow_high_temp_charge_max_voltage) {
charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp);
} else {
enable_charge = false;
}
} else if (double(params->temp_max) <= temperature) {
enable_charge = false;
if (battery_voltage < params->allow_high_temp_charge_max_voltage) {
charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp);
}
}
u32 fast_charge_current_limit = params->fast_charge_current_limit_default;
if (temperature < double(params->temp_low)) {
fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_temp);
}
if (battery_voltage < params->allow_fast_charge_min_voltage) {
fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_voltage);
}
if (R_FAILED(charger_driver->SetChargeEnabled(enable_charge))) {
pmic_driver->ShutdownSystem();
}
if (R_FAILED(charger_driver->SetChargeVoltageLimit(charge_voltage_limit))) {
pmic_driver->ShutdownSystem();
}
if (R_FAILED(charger_driver->SetFastChargeCurrentLimit(fast_charge_current_limit))) {
pmic_driver->ShutdownSystem();
}
}
static bool IsSufficientBattery(u32 battery_voltage, bool ac_ok) {
/* Nintendo has stuff for updating a static variable every 10 seconds here, but this seems, again, to be debug leftovers. */
const u32 required_voltage = ac_ok ? 4000 : 3650;
return battery_voltage >= required_voltage;
}
static CheckBatteryResult LoopCheckBattery(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit, bool reboot_on_power_button_pressed, bool succeed_on_sufficient_battery, bool shutdown_on_full_battery, bool can_show_battery_icon, bool can_show_charging_icon) {
bool is_showing_charging_icon = false;
ON_SCOPE_EXIT {
if (is_showing_charging_icon) {
Boot::EndShowChargingIcon();
}
};
if (can_show_charging_icon) {
size_t battery_percentage;
if (R_FAILED(battery_driver->GetBatteryPercentage(&battery_percentage))) {
return CheckBatteryResult_Shutdown;
}
Boot::StartShowChargingIcon(battery_percentage, true);
is_showing_charging_icon = true;
}
while (true) {
double battery_charge;
if (R_FAILED(battery_driver->GetSocRep(&battery_charge))) {
return CheckBatteryResult_Shutdown;
}
if (succeed_on_sufficient_battery && battery_charge >= 3.0) {
return CheckBatteryResult_Success;
} else if (shutdown_on_full_battery && battery_charge >= 99.0) {
return CheckBatteryResult_Shutdown;
} else {
/* Nintendo has logic for checking a value every 10 seconds. */
/* They never do anything with this value though, so it's probably just leftovers from debug? */
}
bool ac_ok;
if (R_FAILED(pmic_driver->GetAcOk(&ac_ok))) {
return CheckBatteryResult_Shutdown;
}
u32 battery_voltage;
if (R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) {
return CheckBatteryResult_Shutdown;
}
if (succeed_on_sufficient_battery && IsSufficientBattery(battery_voltage, ac_ok)) {
return CheckBatteryResult_Success;
}
if (!ac_ok) {
if (can_show_battery_icon && !is_showing_charging_icon) {
Boot::ShowLowBatteryIcon();
}
return CheckBatteryResult_Shutdown;
}
if (reboot_on_power_button_pressed) {
bool power_button_pressed;
if (R_FAILED(pmic_driver->GetPowerButtonPressed(&power_button_pressed))) {
return CheckBatteryResult_Shutdown;
}
if (power_button_pressed) {
return CheckBatteryResult_Reboot;
}
}
if (can_show_battery_icon && !is_showing_charging_icon) {
Boot::StartShowChargingIcon(1, false);
is_showing_charging_icon = true;
}
svcSleepThread(20'000'000ul);
UpdateCharger(pmic_driver, charger_driver, battery_driver, params, charge_voltage_limit);
}
}
void Boot::CheckBatteryCharge() {
PmicDriver pmic_driver;
BatteryDriver battery_driver;
ChargerDriver charger_driver;
if (R_FAILED(battery_driver.InitializeBatteryParameters())) {
pmic_driver.ShutdownSystem();
}
{
bool removed;
if (R_FAILED(battery_driver.IsBatteryRemoved(&removed)) || removed) {
pmic_driver.ShutdownSystem();
}
}
const u32 boot_reason = Boot::GetBootReason();
InputCurrentLimit input_current_limit;
if (R_FAILED(charger_driver.Initialize(boot_reason != 4)) || R_FAILED(charger_driver.GetInputCurrentLimit(&input_current_limit))) {
pmic_driver.ShutdownSystem();
}
if (input_current_limit <= InputCurrentLimit_150mA) {
charger_driver.SetChargerConfiguration(ChargerConfiguration_ChargeDisable);
pmic_driver.ShutdownSystem();
}
const BatteryChargeParameters *params = GetBatteryChargeParameters(Boot::GetBatteryVersion());
u32 charge_voltage_limit = params->charge_voltage_limit_default;
CheckBatteryResult check_result;
if (boot_reason == 4) {
if (R_FAILED(charger_driver.GetChargeVoltageLimit(&charge_voltage_limit))) {
pmic_driver.ShutdownSystem();
}
UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit);
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, false, true, false, false);
} else {
UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit);
if (boot_reason == 1) {
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, true, false, true, true);
} else {
check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, false, true, false, true, false);
}
}
switch (check_result) {
case CheckBatteryResult_Success:
break;
case CheckBatteryResult_Shutdown:
pmic_driver.ShutdownSystem();
break;
case CheckBatteryResult_Reboot:
Boot::RebootSystem();
break;
default:
std::abort();
}
}
@@ -0,0 +1,48 @@
/*
* 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"
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;
static constexpr u32 ExpectedUtmipDivN = (25 << 16);
static constexpr u32 ExpectedUtmipDivM = (1 << 8);
static constexpr u32 ExpectedUtmipVal = (ExpectedUtmipDivN | ExpectedUtmipDivM);
static constexpr u32 ExpectedUtmipMask = 0xFFFF00;
static bool IsUsbClockValid() {
u64 _vaddr;
if (R_FAILED(svcQueryIoMapping(&_vaddr, 0x60006000ul, 0x1000))) {
std::abort();
}
volatile u32 *car_regs = reinterpret_cast<volatile u32 *>(_vaddr);
const u32 pllu = car_regs[0xC0 >> 2];
const u32 utmip = car_regs[0x480 >> 2];
return ((pllu & ExpectedPlluMask) == ExpectedPlluVal) && ((utmip & ExpectedUtmipMask) == ExpectedUtmipVal);
}
void Boot::CheckClock() {
if (!IsUsbClockValid()) {
/* Sleep for 1s, then reboot. */
svcSleepThread(1'000'000'000ul);
Boot::RebootSystem();
}
}
@@ -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/>.
*/
#include "boot_functions.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;
void Boot::SetInitialClockConfiguration() {
/* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */
const u32 mask = GetRuntimeFirmwareVersion() >= FirmwareVersion_600 ? InitialClockOutMask6x : InitialClockOutMask1x;
WritePmcRegister(PmcClkOutCntrl, mask, mask);
}
+536
View File
@@ -0,0 +1,536 @@
/*
* 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_display_config.hpp"
#include "i2c_driver/i2c_api.hpp"
/* Helpful defines. */
constexpr size_t DeviceAddressSpaceAlignSize = 0x400000;
constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1;
constexpr uintptr_t FrameBufferPaddr = DisplayConfigFrameBufferAddress;
constexpr size_t FrameBufferWidth = 768;
constexpr size_t FrameBufferHeight = 1280;
constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32);
constexpr uintptr_t Disp1Base = 0x54200000ul;
constexpr uintptr_t DsiBase = 0x54300000ul;
constexpr uintptr_t ClkRstBase = 0x60006000ul;
constexpr uintptr_t GpioBase = 0x6000D000ul;
constexpr uintptr_t ApbMiscBase = 0x70000000ul;
constexpr uintptr_t MipiCalBase = 0x700E3000ul;
constexpr size_t Disp1Size = 0x3000;
constexpr size_t DsiSize = 0x1000;
constexpr size_t ClkRstSize = 0x1000;
constexpr size_t GpioSize = 0x1000;
constexpr size_t ApbMiscSize = 0x1000;
constexpr size_t MipiCalSize = 0x1000;
/* Types. */
/* Globals. */
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];
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;
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);
}
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) {
for (size_t i = 0; i < num_writes; i++) {
*(reinterpret_cast<volatile u32 *>(base_address + reg_writes[i].offset)) = reg_writes[i].value;
}
}
static inline void DoSocDependentRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes_erista, size_t num_writes_erista, const RegisterWrite *reg_writes_mariko, size_t num_writes_mariko) {
if (g_is_mariko) {
DoRegisterWrites(base_address, reg_writes_mariko, num_writes_mariko);
} else {
DoRegisterWrites(base_address, reg_writes_erista, num_writes_erista);
}
}
static inline void DoDsiSleepOrRegisterWrites(const DsiSleepOrRegisterWrite *reg_writes, size_t num_writes) {
for (size_t i = 0; i < num_writes; i++) {
if (reg_writes[i].kind == DsiSleepOrRegisterWriteKind_Write) {
*(reinterpret_cast<volatile u32 *>(g_dsi_regs + sizeof(u32) * reg_writes[i].offset)) = reg_writes[i].value;
} else if (reg_writes[i].kind == DsiSleepOrRegisterWriteKind_Sleep) {
svcSleepThread(1'000'000ul * u64(reg_writes[i].offset));
} else {
std::abort();
}
}
}
#define DO_REGISTER_WRITES(base_address, writes) DoRegisterWrites(base_address, writes, sizeof(writes) / sizeof(writes[0]))
#define DO_SOC_DEPENDENT_REGISTER_WRITES(base_address, writes) DoSocDependentRegisterWrites(base_address, writes##Erista, sizeof(writes##Erista) / sizeof(writes##Erista[0]), writes##Mariko, sizeof(writes##Mariko) / sizeof(writes##Mariko[0]))
#define DO_DSI_SLEEP_OR_REGISTER_WRITES(writes) DoDsiSleepOrRegisterWrites(writes, sizeof(writes) / sizeof(writes[0]))
static void InitializeFrameBuffer() {
if (g_frame_buffer != nullptr) {
std::memset(g_frame_buffer, 0x00, FrameBufferSize);
armDCacheFlush(g_frame_buffer, FrameBufferSize);
} else {
const uintptr_t frame_buffer_aligned = ((reinterpret_cast<uintptr_t>(g_frame_buffer_storage) + DeviceAddressSpaceAlignMask) & ~uintptr_t(DeviceAddressSpaceAlignMask));
g_frame_buffer = reinterpret_cast<u32 *>(frame_buffer_aligned);
std::memset(g_frame_buffer, 0x00, FrameBufferSize);
armDCacheFlush(g_frame_buffer, FrameBufferSize);
constexpr u64 DeviceName_DC = 2;
/* Create Address Space. */
if (R_FAILED(svcCreateDeviceAddressSpace(&g_dc_das_hnd, 0, (1ul << 32)))) {
std::abort();
}
/* Attach it to the DC. */
if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd))) {
std::abort();
}
/* Map the framebuffer for the DC as read-only. */
if (R_FAILED(svcMapDeviceAddressSpaceAligned(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr, 1))) {
std::abort();
}
}
}
static void FinalizeFrameBuffer() {
if (g_frame_buffer != nullptr) {
const uintptr_t frame_buffer_aligned = reinterpret_cast<uintptr_t>(g_frame_buffer);
constexpr u64 DeviceName_DC = 2;
/* Unmap the framebuffer from the DC. */
if (R_FAILED(svcUnmapDeviceAddressSpace(g_dc_das_hnd, CUR_PROCESS_HANDLE, frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr))) {
std::abort();
}
/* Detach address space from the DC. */
if (R_FAILED(svcDetachDeviceAddressSpace(DeviceName_DC, g_dc_das_hnd))) {
std::abort();
}
/* Close the address space. */
if (R_FAILED(svcCloseHandle(g_dc_das_hnd))) {
std::abort();
}
g_dc_das_hnd = INVALID_HANDLE;
g_frame_buffer = nullptr;
}
}
static void WaitDsiTrigger() {
TimeoutHelper timeout_helper(250'000'000ul);
while (true) {
if (timeout_helper.TimedOut()) {
break;
}
if (ReadRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER) == 0) {
break;
}
}
svcSleepThread(5'000'000ul);
}
static void WaitDsiHostControl() {
TimeoutHelper timeout_helper(150'000'000ul);
while (true) {
if (timeout_helper.TimedOut()) {
break;
}
if ((ReadRegister(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL) & DSI_HOST_CONTROL_IMM_BTA) == 0) {
break;
}
}
}
void Boot::InitializeDisplay() {
/* Setup globals. */
InitializeRegisterBaseAddresses();
g_is_mariko = Boot::IsMariko();
InitializeFrameBuffer();
/* Turn on DSI/voltage rail. */
{
I2cSessionImpl i2c_session;
I2cDriver::Initialize();
I2cDriver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic);
if (g_is_mariko) {
Boot::WriteI2cRegister(i2c_session, 0x18, 0x3A);
Boot::WriteI2cRegister(i2c_session, 0x1F, 0x71);
}
Boot::WriteI2cRegister(i2c_session, 0x23, 0xD0);
I2cDriver::Finalize();
}
/* Enable MIPI CAL, DSI, DISP1, HOST1X, UART_FST_MIPI_CAL, DSIA LP clocks. */
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_CLR, 0x1010000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_SET, 0x1010000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_CLR, 0x18000000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_SET, 0x18000000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_X_SET, 0x20000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL, 0xA);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_W_SET, 0x80000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP, 0xA);
/* DPD idle. */
WritePmcRegister(PmcBase + APBDEV_PMC_IO_DPD_REQ, 0x40000000);
WritePmcRegister(PmcBase + APBDEV_PMC_IO_DPD2_REQ, 0x40000000);
/* Configure LCD pinmux tristate + passthrough. */
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_NFC_EN, ~PINMUX_TRISTATE);
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_NFC_INT, ~PINMUX_TRISTATE);
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, ~PINMUX_TRISTATE);
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_EN, ~PINMUX_TRISTATE);
ClearRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_RST, ~PINMUX_TRISTATE);
/* Configure LCD power, VDD. */
SetRegisterBits(g_gpio_regs + GPIO_PORT3_CNF_0, 0x3);
SetRegisterBits(g_gpio_regs + GPIO_PORT3_OE_0, 0x3);
SetRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1);
svcSleepThread(10'000'000ul);
SetRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2);
svcSleepThread(10'000'000ul);
/* Configure LCD backlight. */
SetRegisterBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x7);
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x7);
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x2);
/* Configure display interface and display. */
WriteRegister(g_mipi_cal_regs + 0x060, 0);
if (g_is_mariko) {
WriteRegister(g_mipi_cal_regs + 0x058, 0);
WriteRegister(g_apb_misc_regs + 0xAC0, 0);
}
/* Execute configs. */
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01);
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc01);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init01);
/* NOTE: Nintendo bug here. */
/* As of 8.0.0, Nintendo writes this list to CAR instead of DSI */
/* This results in them zeroing CLK_SOURCE_UARTA... */
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init02);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init03);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init04);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init05);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init06);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init07);
svcSleepThread(10'000'000ul);
/* Enable backlight reset. */
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4);
svcSleepThread(60'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x50204);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x337);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
WaitDsiTrigger();
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x406);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
WaitDsiTrigger();
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_IMM_BTA | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC);
WaitDsiHostControl();
svcSleepThread(5'000'000ul);
/* Parse LCD vendor. */
{
u32 host_response[3];
for (size_t i = 0; i < sizeof(host_response) / sizeof(host_response[0]); i++) {
host_response[i] = ReadRegister(g_dsi_regs + sizeof(u32) * DSI_RD_DATA);
}
if ((host_response[2] & 0xFF) == 0x10) {
g_lcd_vendor = 0;
} else {
g_lcd_vendor = (host_response[2] >> 8) & 0xFF00;
}
g_lcd_vendor = (g_lcd_vendor & 0xFFFFFF00) | (host_response[2] & 0xFF);
}
/* LCD vendor specific configuration. */
switch (g_lcd_vendor) {
case 0xF30: /* TODO: What's this? */
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(180'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
break;
case 0xF20: /* TODO: What's this? */
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(180'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
break;
case 0x10: /* Japan Display Inc screens. */
DO_DSI_SLEEP_OR_REGISTER_WRITES(DisplayConfigJdiSpecificInit01);
break;
default:
if ((g_lcd_vendor | 0x10) == 0x1030) {
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(120'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
}
break;
}
svcSleepThread(20'000'000ul);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld02);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init08);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init09);
WriteRegister(g_disp1_regs + sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, SHIFT_CLK_DIVIDER(4));
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init10);
svcSleepThread(10'000'000ul);
/* Configure MIPI CAL. */
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal01);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03);
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04);
if (g_is_mariko) {
/* On Mariko the above configurations are executed twice, for some reason. */
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03);
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04);
}
svcSleepThread(10'000'000ul);
/* Write DISP1, FrameBuffer config. */
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc02);
DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigFrameBuffer);
svcSleepThread(35'000'000ul);
g_is_display_intialized = true;
}
void Boot::ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img) {
if (!g_is_display_intialized) {
return;
}
/* Draw the image to the screen. */
std::memset(g_frame_buffer, 0, FrameBufferSize);
{
for (size_t cur_y = 0; cur_y < height; cur_y++) {
for (size_t cur_x = 0; cur_x < width; cur_x++) {
g_frame_buffer[(FrameBufferHeight - (x + cur_x)) * FrameBufferWidth + y + cur_y] = img[cur_y * width + cur_x];
}
}
}
armDCacheFlush(g_frame_buffer, FrameBufferSize);
/* Enable backlight. */
SetRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1);
}
void Boot::FinalizeDisplay() {
if (!g_is_display_intialized) {
return;
}
/* Disable backlight. */
ClearRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, ~0x1);
WriteRegister(g_disp1_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 1);
WriteRegister(g_disp1_regs + sizeof(u32) * DSI_WR_DATA, 0x2805);
/* Nintendo waits 5 frames before continuing. */
{
const uintptr_t host1x_vaddr = QueryVirtualAddress(0x500030a4, 4);
const u32 start_val = ReadRegister(host1x_vaddr);
while (ReadRegister(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);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Fini01);
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Fini02);
svcSleepThread(10'000'000ul);
/* Vendor specific shutdown. */
switch (g_lcd_vendor) {
case 0x10: /* Japan Display Inc screens. */
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigJdiSpecificFini01);
break;
case 0xF30: /* TODO: What's this? */
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigF30SpecificFini01);
svcSleepThread(5'000'000ul);
break;
case 0x1020: /* TODO: What's this? */
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71143209);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x115631);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
break;
case 0x1030: /* TODO: What's this? */
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71143209);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x114D31);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(5'000'000ul);
break;
default:
break;
}
svcSleepThread(5'000'000ul);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1005);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
svcSleepThread(50'000'000ul);
/* Disable backlight RST/Voltage. */
ClearRegisterBits(g_gpio_regs + GPIO_PORT6_OUT_1, ~0x4);
svcSleepThread(10'000'000ul);
ClearRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, ~0x2);
svcSleepThread(10'000'000ul);
ClearRegisterBits(g_gpio_regs + GPIO_PORT3_OUT_0, ~0x1);
svcSleepThread(10'000'000ul);
/* Cut clock to DSI. */
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_SET, 0x1010000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, 0x1010000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_SET, 0x18000000);
WriteRegister(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, 0x18000000);
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_PAD_CONTROL_0, (DSI_PAD_CONTROL_VS1_PULLDN_CLK | DSI_PAD_CONTROL_VS1_PULLDN(0xF) | DSI_PAD_CONTROL_VS1_PDIO_CLK | DSI_PAD_CONTROL_VS1_PDIO(0xF)));
WriteRegister(g_dsi_regs + sizeof(u32) * DSI_POWER_CONTROL, 0);
/* Final LCD config for PWM */
ClearRegisterBits(g_gpio_regs + GPIO_PORT6_CNF_1, ~0x1);
SetRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, PINMUX_TRISTATE);
ReadWriteRegisterBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, 1, 0x3);
/* Unmap framebuffer from DC virtual address space. */
FinalizeFrameBuffer();
g_is_display_intialized = false;
}
@@ -0,0 +1,676 @@
/*
* 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 "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;
};
enum DsiSleepOrRegisterWriteKind : u16 {
DsiSleepOrRegisterWriteKind_Write = 0,
DsiSleepOrRegisterWriteKind_Sleep = 1,
};
struct DsiSleepOrRegisterWrite {
DsiSleepOrRegisterWriteKind kind;
u16 offset;
u32 value;
};
static 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[] = {
{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[] = {
{sizeof(u32) * DC_CMD_STATE_ACCESS, 0},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
{sizeof(u32) * DC_CMD_REG_ACT_CONTROL, 0x54},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_DISP_DC_MCCIF_FIFOCTRL, 0},
{sizeof(u32) * DC_DISP_DISP_MEM_HIGH_PRIORITY, 0},
{sizeof(u32) * DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER, 0},
{sizeof(u32) * DC_CMD_DISPLAY_POWER_CONTROL, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE},
{sizeof(u32) * DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL},
{sizeof(u32) * DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE | 0x9}, // 9: SYNCPT
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ},
{sizeof(u32) * DC_CMD_STATE_ACCESS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_DV_CONTROL, 0},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
/* Setup default YUV colorspace conversion coefficients */
{sizeof(u32) * DC_WIN_CSC_YOF, 0xF0},
{sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A},
{sizeof(u32) * DC_WIN_CSC_KUR, 0},
{sizeof(u32) * DC_WIN_CSC_KVR, 0x198},
{sizeof(u32) * DC_WIN_CSC_KUG, 0x39B},
{sizeof(u32) * DC_WIN_CSC_KVG, 0x32F},
{sizeof(u32) * DC_WIN_CSC_KUB, 0x204},
{sizeof(u32) * DC_WIN_CSC_KVB, 0},
/* End of color coefficients */
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_DV_CONTROL, 0},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
/* Setup default YUV colorspace conversion coefficients */
{sizeof(u32) * DC_WIN_CSC_YOF, 0xF0},
{sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A},
{sizeof(u32) * DC_WIN_CSC_KUR, 0},
{sizeof(u32) * DC_WIN_CSC_KVR, 0x198},
{sizeof(u32) * DC_WIN_CSC_KUG, 0x39B},
{sizeof(u32) * DC_WIN_CSC_KVG, 0x32F},
{sizeof(u32) * DC_WIN_CSC_KUB, 0x204},
{sizeof(u32) * DC_WIN_CSC_KVB, 0},
/* End of color coefficients */
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_DV_CONTROL, 0},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
/* Setup default YUV colorspace conversion coefficients */
{sizeof(u32) * DC_WIN_CSC_YOF, 0xF0},
{sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A},
{sizeof(u32) * DC_WIN_CSC_KUR, 0},
{sizeof(u32) * DC_WIN_CSC_KVR, 0x198},
{sizeof(u32) * DC_WIN_CSC_KUG, 0x39B},
{sizeof(u32) * DC_WIN_CSC_KVG, 0x32F},
{sizeof(u32) * DC_WIN_CSC_KUB, 0x204},
{sizeof(u32) * DC_WIN_CSC_KVB, 0},
/* End of color coefficients */
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888},
{sizeof(u32) * DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C},
{sizeof(u32) * DC_COM_PIN_OUTPUT_POLARITY(1), 0x1000000},
{sizeof(u32) * DC_COM_PIN_OUTPUT_POLARITY(3), 0},
{sizeof(u32) * 0x4E4, 0},
{sizeof(u32) * DC_COM_CRC_CONTROL, 0},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * 0x716, 0x10000FF},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * 0x716, 0x10000FF},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * 0x716, 0x10000FF},
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND, 0},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
{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[] = {
{sizeof(u32) * DSI_WR_DATA, 0x0},
{sizeof(u32) * DSI_INT_ENABLE, 0x0},
{sizeof(u32) * DSI_INT_STATUS, 0x0},
{sizeof(u32) * DSI_INT_MASK, 0x0},
{sizeof(u32) * DSI_INIT_SEQ_DATA_0, 0x0},
{sizeof(u32) * DSI_INIT_SEQ_DATA_1, 0x0},
{sizeof(u32) * DSI_INIT_SEQ_DATA_2, 0x0},
{sizeof(u32) * DSI_INIT_SEQ_DATA_3, 0x0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init02Erista[] = {
{sizeof(u32) * DSI_INIT_SEQ_DATA_15, 0x0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init02Mariko[] = {
{sizeof(u32) * DSI_INIT_SEQ_DATA_15_MARIKO, 0x0},
};
static 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},
{sizeof(u32) * DSI_PKT_SEQ_2_LO, 0},
{sizeof(u32) * DSI_PKT_SEQ_3_LO, 0},
{sizeof(u32) * DSI_PKT_SEQ_4_LO, 0},
{sizeof(u32) * DSI_PKT_SEQ_5_LO, 0},
{sizeof(u32) * DSI_PKT_SEQ_0_HI, 0},
{sizeof(u32) * DSI_PKT_SEQ_1_HI, 0},
{sizeof(u32) * DSI_PKT_SEQ_2_HI, 0},
{sizeof(u32) * DSI_PKT_SEQ_3_HI, 0},
{sizeof(u32) * DSI_PKT_SEQ_4_HI, 0},
{sizeof(u32) * DSI_PKT_SEQ_5_HI, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init04Erista[] = {
/* No register writes. */
};
static constexpr RegisterWrite DisplayConfigDsi01Init04Mariko[] = {
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
{sizeof(u32) * DSI_PAD_CONTROL_2, 0},
{sizeof(u32) * DSI_PAD_CONTROL_3, 0},
{sizeof(u32) * DSI_PAD_CONTROL_4, 0},
{sizeof(u32) * DSI_PAD_CONTROL_5_MARIKO, 0},
{sizeof(u32) * DSI_PAD_CONTROL_6_MARIKO, 0},
{sizeof(u32) * DSI_PAD_CONTROL_7_MARIKO, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init05[] = {
{sizeof(u32) * DSI_PAD_CONTROL_CD, 0},
{sizeof(u32) * DSI_SOL_DELAY, 0x18},
{sizeof(u32) * DSI_MAX_THRESHOLD, 0x1E0},
{sizeof(u32) * DSI_TRIGGER, 0},
{sizeof(u32) * DSI_INIT_SEQ_CONTROL, 0},
{sizeof(u32) * DSI_PKT_LEN_0_1, 0},
{sizeof(u32) * DSI_PKT_LEN_2_3, 0},
{sizeof(u32) * DSI_PKT_LEN_4_5, 0},
{sizeof(u32) * DSI_PKT_LEN_6_7, 0},
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init06[] = {
{sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05},
{sizeof(u32) * DSI_PHY_TIMING_2, 0x30109},
{sizeof(u32) * DSI_BTA_TIMING, 0x190A14},
{sizeof(u32) * DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF)},
{sizeof(u32) * DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x765) | DSI_TIMEOUT_TA(0x2000)},
{sizeof(u32) * DSI_TO_TALLY, 0},
{sizeof(u32) * DSI_PAD_CONTROL_0, DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0)}, // Enable
{sizeof(u32) * DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
{sizeof(u32) * DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
{sizeof(u32) * DSI_POWER_CONTROL, 0},
{sizeof(u32) * DSI_POWER_CONTROL, 0},
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init07[] = {
{sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05},
{sizeof(u32) * DSI_PHY_TIMING_2, 0x30118},
{sizeof(u32) * DSI_BTA_TIMING, 0x190A14},
{sizeof(u32) * DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF)},
{sizeof(u32) * DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x1343) | DSI_TIMEOUT_TA(0x2000)},
{sizeof(u32) * DSI_TO_TALLY, 0},
{sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
{sizeof(u32) * DSI_CONTROL, DSI_CONTROL_LANES(3) | DSI_CONTROL_HOST_ENABLE},
{sizeof(u32) * DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
{sizeof(u32) * DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
{sizeof(u32) * DSI_MAX_THRESHOLD, 0x40},
{sizeof(u32) * DSI_TRIGGER, 0},
{sizeof(u32) * DSI_TX_CRC, 0},
{sizeof(u32) * DSI_INIT_SEQ_CONTROL, 0}
};
static constexpr RegisterWrite DisplayConfigDsiPhyTimingErista[] = {
{sizeof(u32) * DSI_PHY_TIMING_0, 0x6070601},
};
static constexpr RegisterWrite DisplayConfigDsiPhyTimingMariko[] = {
{sizeof(u32) * DSI_PHY_TIMING_0, 0x6070603},
};
static constexpr DsiSleepOrRegisterWrite DisplayConfigJdiSpecificInit01[] = {
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x9483FFB9},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xBD15},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1939},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAD8},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAEB},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAEBAAAA},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAAA},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAEB},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAEBAAAA},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAA},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1BD15},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2739},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFD8},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2BD15},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xF39},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFD8},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFF},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xBD15},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x6D915},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xB9},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1105},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
{DsiSleepOrRegisterWriteKind_Sleep, 0xB4, 0},
{DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2905},
{DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST},
};
static 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[] = {
{CLK_RST_CONTROLLER_PLLD_BASE, 0x4810c001},
{CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000000},
{CLK_RST_CONTROLLER_PLLD_MISC, 0x002DFC00},
};
static constexpr RegisterWrite DisplayConfigDsi01Init08[] = {
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init09[] = {
{sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05},
{sizeof(u32) * DSI_PHY_TIMING_2, 0x30172},
{sizeof(u32) * DSI_BTA_TIMING, 0x190A14},
{sizeof(u32) * DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xA40)},
{sizeof(u32) * DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x5A2F) | DSI_TIMEOUT_TA(0x2000)},
{sizeof(u32) * DSI_TO_TALLY, 0},
{sizeof(u32) * DSI_PKT_SEQ_0_LO, 0x40000208},
{sizeof(u32) * DSI_PKT_SEQ_2_LO, 0x40000308},
{sizeof(u32) * DSI_PKT_SEQ_4_LO, 0x40000308},
{sizeof(u32) * DSI_PKT_SEQ_1_LO, 0x40000308},
{sizeof(u32) * DSI_PKT_SEQ_3_LO, 0x3F3B2B08},
{sizeof(u32) * DSI_PKT_SEQ_3_HI, 0x2CC},
{sizeof(u32) * DSI_PKT_SEQ_5_LO, 0x3F3B2B08},
{sizeof(u32) * DSI_PKT_SEQ_5_HI, 0x2CC},
{sizeof(u32) * DSI_PKT_LEN_0_1, 0xCE0000},
{sizeof(u32) * DSI_PKT_LEN_2_3, 0x87001A2},
{sizeof(u32) * DSI_PKT_LEN_4_5, 0x190},
{sizeof(u32) * DSI_PKT_LEN_6_7, 0x190},
{sizeof(u32) * DSI_HOST_CONTROL, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Init10[] = {
{sizeof(u32) * DSI_TRIGGER, 0},
{sizeof(u32) * DSI_CONTROL, 0},
{sizeof(u32) * DSI_SOL_DELAY, 6},
{sizeof(u32) * DSI_MAX_THRESHOLD, 0x1E0},
{sizeof(u32) * DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
{sizeof(u32) * DSI_CONTROL, DSI_CONTROL_HS_CLK_CTRL | DSI_CONTROL_FORMAT(3) | DSI_CONTROL_LANES(3) | DSI_CONTROL_VIDEO_ENABLE},
{sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_FIFO_SEL| DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
{sizeof(u32) * DSI_CONTROL, DSI_CONTROL_HS_CLK_CTRL | DSI_CONTROL_FORMAT(3) | DSI_CONTROL_LANES(3) | DSI_CONTROL_VIDEO_ENABLE},
{sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
{sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC}
};
static 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[] = {
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
{sizeof(u32) * DSI_PAD_CONTROL_2, 0},
{sizeof(u32) * DSI_PAD_CONTROL_3, 0},
{sizeof(u32) * DSI_PAD_CONTROL_4, 0x77777},
{sizeof(u32) * DSI_PAD_CONTROL_5_MARIKO, 0x77777},
{sizeof(u32) * DSI_PAD_CONTROL_6_MARIKO, DSI_PAD_PREEMP_PD_CLK(0x1) | DSI_PAD_PREEMP_PU_CLK(0x1) | DSI_PAD_PREEMP_PD(0x01) | DSI_PAD_PREEMP_PU(0x1)},
{sizeof(u32) * DSI_PAD_CONTROL_7_MARIKO, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal01[] = {
{0x60, 0},
{0x08, 0xF3F10000},
{0x58, 1},
{0x60, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal02Erista[] = {
{0x60, 0x10010},
{0x5C, 0x300},
};
static constexpr RegisterWrite DisplayConfigMipiCal02Mariko[] = {
{0x60, 0x10010},
{0x5C, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal03Erista[] = {
{0x38, 0x200200},
{0x3C, 0x200200},
{0x64, 0x200002},
{0x68, 0x200002},
{0x14, 0},
{0x18, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal03Mariko[] = {
{0x38, 0x200006},
{0x3C, 0x200006},
{0x64, 0x260000},
{0x68, 0x260000},
{0x14, 0},
{0x18, 0},
};
static constexpr RegisterWrite DisplayConfigMipiCal04[] = {
{0x1C, 0},
{0x20, 0},
{0x24, 0},
{0x28, 0},
{0x40, 0},
{0x44, 0},
{0x68, 0},
{0x70, 0},
{0x74, 0},
{0x00, 0x2A000001},
};
static 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},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_DV_CONTROL, 0},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
/* Setup default YUV colorspace conversion coefficients */
{sizeof(u32) * DC_WIN_CSC_YOF, 0xF0},
{sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A},
{sizeof(u32) * DC_WIN_CSC_KUR, 0},
{sizeof(u32) * DC_WIN_CSC_KVR, 0x198},
{sizeof(u32) * DC_WIN_CSC_KUG, 0x39B},
{sizeof(u32) * DC_WIN_CSC_KVG, 0x32F},
{sizeof(u32) * DC_WIN_CSC_KUB, 0x204},
{sizeof(u32) * DC_WIN_CSC_KVB, 0},
/* End of color coefficients */
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_DV_CONTROL, 0},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
/* Setup default YUV colorspace conversion coefficients */
{sizeof(u32) * DC_WIN_CSC_YOF, 0xF0},
{sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A},
{sizeof(u32) * DC_WIN_CSC_KUR, 0},
{sizeof(u32) * DC_WIN_CSC_KVR, 0x198},
{sizeof(u32) * DC_WIN_CSC_KUG, 0x39B},
{sizeof(u32) * DC_WIN_CSC_KVG, 0x32F},
{sizeof(u32) * DC_WIN_CSC_KUB, 0x204},
{sizeof(u32) * DC_WIN_CSC_KVB, 0},
/* End of color coefficients */
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_DV_CONTROL, 0},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
/* Setup default YUV colorspace conversion coefficients */
{sizeof(u32) * DC_WIN_CSC_YOF, 0xF0},
{sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A},
{sizeof(u32) * DC_WIN_CSC_KUR, 0},
{sizeof(u32) * DC_WIN_CSC_KVR, 0x198},
{sizeof(u32) * DC_WIN_CSC_KUG, 0x39B},
{sizeof(u32) * DC_WIN_CSC_KVG, 0x32F},
{sizeof(u32) * DC_WIN_CSC_KUB, 0x204},
{sizeof(u32) * DC_WIN_CSC_KVB, 0},
/* End of color coefficients */
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888},
{sizeof(u32) * DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C},
{sizeof(u32) * DC_COM_PIN_OUTPUT_POLARITY(1), 0x1000000},
{sizeof(u32) * DC_COM_PIN_OUTPUT_POLARITY(3), 0},
{sizeof(u32) * 0x4E4, 0},
{sizeof(u32) * DC_COM_CRC_CONTROL, 0},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * 0x716, 0x10000FF},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * 0x716, 0x10000FF},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * 0x716, 0x10000FF},
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND, 0},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ},
{sizeof(u32) * DC_CMD_STATE_ACCESS, 0},
/* Set Display timings */
{sizeof(u32) * DC_DISP_DISP_TIMING_OPTIONS, 0},
{sizeof(u32) * DC_DISP_REF_TO_SYNC, (1 << 16)}, // h_ref_to_sync = 0, v_ref_to_sync = 1.
{sizeof(u32) * DC_DISP_SYNC_WIDTH, 0x10048},
{sizeof(u32) * DC_DISP_BACK_PORCH, 0x90048},
{sizeof(u32) * DC_DISP_ACTIVE, 0x50002D0},
{sizeof(u32) * DC_DISP_FRONT_PORCH, 0xA0088}, // Sources say that this should be above the DC_DISP_ACTIVE cmd.
/* End of Display timings */
{sizeof(u32) * DC_DISP_SHIFT_CLOCK_OPTIONS, SC1_H_QUALIFIER_NONE | SC0_H_QUALIFIER_NONE},
{sizeof(u32) * DC_COM_PIN_OUTPUT_ENABLE(1), 0},
{sizeof(u32) * DC_DISP_DATA_ENABLE_OPTIONS, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL},
{sizeof(u32) * DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C},
{sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, 0},
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
{sizeof(u32) * DC_CMD_STATE_ACCESS, READ_MUX | WRITE_MUX},
{sizeof(u32) * DC_DISP_FRONT_PORCH, 0xA0088},
{sizeof(u32) * DC_CMD_STATE_ACCESS, 0},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
{sizeof(u32) * DC_CMD_GENERAL_INCR_SYNCPT, 0x301},
{sizeof(u32) * DC_CMD_GENERAL_INCR_SYNCPT, 0x301},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE},
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ},
{sizeof(u32) * DC_CMD_STATE_ACCESS, 0},
{sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(4)},
{sizeof(u32) * DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888},
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0}
};
static constexpr u32 DisplayConfigFrameBufferAddress = 0xC0000000;
static 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.
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, //Enable window A.
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE
{sizeof(u32) * DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, //T_A8R8G8B8 //NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_WIN_POSITION, 0}, //(0,0)
{sizeof(u32) * DC_WIN_H_INITIAL_DDA, 0},
{sizeof(u32) * DC_WIN_V_INITIAL_DDA, 0},
{sizeof(u32) * DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(2880)}, //Pre-scaled size: 1280x2880 bytes.
{sizeof(u32) * DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)},
{sizeof(u32) * DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)}, //Window size: 1280 vertical lines x 720 horizontal pixels.
{sizeof(u32) * DC_WIN_LINE_STRIDE, 0x6000C00}, //768*2x768*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements.
{sizeof(u32) * DC_WIN_BUFFER_CONTROL, 0},
{sizeof(u32) * DC_WINBUF_SURFACE_KIND, 0}, //Regular surface.
{sizeof(u32) * DC_WINBUF_START_ADDR, DisplayConfigFrameBufferAddress}, //Framebuffer address.
{sizeof(u32) * DC_WINBUF_ADDR_H_OFFSET, 0},
{sizeof(u32) * DC_WINBUF_ADDR_V_OFFSET, 0},
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE
{sizeof(u32) * DC_WIN_WIN_OPTIONS, 0},
{sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE
{sizeof(u32) * DC_WIN_WIN_OPTIONS, WIN_ENABLE}, //Enable window AD.
{sizeof(u32) * DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, //DISPLAY_CTRL_MODE: continuous display.
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE}, //General update; window A update.
{sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ} //General activation request; window A activation request.
};
static constexpr RegisterWrite DisplayConfigDsi01Fini01[] = {
{sizeof(u32) * DSI_POWER_CONTROL, 0},
{sizeof(u32) * DSI_PAD_CONTROL_1, 0},
};
static constexpr RegisterWrite DisplayConfigDsi01Fini02[] = {
{sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05},
{sizeof(u32) * DSI_PHY_TIMING_2, 0x30109},
{sizeof(u32) * DSI_BTA_TIMING, 0x190A14},
{sizeof(u32) * DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF) },
{sizeof(u32) * DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x765) | DSI_TIMEOUT_TA(0x2000)},
{sizeof(u32) * DSI_TO_TALLY, 0},
{sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC},
{sizeof(u32) * DSI_CONTROL, DSI_CONTROL_LANES(3) | DSI_CONTROL_HOST_ENABLE},
{sizeof(u32) * DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE},
{sizeof(u32) * DSI_MAX_THRESHOLD, 0x40},
{sizeof(u32) * DSI_TRIGGER, 0},
{sizeof(u32) * DSI_TX_CRC, 0},
{sizeof(u32) * DSI_INIT_SEQ_CONTROL, 0}
};
static constexpr RegisterWrite DisplayConfigJdiSpecificFini01[] = {
{sizeof(u32) * DSI_WR_DATA, 0x439},
{sizeof(u32) * DSI_WR_DATA, 0x9483FFB9},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
{sizeof(u32) * DSI_WR_DATA, 0x2139},
{sizeof(u32) * DSI_WR_DATA, 0x191919D5},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
{sizeof(u32) * DSI_WR_DATA, 0xB39},
{sizeof(u32) * DSI_WR_DATA, 0x4F0F41B1},
{sizeof(u32) * DSI_WR_DATA, 0xF179A433},
{sizeof(u32) * DSI_WR_DATA, 0x2D81},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
{sizeof(u32) * DSI_WR_DATA, 0x439},
{sizeof(u32) * DSI_WR_DATA, 0xB9},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
};
static constexpr RegisterWrite DisplayConfigF30SpecificFini01[] = {
{sizeof(u32) * DSI_WR_DATA, 0x439},
{sizeof(u32) * DSI_WR_DATA, 0x9483FFB9},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
{sizeof(u32) * DSI_WR_DATA, 0x2C39},
{sizeof(u32) * DSI_WR_DATA, 0x191919D5},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
{sizeof(u32) * DSI_WR_DATA, 0x2C39},
{sizeof(u32) * DSI_WR_DATA, 0x191919D6},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_WR_DATA, 0x19191919},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
{sizeof(u32) * DSI_WR_DATA, 0xB39},
{sizeof(u32) * DSI_WR_DATA, 0x711148B1},
{sizeof(u32) * DSI_WR_DATA, 0x71143209},
{sizeof(u32) * DSI_WR_DATA, 0x114D31},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
{sizeof(u32) * DSI_WR_DATA, 0x439},
{sizeof(u32) * DSI_WR_DATA, 0xB9},
{sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST},
};
@@ -0,0 +1,26 @@
/*
* 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"
static constexpr u32 GpioPadName_FanEnable = 0x4B;
void Boot::SetFanEnabled() {
if (Boot::GetHardwareType() == HardwareType_Copper) {
Boot::GpioSetDirection(GpioPadName_FanEnable, GpioDirection_Output);
Boot::GpioSetValue(GpioPadName_FanEnable, GpioValue_High);
}
}
@@ -0,0 +1,91 @@
/*
* 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 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);
};
@@ -0,0 +1,80 @@
/*
* 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);
/* Set the GPIO's value. */
Boot::GpioSetValue(configs[i].pad_name, configs[i].value);
}
}
@@ -0,0 +1,69 @@
/*
* 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 "boot_types.hpp"
static constexpr GpioInitialConfig GpioInitialConfigsCopper[] = {
{0x40, GpioDirection_Output, GpioValue_Low},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x41, GpioDirection_Input, GpioValue_High},
{0x42, GpioDirection_Input, GpioValue_Low},
{0x43, GpioDirection_Output, GpioValue_Low},
{0x02, GpioDirection_Output, GpioValue_Low},
{0x07, GpioDirection_Output, GpioValue_Low},
{0x44, GpioDirection_Input, GpioValue_High},
{0x45, GpioDirection_Input, GpioValue_High},
{0x0F, GpioDirection_Input, GpioValue_High},
{0x46, GpioDirection_Output, GpioValue_Low},
{0x47, GpioDirection_Output, GpioValue_Low},
{0x10, GpioDirection_Input, GpioValue_Low},
{0x11, GpioDirection_Input, GpioValue_Low},
{0x12, GpioDirection_Input, GpioValue_Low},
{0x13, GpioDirection_Input, GpioValue_Low},
{0x14, GpioDirection_Input, GpioValue_High},
{0x18, GpioDirection_Input, GpioValue_Low},
{0x19, GpioDirection_Input, GpioValue_High},
{0x1A, GpioDirection_Input, GpioValue_High},
{0x1C, GpioDirection_Input, GpioValue_High},
{0x4D, GpioDirection_Output, GpioValue_Low},
{0x20, GpioDirection_Output, GpioValue_Low},
{0x38, GpioDirection_Input, GpioValue_High},
{0x23, GpioDirection_Input, GpioValue_High},
{0x25, GpioDirection_Input, GpioValue_Low},
{0x26, GpioDirection_Input, GpioValue_Low},
{0x27, GpioDirection_Input, GpioValue_Low},
{0x28, GpioDirection_Input, GpioValue_High},
{0x29, GpioDirection_Input, GpioValue_High},
{0x2A, GpioDirection_Input, GpioValue_High},
{0x48, GpioDirection_Output, GpioValue_Low},
{0x49, GpioDirection_Output, GpioValue_Low},
{0x4A, GpioDirection_Output, GpioValue_Low},
{0x2D, GpioDirection_Output, GpioValue_Low},
{0x2E, GpioDirection_Output, GpioValue_Low},
{0x37, GpioDirection_Input, GpioValue_Low},
{0x2F, GpioDirection_Output, GpioValue_Low},
{0x03, GpioDirection_Output, GpioValue_Low},
{0x30, GpioDirection_Input, GpioValue_Low},
{0x31, GpioDirection_Output, GpioValue_Low},
{0x4B, GpioDirection_Output, GpioValue_Low},
{0x4C, GpioDirection_Input, GpioValue_High},
{0x4E, GpioDirection_Input, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsCopper = (sizeof(GpioInitialConfigsCopper) / sizeof(GpioInitialConfigsCopper[0]));
@@ -0,0 +1,84 @@
/*
* 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 "boot_types.hpp"
static constexpr GpioInitialConfig GpioInitialConfigsHoag[] = {
{0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x06, GpioDirection_Input, GpioValue_Low},
{0x02, GpioDirection_Output, GpioValue_Low},
{0x3C, GpioDirection_Input, GpioValue_Low},
{0x0F, GpioDirection_Input, GpioValue_High},
{0x08, GpioDirection_Input, GpioValue_Low},
{0x09, GpioDirection_Input, GpioValue_Low},
{0x0A, GpioDirection_Output, GpioValue_Low},
{0x0B, GpioDirection_Input, GpioValue_Low},
{0x0D, GpioDirection_Output, GpioValue_Low},
{0x0E, GpioDirection_Input, GpioValue_Low},
{0x10, GpioDirection_Input, GpioValue_Low},
{0x11, GpioDirection_Input, GpioValue_Low},
{0x12, GpioDirection_Input, GpioValue_Low},
{0x13, GpioDirection_Input, GpioValue_Low},
{0x14, GpioDirection_Input, GpioValue_High},
{0x16, GpioDirection_Input, GpioValue_Low},
{0x15, GpioDirection_Input, GpioValue_Low},
{0x17, GpioDirection_Input, GpioValue_High},
{0x18, GpioDirection_Input, GpioValue_Low},
{0x19, GpioDirection_Input, GpioValue_High},
{0x1A, GpioDirection_Input, GpioValue_High},
{0x1B, GpioDirection_Input, GpioValue_Low},
{0x1C, GpioDirection_Input, GpioValue_Low},
{0x1D, GpioDirection_Output, GpioValue_Low},
{0x1E, GpioDirection_Output, GpioValue_Low},
{0x20, GpioDirection_Output, GpioValue_Low},
{0x21, GpioDirection_Input, GpioValue_Low},
{0x38, GpioDirection_Input, GpioValue_High},
{0x22, GpioDirection_Input, GpioValue_Low},
{0x23, GpioDirection_Input, GpioValue_High},
{0x01, GpioDirection_Output, GpioValue_Low},
{0x39, GpioDirection_Output, GpioValue_Low},
{0x24, GpioDirection_Output, GpioValue_Low},
{0x34, GpioDirection_Input, GpioValue_Low},
{0x25, GpioDirection_Input, GpioValue_Low},
{0x26, GpioDirection_Input, GpioValue_Low},
{0x27, GpioDirection_Input, GpioValue_Low},
{0x2B, GpioDirection_Output, GpioValue_Low},
{0x28, GpioDirection_Input, GpioValue_High},
{0x1F, GpioDirection_Output, GpioValue_Low},
{0x29, GpioDirection_Input, GpioValue_High},
{0x3A, GpioDirection_Output, GpioValue_Low},
{0x0C, GpioDirection_Input, GpioValue_Low},
{0x2D, GpioDirection_Output, GpioValue_Low},
{0x2E, GpioDirection_Output, GpioValue_Low},
{0x37, GpioDirection_Input, GpioValue_Low},
{0x2F, GpioDirection_Output, GpioValue_Low},
{0x03, GpioDirection_Output, GpioValue_Low},
{0x30, GpioDirection_Input, GpioValue_Low},
{0x3B, GpioDirection_Input, GpioValue_Low},
{0x31, GpioDirection_Output, GpioValue_Low},
{0x32, GpioDirection_Output, GpioValue_Low},
{0x33, GpioDirection_Output, GpioValue_Low},
{0x35, GpioDirection_Input, GpioValue_High},
{0x2C, GpioDirection_Output, GpioValue_Low},
{0x36, GpioDirection_Output, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsHoag = (sizeof(GpioInitialConfigsHoag) / sizeof(GpioInitialConfigsHoag[0]));
@@ -0,0 +1,150 @@
/*
* 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 "boot_types.hpp"
static constexpr GpioInitialConfig GpioInitialConfigsIcosa[] = {
{0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x06, GpioDirection_Input, GpioValue_Low},
{0x02, GpioDirection_Output, GpioValue_Low},
{0x07, GpioDirection_Output, GpioValue_Low},
{0x3C, GpioDirection_Input, GpioValue_Low},
{0x0F, GpioDirection_Input, GpioValue_High},
{0x08, GpioDirection_Input, GpioValue_Low},
{0x09, GpioDirection_Input, GpioValue_Low},
{0x0A, GpioDirection_Output, GpioValue_Low},
{0x0B, GpioDirection_Input, GpioValue_High},
{0x0D, GpioDirection_Output, GpioValue_Low},
{0x0E, GpioDirection_Input, GpioValue_Low},
{0x10, GpioDirection_Input, GpioValue_Low},
{0x11, GpioDirection_Input, GpioValue_Low},
{0x12, GpioDirection_Input, GpioValue_Low},
{0x13, GpioDirection_Input, GpioValue_Low},
{0x14, GpioDirection_Input, GpioValue_High},
{0x16, GpioDirection_Input, GpioValue_Low},
{0x15, GpioDirection_Input, GpioValue_Low},
{0x17, GpioDirection_Input, GpioValue_High},
{0x18, GpioDirection_Input, GpioValue_Low},
{0x19, GpioDirection_Input, GpioValue_High},
{0x1A, GpioDirection_Input, GpioValue_High},
{0x1B, GpioDirection_Input, GpioValue_High},
{0x1C, GpioDirection_Input, GpioValue_Low},
{0x1D, GpioDirection_Output, GpioValue_Low},
{0x1E, GpioDirection_Output, GpioValue_Low},
{0x20, GpioDirection_Output, GpioValue_Low},
{0x21, GpioDirection_Input, GpioValue_High},
{0x38, GpioDirection_Input, GpioValue_High},
{0x22, GpioDirection_Input, GpioValue_Low},
{0x23, GpioDirection_Input, GpioValue_High},
{0x01, GpioDirection_Output, GpioValue_Low},
{0x39, GpioDirection_Output, GpioValue_Low},
{0x24, GpioDirection_Output, GpioValue_Low},
{0x34, GpioDirection_Input, GpioValue_Low},
{0x25, GpioDirection_Input, GpioValue_Low},
{0x26, GpioDirection_Input, GpioValue_Low},
{0x27, GpioDirection_Input, GpioValue_Low},
{0x2B, GpioDirection_Output, GpioValue_Low},
{0x28, GpioDirection_Input, GpioValue_High},
{0x1F, GpioDirection_Output, GpioValue_Low},
{0x29, GpioDirection_Input, GpioValue_High},
{0x2A, GpioDirection_Input, GpioValue_High},
{0x3A, GpioDirection_Output, GpioValue_Low},
{0x0C, GpioDirection_Input, GpioValue_Low},
{0x2D, GpioDirection_Output, GpioValue_Low},
{0x2E, GpioDirection_Output, GpioValue_Low},
{0x37, GpioDirection_Input, GpioValue_Low},
{0x2F, GpioDirection_Output, GpioValue_Low},
{0x03, GpioDirection_Output, GpioValue_Low},
{0x30, GpioDirection_Input, GpioValue_Low},
{0x3B, GpioDirection_Input, GpioValue_Low},
{0x31, GpioDirection_Output, GpioValue_Low},
{0x32, GpioDirection_Output, GpioValue_Low},
{0x33, GpioDirection_Output, GpioValue_Low},
{0x35, GpioDirection_Input, GpioValue_High},
{0x2C, GpioDirection_Output, GpioValue_Low},
{0x36, GpioDirection_Output, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsIcosa = (sizeof(GpioInitialConfigsIcosa) / sizeof(GpioInitialConfigsIcosa[0]));
static constexpr GpioInitialConfig GpioInitialConfigsIcosa4x[] = {
{0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x06, GpioDirection_Input, GpioValue_Low},
{0x02, GpioDirection_Output, GpioValue_Low},
{0x07, GpioDirection_Output, GpioValue_Low},
{0x3C, GpioDirection_Input, GpioValue_Low},
{0x0F, GpioDirection_Input, GpioValue_High},
{0x08, GpioDirection_Input, GpioValue_Low},
{0x09, GpioDirection_Input, GpioValue_Low},
{0x0A, GpioDirection_Output, GpioValue_Low},
{0x0B, GpioDirection_Input, GpioValue_High},
{0x0D, GpioDirection_Output, GpioValue_Low},
{0x0E, GpioDirection_Input, GpioValue_Low},
{0x10, GpioDirection_Input, GpioValue_Low},
{0x11, GpioDirection_Input, GpioValue_Low},
{0x12, GpioDirection_Input, GpioValue_Low},
{0x13, GpioDirection_Input, GpioValue_Low},
{0x14, GpioDirection_Input, GpioValue_High},
{0x16, GpioDirection_Input, GpioValue_Low},
{0x15, GpioDirection_Input, GpioValue_Low},
{0x17, GpioDirection_Input, GpioValue_High},
{0x18, GpioDirection_Input, GpioValue_High},
{0x19, GpioDirection_Input, GpioValue_High},
{0x1A, GpioDirection_Input, GpioValue_High},
{0x1B, GpioDirection_Input, GpioValue_High},
{0x1C, GpioDirection_Input, GpioValue_Low},
{0x1D, GpioDirection_Output, GpioValue_Low},
{0x1E, GpioDirection_Output, GpioValue_Low},
{0x20, GpioDirection_Output, GpioValue_Low},
{0x21, GpioDirection_Input, GpioValue_High},
{0x38, GpioDirection_Input, GpioValue_High},
{0x22, GpioDirection_Input, GpioValue_Low},
{0x23, GpioDirection_Input, GpioValue_High},
{0x01, GpioDirection_Output, GpioValue_Low},
{0x39, GpioDirection_Output, GpioValue_Low},
{0x24, GpioDirection_Output, GpioValue_Low},
{0x34, GpioDirection_Input, GpioValue_Low},
{0x25, GpioDirection_Input, GpioValue_Low},
{0x26, GpioDirection_Input, GpioValue_Low},
{0x27, GpioDirection_Input, GpioValue_Low},
{0x2B, GpioDirection_Output, GpioValue_Low},
{0x28, GpioDirection_Input, GpioValue_High},
{0x1F, GpioDirection_Output, GpioValue_Low},
{0x29, GpioDirection_Input, GpioValue_High},
{0x2A, GpioDirection_Input, GpioValue_High},
{0x3A, GpioDirection_Output, GpioValue_Low},
{0x0C, GpioDirection_Input, GpioValue_Low},
{0x2D, GpioDirection_Output, GpioValue_Low},
{0x2E, GpioDirection_Output, GpioValue_Low},
{0x37, GpioDirection_Input, GpioValue_Low},
{0x2F, GpioDirection_Output, GpioValue_Low},
{0x03, GpioDirection_Output, GpioValue_Low},
{0x30, GpioDirection_Input, GpioValue_Low},
{0x3B, GpioDirection_Input, GpioValue_Low},
{0x31, GpioDirection_Output, GpioValue_Low},
{0x32, GpioDirection_Output, GpioValue_Low},
{0x33, GpioDirection_Output, GpioValue_Low},
{0x35, GpioDirection_Input, GpioValue_High},
{0x2C, GpioDirection_Output, GpioValue_Low},
{0x36, GpioDirection_Output, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsIcosa4x = (sizeof(GpioInitialConfigsIcosa4x) / sizeof(GpioInitialConfigsIcosa4x[0]));
@@ -0,0 +1,84 @@
/*
* 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 "boot_types.hpp"
static constexpr GpioInitialConfig GpioInitialConfigsIowa[] = {
{0x04, GpioDirection_Input, GpioValue_High},
{0x05, GpioDirection_Output, GpioValue_Low},
{0x06, GpioDirection_Input, GpioValue_Low},
{0x02, GpioDirection_Output, GpioValue_Low},
{0x3C, GpioDirection_Input, GpioValue_Low},
{0x0F, GpioDirection_Input, GpioValue_High},
{0x08, GpioDirection_Input, GpioValue_Low},
{0x09, GpioDirection_Input, GpioValue_Low},
{0x0A, GpioDirection_Output, GpioValue_Low},
{0x0B, GpioDirection_Input, GpioValue_Low},
{0x0D, GpioDirection_Output, GpioValue_Low},
{0x0E, GpioDirection_Input, GpioValue_Low},
{0x10, GpioDirection_Input, GpioValue_Low},
{0x11, GpioDirection_Input, GpioValue_Low},
{0x12, GpioDirection_Input, GpioValue_Low},
{0x13, GpioDirection_Input, GpioValue_Low},
{0x14, GpioDirection_Input, GpioValue_High},
{0x16, GpioDirection_Input, GpioValue_Low},
{0x15, GpioDirection_Input, GpioValue_Low},
{0x17, GpioDirection_Input, GpioValue_High},
{0x18, GpioDirection_Input, GpioValue_Low},
{0x19, GpioDirection_Input, GpioValue_High},
{0x1A, GpioDirection_Input, GpioValue_High},
{0x1B, GpioDirection_Input, GpioValue_Low},
{0x1C, GpioDirection_Input, GpioValue_Low},
{0x1D, GpioDirection_Output, GpioValue_Low},
{0x1E, GpioDirection_Output, GpioValue_Low},
{0x20, GpioDirection_Output, GpioValue_Low},
{0x21, GpioDirection_Input, GpioValue_Low},
{0x38, GpioDirection_Input, GpioValue_High},
{0x22, GpioDirection_Input, GpioValue_Low},
{0x23, GpioDirection_Input, GpioValue_High},
{0x01, GpioDirection_Output, GpioValue_Low},
{0x39, GpioDirection_Output, GpioValue_Low},
{0x24, GpioDirection_Output, GpioValue_Low},
{0x34, GpioDirection_Input, GpioValue_Low},
{0x25, GpioDirection_Input, GpioValue_Low},
{0x26, GpioDirection_Input, GpioValue_Low},
{0x27, GpioDirection_Input, GpioValue_Low},
{0x2B, GpioDirection_Output, GpioValue_Low},
{0x28, GpioDirection_Input, GpioValue_High},
{0x1F, GpioDirection_Output, GpioValue_Low},
{0x29, GpioDirection_Input, GpioValue_High},
{0x3A, GpioDirection_Output, GpioValue_Low},
{0x0C, GpioDirection_Input, GpioValue_Low},
{0x2D, GpioDirection_Output, GpioValue_Low},
{0x2E, GpioDirection_Output, GpioValue_Low},
{0x37, GpioDirection_Input, GpioValue_Low},
{0x2F, GpioDirection_Output, GpioValue_Low},
{0x03, GpioDirection_Output, GpioValue_Low},
{0x30, GpioDirection_Input, GpioValue_Low},
{0x3B, GpioDirection_Input, GpioValue_Low},
{0x31, GpioDirection_Output, GpioValue_Low},
{0x32, GpioDirection_Output, GpioValue_Low},
{0x33, GpioDirection_Output, GpioValue_Low},
{0x35, GpioDirection_Input, GpioValue_High},
{0x2C, GpioDirection_Output, GpioValue_Low},
{0x36, GpioDirection_Output, GpioValue_Low},
};
static constexpr u32 GpioNumInitialConfigsIowa = (sizeof(GpioInitialConfigsIowa) / sizeof(GpioInitialConfigsIowa[0]));
+111
View File
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
static constexpr u32 GpioInvalid = UINT32_MAX;
static constexpr u32 GpioMap[] = {
GpioInvalid, /* Invalid */
0x000000CC, /* Port Z, Pin 4 */
0x00000024, /* Port E, Pin 4 */
0x0000003C, /* Port H, Pin 4 */
0x000000DA, /* Port BB, Pin 2 */
0x000000DB, /* Port BB, Pin 3 */
0x000000DC, /* Port BB, Pin 4 */
0x00000025, /* Port E, Pin 5 */
0x00000090, /* Port S, Pin 0 */
0x00000091, /* Port S, Pin 1 */
0x00000096, /* Port S, Pin 6 */
0x00000097, /* Port S, Pin 7 */
0x00000026, /* Port E, Pin 6 */
0x00000005, /* Port A, Pin 5 */
0x00000078, /* Port P, Pin 0 */
0x00000093, /* Port S, Pin 3 */
0x0000007D, /* Port P, Pin 5 */
0x0000007C, /* Port P, Pin 4 */
0x0000007B, /* Port P, Pin 3 */
0x0000007A, /* Port P, Pin 2 */
0x000000BC, /* Port X, Pin 4 */
0x000000AE, /* Port V, Pin 6 */
0x000000BA, /* Port X, Pin 2 */
0x000000B9, /* Port X, Pin 1 */
0x000000BD, /* Port X, Pin 5 */
0x000000BE, /* Port X, Pin 6 */
0x000000BF, /* Port X, Pin 7 */
0x000000C0, /* Port Y, Pin 0 */
0x000000C1, /* Port Y, Pin 1 */
0x000000A9, /* Port V, Pin 1 */
0x000000AA, /* Port V, Pin 2 */
0x00000055, /* Port K, Pin 5 */
0x000000AD, /* Port V, Pin 5 */
0x000000C8, /* Port Z, Pin 0 */
0x000000CA, /* Port Z, Pin 2 */
0x000000CB, /* Port Z, Pin 3 */
0x0000004F, /* Port J, Pin 7 */
0x00000050, /* Port K, Pin 0 */
0x00000051, /* Port K, Pin 1 */
0x00000052, /* Port K, Pin 2 */
0x00000054, /* Port K, Pin 4 */
0x00000056, /* Port K, Pin 6 */
0x00000057, /* Port K, Pin 7 */
0x00000053, /* Port K, Pin 3 */
0x000000E3, /* Port CC, Pin 3 */
0x00000038, /* Port H, Pin 0 */
0x00000039, /* Port H, Pin 1 */
0x0000003B, /* Port H, Pin 3 */
0x0000003D, /* Port H, Pin 5 */
0x0000003F, /* Port H, Pin 7 */
0x00000040, /* Port I, Pin 0 */
0x00000041, /* Port I, Pin 1 */
0x0000003E, /* Port H, Pin 6 */
0x000000E2, /* Port CC, Pin 2 */
0x000000E4, /* Port CC, Pin 4 */
0x0000003A, /* Port H, Pin 2 */
0x000000C9, /* Port Z, Pin 1 */
0x0000004D, /* Port J, Pin 5 */
0x00000058, /* Port L, Pin 0 */
0x0000003E, /* Port H, Pin 6 */
0x00000026, /* Port E, Pin 6 */
/* Copper only */
GpioInvalid, /* Invalid */
0x00000033, /* Port G, Pin 3 */
0x0000001C, /* Port D, Pin 4 */
0x000000D9, /* Port BB, Pin 1 */
0x0000000C, /* Port B, Pin 4 */
0x0000000D, /* Port B, Pin 5 */
0x00000021, /* Port E, Pin 1 */
0x00000027, /* Port E, Pin 7 */
0x00000092, /* Port S, Pin 2 */
0x00000095, /* Port S, Pin 5 */
0x00000098, /* Port T, Pin 0 */
0x00000010, /* Port C, Pin 0 */
0x00000011, /* Port C, Pin 1 */
0x00000012, /* Port C, Pin 2 */
0x00000042, /* Port I, Pin 2 */
0x000000E6, /* Port CC, Pin 6 */
/* 2.0.0+ Copper only */
0x000000AC, /* Port V, Pin 4 */
0x000000E1, /* Port CC, Pin 1 */
/* 5.0.0+ Copper only (unused) */
0x00000056, /* Port K, Pin 6 */
};
static constexpr u32 GpioPadNameMax = (sizeof(GpioMap) / sizeof(GpioMap[0]));
@@ -0,0 +1,119 @@
/*
* 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;
}
@@ -0,0 +1,75 @@
/*
* 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 "i2c_driver/i2c_api.hpp"
template<typename F>
static Result RetryUntilSuccess(F f) {
constexpr u64 timeout = 10'000'000'000ul;
constexpr u64 retry_interval = 20'000'000ul;
u64 cur_time = 0;
while (true) {
Result rc = f();
if (R_SUCCEEDED(rc)) {
return rc;
} else {
cur_time += retry_interval;
if (cur_time >= timeout) {
return rc;
}
}
svcSleepThread(retry_interval);
}
}
Result Boot::ReadI2cRegister(I2cSessionImpl &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) {
Result rc;
if (dst == nullptr || dst_size == 0 || cmd == nullptr || cmd_size == 0) {
std::abort();
}
u8 cmd_list[I2cCommandListFormatter::MaxCommandListSize];
I2cCommandListFormatter formatter(cmd_list, sizeof(cmd_list));
if (R_FAILED((rc = formatter.EnqueueSendCommand(I2cTransactionOption_Start, cmd, cmd_size)))) {
std::abort();
}
if (R_FAILED((rc = formatter.EnqueueReceiveCommand(static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop), dst_size)))) {
std::abort();
}
return RetryUntilSuccess([&]() { return I2cDriver::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) {
if (src == nullptr || src_size == 0 || cmd == nullptr || cmd_size == 0) {
std::abort();
}
u8 cmd_list[0x20];
/* N doesn't use a CommandListFormatter here... */
std::memcpy(&cmd_list[0], cmd, cmd_size);
std::memcpy(&cmd_list[cmd_size], src, src_size);
return RetryUntilSuccess([&]() { return I2cDriver::Send(session, cmd_list, src_size + cmd_size, static_cast<I2cTransactionOption>(I2cTransactionOption_Start | I2cTransactionOption_Stop)); });
}
Result Boot::WriteI2cRegister(I2cSessionImpl &session, const u8 address, const u8 value) {
return Boot::WriteI2cRegister(session, &value, sizeof(value), &address, sizeof(address));
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,90 @@
/*
* 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));
}
/* 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);
}
}
@@ -0,0 +1,186 @@
/*
* 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 "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialConfigsCopper[] = {
{0x10, 0x20, 0x27F},
{0x0F, 0x00, 0x267},
{0x0E, 0x20, 0x27F},
{0x5B, 0x00, 0x00},
{0x80, 0x01, 0x7F},
{0x34, 0x40, 0x267},
{0x35, 0x40, 0x267},
{0x55, 0x00, 0x18},
{0x56, 0x01, 0x67},
{0x5C, 0x00, 0x00},
{0x59, 0x00, 0x00},
{0x5A, 0x10, 0x18},
{0x2C, 0x40, 0x267},
{0x2D, 0x40, 0x267},
{0x2E, 0x40, 0x267},
{0x2F, 0x40, 0x267},
{0x36, 0x00, 0x67},
{0x37, 0x30, 0x7F},
{0x38, 0x00, 0x67},
{0x39, 0x28, 0x7F},
{0x54, 0x00, 0x67},
{0x9B, 0x30, 0x7F},
{0x42, 0x00, 0x67},
{0x43, 0x28, 0x7F},
{0x44, 0x00, 0x67},
{0x45, 0x28, 0x7F},
{0x4B, 0x28, 0x7F},
{0x4C, 0x00, 0x67},
{0x4A, 0x00, 0x67},
{0x4D, 0x00, 0x67},
{0x64, 0x20, 0x27F},
{0x63, 0x40, 0x267},
{0x5E, 0x04, 0x67},
{0x60, 0x04, 0x67},
{0x17, 0x24, 0x7F},
{0x18, 0x24, 0x7F},
{0x27, 0x04, 0x67},
{0x2A, 0x04, 0x67},
{0x2B, 0x04, 0x67},
{0x90, 0x24, 0x7F},
{0x32, 0x24, 0x27F},
{0x33, 0x34, 0x27F},
{0x76, 0x04, 0x67},
{0x79, 0x04, 0x67},
{0x08, 0x24, 0x7F},
{0x09, 0x24, 0x7F},
{0x0A, 0x24, 0x7F},
{0x0B, 0x24, 0x7F},
{0x88, 0x34, 0x7F},
{0x89, 0x24, 0x7F},
{0x8A, 0x34, 0x7F},
{0x8B, 0x34, 0x7F},
{0x8D, 0x34, 0x7F},
{0x81, 0x04, 0x67},
{0x9D, 0x34, 0x7F},
{0x9F, 0x34, 0x7F},
{0xA1, 0x34, 0x7F},
{0x92, 0x4C, 0x7F},
{0x93, 0x4C, 0x7F},
{0x94, 0x44, 0x7F},
{0x96, 0x34, 0x7F},
{0x98, 0x34, 0x7F},
{0x99, 0x34, 0x7F},
{0x12, 0x04, 0x7F},
{0x13, 0x04, 0x67},
{0x14, 0x04, 0x7F},
{0x6A, 0x04, 0x67},
{0x6B, 0x04, 0x67},
{0x6C, 0x2C, 0x7F},
{0x6D, 0x04, 0x67},
{0x6E, 0x04, 0x67},
{0x6F, 0x24, 0x7F},
{0x70, 0x04, 0x7F},
{0x73, 0x04, 0x67},
{0x69, 0x24, 0x7F},
{0x5D, 0x05, 0x07},
{0x5F, 0x05, 0x07},
{0x61, 0x05, 0x07},
{0x47, 0x05, 0x07},
{0x48, 0x05, 0x07},
{0x46, 0x05, 0x07},
{0x49, 0x05, 0x07},
{0x19, 0x05, 0x07},
{0x1A, 0x05, 0x07},
{0x1B, 0x05, 0x07},
{0x26, 0x05, 0x07},
{0x28, 0x05, 0x07},
{0x29, 0x05, 0x07},
{0x8F, 0x05, 0x07},
{0x30, 0x05, 0x07},
{0x31, 0x05, 0x07},
{0x52, 0x05, 0x07},
{0x53, 0x05, 0x07},
{0x75, 0x05, 0x07},
{0x77, 0x05, 0x07},
{0x78, 0x05, 0x07},
{0x7A, 0x05, 0x07},
{0x0D, 0x05, 0x07},
{0x0C, 0x05, 0x07},
{0x11, 0x05, 0x07},
{0x8E, 0x05, 0x07},
{0x00, 0x05, 0x07},
{0x01, 0x05, 0x07},
{0x05, 0x05, 0x07},
{0x04, 0x05, 0x07},
{0x03, 0x05, 0x07},
{0x02, 0x05, 0x07},
{0x06, 0x05, 0x07},
{0x07, 0x05, 0x07},
{0x87, 0x05, 0x07},
{0x86, 0x05, 0x07},
{0x82, 0x05, 0x07},
{0x83, 0x05, 0x07},
{0x85, 0x05, 0x07},
{0x84, 0x05, 0x07},
{0x8C, 0x05, 0x07},
{0x7B, 0x05, 0x07},
{0x7C, 0x05, 0x07},
{0x7D, 0x05, 0x07},
{0x7E, 0x05, 0x07},
{0x7F, 0x05, 0x07},
{0x9C, 0x05, 0x07},
{0x9E, 0x05, 0x07},
{0xA0, 0x05, 0x07},
{0x58, 0x00, 0x00},
{0x4F, 0x05, 0x07},
{0x50, 0x05, 0x07},
{0x4E, 0x05, 0x07},
{0x51, 0x05, 0x07},
{0x3A, 0x05, 0x07},
{0x3B, 0x05, 0x07},
{0x3C, 0x05, 0x07},
{0x3D, 0x05, 0x07},
{0x95, 0x05, 0x07},
{0x97, 0x05, 0x07},
{0x9A, 0x05, 0x07},
{0x15, 0x05, 0x07},
{0x16, 0x05, 0x07},
{0x1C, 0x05, 0x07},
{0x1D, 0x05, 0x07},
{0x1E, 0x05, 0x07},
{0x1F, 0x05, 0x07},
{0x3E, 0x05, 0x07},
{0x3F, 0x05, 0x07},
{0x40, 0x05, 0x07},
{0x41, 0x05, 0x07},
{0x91, 0x05, 0x07},
{0x71, 0x05, 0x07},
{0x72, 0x05, 0x07},
{0x74, 0x05, 0x07},
{0x22, 0x05, 0x07},
{0x23, 0x05, 0x07},
{0x20, 0x05, 0x07},
{0x21, 0x05, 0x07},
{0x24, 0x05, 0x07},
{0x25, 0x05, 0x07},
{0x62, 0x05, 0x07},
{0x65, 0x05, 0x07},
{0x66, 0x05, 0x07},
{0x67, 0x05, 0x07},
{0x68, 0x05, 0x07},
};
static constexpr u32 PinmuxNumInitialConfigsCopper = (sizeof(PinmuxInitialConfigsCopper) / sizeof(PinmuxInitialConfigsCopper[0]));
@@ -0,0 +1,186 @@
/*
* 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 "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialConfigsHoag[] = {
{0x5D, 0x00, 0x67},
{0x47, 0x28, 0x7F},
{0x48, 0x00, 0x67},
{0x46, 0x00, 0x67},
{0x49, 0x00, 0x67},
{0x30, 0x40, 0x27F},
{0x31, 0x40, 0x27F},
{0x0D, 0x20, 0x27F},
{0x0C, 0x00, 0x267},
{0x10, 0x20, 0x27F},
{0x0F, 0x00, 0x267},
{0x0E, 0x20, 0x27F},
{0x00, 0x48, 0x7F},
{0x01, 0x50, 0x7F},
{0x05, 0x50, 0x7F},
{0x04, 0x50, 0x7F},
{0x03, 0x50, 0x7F},
{0x02, 0x50, 0x7F},
{0x5B, 0x00, 0x78},
{0x7C, 0x01, 0x67},
{0x80, 0x01, 0x7F},
{0x34, 0x40, 0x27F},
{0x35, 0x40, 0x27F},
{0x55, 0x20, 0x78},
{0x56, 0x20, 0x7F},
{0xA1, 0x30, 0x7F},
{0x5C, 0x00, 0x78},
{0x59, 0x00, 0x60},
{0x5A, 0x30, 0x78},
{0x2C, 0x40, 0x27F},
{0x2D, 0x40, 0x27F},
{0x2E, 0x40, 0x27F},
{0x2F, 0x40, 0x27F},
{0x3B, 0x20, 0x7F},
{0x3C, 0x00, 0x67},
{0x3D, 0x20, 0x7F},
{0x36, 0x00, 0x67},
{0x37, 0x30, 0x7F},
{0x38, 0x00, 0x67},
{0x39, 0x28, 0x7F},
{0x54, 0x00, 0x67},
{0x9B, 0x30, 0x7F},
{0x1C, 0x00, 0x67},
{0x1D, 0x30, 0x7F},
{0x1E, 0x00, 0x67},
{0x1F, 0x00, 0x67},
{0x3F, 0x20, 0x7F},
{0x40, 0x00, 0x67},
{0x41, 0x20, 0x7F},
{0x42, 0x00, 0x67},
{0x43, 0x28, 0x7F},
{0x44, 0x00, 0x67},
{0x45, 0x28, 0x7F},
{0x22, 0x00, 0x67},
{0x23, 0x28, 0x7F},
{0x20, 0x00, 0x67},
{0x21, 0x00, 0x67},
{0x4B, 0x28, 0x7F},
{0x4C, 0x00, 0x67},
{0x4A, 0x00, 0x67},
{0x4D, 0x00, 0x67},
{0x64, 0x20, 0x27F},
{0x5F, 0x34, 0x7F},
{0x60, 0x04, 0x67},
{0x61, 0x2C, 0x7F},
{0x2A, 0x04, 0x67},
{0x2B, 0x04, 0x67},
{0x8F, 0x24, 0x7F},
{0x33, 0x34, 0x27F},
{0x52, 0x2C, 0x7F},
{0x53, 0x24, 0x7F},
{0x77, 0x04, 0x67},
{0x78, 0x34, 0x7F},
{0x11, 0x04, 0x67},
{0x06, 0x2C, 0x7F},
{0x08, 0x24, 0x7F},
{0x09, 0x24, 0x7F},
{0x0A, 0x24, 0x7F},
{0x0B, 0x24, 0x7F},
{0x88, 0x34, 0x7F},
{0x86, 0x2C, 0x7F},
{0x82, 0x24, 0x7F},
{0x85, 0x34, 0x7F},
{0x89, 0x24, 0x7F},
{0x8A, 0x34, 0x7F},
{0x8B, 0x34, 0x7F},
{0x8C, 0x34, 0x7F},
{0x8D, 0x24, 0x7F},
{0x7D, 0x04, 0x67},
{0x7E, 0x04, 0x67},
{0x81, 0x04, 0x67},
{0x9C, 0x34, 0x7F},
{0x9D, 0x34, 0x7F},
{0x9E, 0x2C, 0x7F},
{0x9F, 0x34, 0x7F},
{0xA0, 0x04, 0x67},
{0x4F, 0x04, 0x67},
{0x51, 0x04, 0x67},
{0x3A, 0x24, 0x7F},
{0x92, 0x4C, 0x7F},
{0x93, 0x4C, 0x7F},
{0x94, 0x44, 0x7F},
{0x95, 0x04, 0x67},
{0x96, 0x34, 0x7F},
{0x97, 0x04, 0x67},
{0x98, 0x34, 0x7F},
{0x99, 0x34, 0x7F},
{0x9A, 0x04, 0x67},
{0x3E, 0x24, 0x7F},
{0x6A, 0x04, 0x67},
{0x6B, 0x04, 0x67},
{0x6C, 0x2C, 0x7F},
{0x6D, 0x04, 0x67},
{0x6E, 0x04, 0x67},
{0x6F, 0x24, 0x7F},
{0x91, 0x24, 0x7F},
{0x70, 0x04, 0x7F},
{0x71, 0x04, 0x67},
{0x72, 0x04, 0x67},
{0x65, 0x34, 0x7F},
{0x66, 0x04, 0x67},
{0x67, 0x04, 0x267},
{0x5E, 0x05, 0x07},
{0x17, 0x05, 0x07},
{0x18, 0x05, 0x07},
{0x19, 0x05, 0x07},
{0x1A, 0x05, 0x07},
{0x1B, 0x05, 0x07},
{0x26, 0x05, 0x07},
{0x27, 0x05, 0x07},
{0x28, 0x05, 0x07},
{0x29, 0x05, 0x07},
{0x90, 0x05, 0x07},
{0x32, 0x05, 0x07},
{0x75, 0x05, 0x07},
{0x76, 0x05, 0x07},
{0x79, 0x05, 0x07},
{0x7A, 0x05, 0x07},
{0x8E, 0x05, 0x07},
{0x07, 0x05, 0x07},
{0x87, 0x05, 0x07},
{0x83, 0x05, 0x07},
{0x84, 0x05, 0x07},
{0x7B, 0x05, 0x07},
{0x7F, 0x05, 0x07},
{0x58, 0x00, 0x00},
{0x50, 0x05, 0x07},
{0x4E, 0x05, 0x07},
{0x12, 0x05, 0x07},
{0x13, 0x05, 0x07},
{0x14, 0x05, 0x07},
{0x15, 0x05, 0x07},
{0x16, 0x05, 0x07},
{0x73, 0x05, 0x07},
{0x74, 0x05, 0x07},
{0x24, 0x05, 0x07},
{0x25, 0x05, 0x07},
{0x62, 0x05, 0x07},
{0x68, 0x05, 0x07},
{0x69, 0x05, 0x07},
{0x63, 0x05, 0x07},
};
static constexpr u32 PinmuxNumInitialConfigsHoag = (sizeof(PinmuxInitialConfigsHoag) / sizeof(PinmuxInitialConfigsHoag[0]));
@@ -0,0 +1,186 @@
/*
* 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 "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialConfigsIcosa[] = {
{0x5D, 0x00, 0x67},
{0x47, 0x28, 0x7F},
{0x48, 0x00, 0x67},
{0x46, 0x00, 0x67},
{0x49, 0x00, 0x67},
{0x30, 0x40, 0x27F},
{0x31, 0x40, 0x27F},
{0x0D, 0x20, 0x27F},
{0x0C, 0x00, 0x267},
{0x10, 0x20, 0x27F},
{0x0F, 0x00, 0x267},
{0x0E, 0x20, 0x27F},
{0x00, 0x48, 0x7F},
{0x01, 0x50, 0x7F},
{0x05, 0x50, 0x7F},
{0x04, 0x50, 0x7F},
{0x03, 0x50, 0x7F},
{0x02, 0x50, 0x7F},
{0x5B, 0x00, 0x78},
{0x7C, 0x01, 0x67},
{0x80, 0x01, 0x7F},
{0x34, 0x40, 0x27F},
{0x35, 0x40, 0x27F},
{0x55, 0x20, 0x78},
{0x56, 0x20, 0x7F},
{0xA1, 0x30, 0x7F},
{0x5C, 0x00, 0x78},
{0x59, 0x00, 0x60},
{0x5A, 0x30, 0x78},
{0x2C, 0x40, 0x27F},
{0x2D, 0x40, 0x27F},
{0x2E, 0x40, 0x27F},
{0x2F, 0x40, 0x27F},
{0x3B, 0x20, 0x7F},
{0x3C, 0x00, 0x67},
{0x3D, 0x20, 0x7F},
{0x36, 0x00, 0x67},
{0x37, 0x30, 0x7F},
{0x38, 0x00, 0x67},
{0x39, 0x28, 0x7F},
{0x54, 0x00, 0x67},
{0x9B, 0x30, 0x7F},
{0x1C, 0x00, 0x67},
{0x1D, 0x30, 0x7F},
{0x1E, 0x00, 0x67},
{0x1F, 0x00, 0x67},
{0x3F, 0x20, 0x7F},
{0x40, 0x00, 0x67},
{0x41, 0x20, 0x7F},
{0x42, 0x00, 0x67},
{0x43, 0x28, 0x7F},
{0x44, 0x00, 0x67},
{0x45, 0x28, 0x7F},
{0x22, 0x00, 0x67},
{0x23, 0x28, 0x7F},
{0x20, 0x00, 0x67},
{0x21, 0x00, 0x67},
{0x4B, 0x28, 0x7F},
{0x4C, 0x00, 0x67},
{0x4A, 0x00, 0x67},
{0x4D, 0x00, 0x67},
{0x64, 0x20, 0x27F},
{0x5F, 0x34, 0x7F},
{0x60, 0x04, 0x67},
{0x61, 0x2C, 0x7F},
{0x2A, 0x04, 0x67},
{0x2B, 0x04, 0x67},
{0x8F, 0x24, 0x7F},
{0x33, 0x34, 0x27F},
{0x52, 0x2C, 0x7F},
{0x53, 0x24, 0x7F},
{0x77, 0x04, 0x67},
{0x78, 0x34, 0x7F},
{0x11, 0x04, 0x67},
{0x06, 0x2C, 0x7F},
{0x08, 0x24, 0x7F},
{0x09, 0x24, 0x7F},
{0x0A, 0x24, 0x7F},
{0x0B, 0x24, 0x7F},
{0x88, 0x34, 0x7F},
{0x86, 0x2C, 0x7F},
{0x82, 0x24, 0x7F},
{0x85, 0x34, 0x7F},
{0x89, 0x24, 0x7F},
{0x8A, 0x34, 0x7F},
{0x8B, 0x34, 0x7F},
{0x8C, 0x34, 0x7F},
{0x8D, 0x24, 0x7F},
{0x7D, 0x04, 0x67},
{0x7E, 0x04, 0x67},
{0x81, 0x04, 0x67},
{0x9C, 0x34, 0x7F},
{0x9D, 0x34, 0x7F},
{0x9E, 0x2C, 0x7F},
{0x9F, 0x34, 0x7F},
{0xA0, 0x04, 0x67},
{0x4F, 0x04, 0x67},
{0x51, 0x04, 0x67},
{0x3A, 0x24, 0x7F},
{0x92, 0x4C, 0x7F},
{0x93, 0x4C, 0x7F},
{0x94, 0x44, 0x7F},
{0x95, 0x04, 0x67},
{0x96, 0x34, 0x7F},
{0x97, 0x04, 0x67},
{0x98, 0x34, 0x7F},
{0x99, 0x34, 0x7F},
{0x9A, 0x04, 0x67},
{0x3E, 0x24, 0x7F},
{0x6A, 0x04, 0x67},
{0x6B, 0x04, 0x67},
{0x6C, 0x2C, 0x7F},
{0x6D, 0x04, 0x67},
{0x6E, 0x04, 0x67},
{0x6F, 0x24, 0x7F},
{0x91, 0x24, 0x7F},
{0x70, 0x04, 0x7F},
{0x71, 0x04, 0x67},
{0x72, 0x04, 0x67},
{0x65, 0x34, 0x7F},
{0x66, 0x04, 0x67},
{0x67, 0x04, 0x267},
{0x5E, 0x05, 0x07},
{0x17, 0x05, 0x07},
{0x18, 0x05, 0x07},
{0x19, 0x05, 0x07},
{0x1A, 0x05, 0x07},
{0x1B, 0x05, 0x07},
{0x26, 0x05, 0x07},
{0x27, 0x05, 0x07},
{0x28, 0x05, 0x07},
{0x29, 0x05, 0x07},
{0x90, 0x05, 0x07},
{0x32, 0x05, 0x07},
{0x75, 0x05, 0x07},
{0x76, 0x05, 0x07},
{0x79, 0x05, 0x07},
{0x7A, 0x05, 0x07},
{0x8E, 0x05, 0x07},
{0x07, 0x05, 0x07},
{0x87, 0x05, 0x07},
{0x83, 0x05, 0x07},
{0x84, 0x05, 0x07},
{0x7B, 0x05, 0x07},
{0x7F, 0x05, 0x07},
{0x58, 0x00, 0x00},
{0x50, 0x05, 0x07},
{0x4E, 0x05, 0x07},
{0x12, 0x05, 0x07},
{0x13, 0x05, 0x07},
{0x14, 0x05, 0x07},
{0x15, 0x05, 0x07},
{0x16, 0x05, 0x07},
{0x73, 0x05, 0x07},
{0x74, 0x05, 0x07},
{0x24, 0x05, 0x07},
{0x25, 0x05, 0x07},
{0x62, 0x05, 0x07},
{0x68, 0x05, 0x07},
{0x69, 0x05, 0x07},
{0x63, 0x05, 0x07},
};
static constexpr u32 PinmuxNumInitialConfigsIcosa = (sizeof(PinmuxInitialConfigsIcosa) / sizeof(PinmuxInitialConfigsIcosa[0]));
@@ -0,0 +1,199 @@
/*
* 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 "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialConfigsIowa[] = {
{0x5D, 0x00, 0x7F},
{0x47, 0x28, 0x7F},
{0x48, 0x00, 0x7F},
{0x46, 0x00, 0x7F},
{0x49, 0x00, 0x7F},
{0x30, 0x40, 0x27F},
{0x31, 0x40, 0x27F},
{0x0D, 0x20, 0x27F},
{0x0C, 0x00, 0x27F},
{0x10, 0x40, 0x27F},
{0x0F, 0x00, 0x27F},
{0x0E, 0x20, 0x27F},
{0x00, 0x40, 0x7F},
{0x01, 0x50, 0x7F},
{0x05, 0x50, 0x7F},
{0x04, 0x50, 0x7F},
{0x03, 0x50, 0x7F},
{0x02, 0x50, 0x7F},
{0xAA, 0x40, 0x7F},
{0xAC, 0x40, 0x7F},
{0xA2, 0x50, 0x7F},
{0xA3, 0x50, 0x7F},
{0xA4, 0x50, 0x7F},
{0xA5, 0x50, 0x7F},
{0xA6, 0x50, 0x7F},
{0xA7, 0x50, 0x7F},
{0xA8, 0x50, 0x7F},
{0xA9, 0x50, 0x7F},
{0x5B, 0x00, 0x78},
{0x7C, 0x01, 0x67},
{0x80, 0x01, 0x7F},
{0x34, 0x40, 0x27F},
{0x35, 0x40, 0x27F},
{0x55, 0x20, 0x78},
{0x56, 0x20, 0x7F},
{0xA1, 0x30, 0x7F},
{0x5C, 0x00, 0x78},
{0x5A, 0x20, 0x78},
{0x2C, 0x40, 0x27F},
{0x2D, 0x40, 0x27F},
{0x2E, 0x40, 0x27F},
{0x2F, 0x40, 0x27F},
{0x3B, 0x20, 0x7F},
{0x3C, 0x00, 0x7F},
{0x3D, 0x20, 0x7F},
{0x36, 0x00, 0x7F},
{0x37, 0x30, 0x7F},
{0x38, 0x00, 0x7F},
{0x39, 0x28, 0x7F},
{0x54, 0x00, 0x67},
{0x9B, 0x30, 0x7F},
{0x1C, 0x00, 0x7F},
{0x1D, 0x30, 0x7F},
{0x1E, 0x00, 0x7F},
{0x1F, 0x00, 0x7F},
{0x3F, 0x20, 0x7F},
{0x40, 0x00, 0x7F},
{0x41, 0x20, 0x7F},
{0x42, 0x00, 0x7F},
{0x43, 0x28, 0x7F},
{0x44, 0x00, 0x7F},
{0x45, 0x28, 0x7F},
{0x4B, 0x28, 0x7F},
{0x4C, 0x00, 0x7F},
{0x4A, 0x00, 0x7F},
{0x4D, 0x00, 0x7F},
{0x64, 0x20, 0x27F},
{0x5F, 0x34, 0x7F},
{0x60, 0x04, 0x67},
{0x61, 0x2C, 0x7F},
{0x2A, 0x04, 0x67},
{0x8F, 0x24, 0x7F},
{0x33, 0x34, 0x27F},
{0x52, 0x2C, 0x7F},
{0x53, 0x24, 0x7F},
{0x77, 0x04, 0x67},
{0x78, 0x24, 0x7F},
{0x11, 0x04, 0x67},
{0x06, 0x2C, 0x7F},
{0x08, 0x24, 0x7F},
{0x09, 0x24, 0x7F},
{0x0A, 0x24, 0x7F},
{0x0B, 0x24, 0x7F},
{0x88, 0x34, 0x7F},
{0x86, 0x2C, 0x7F},
{0x82, 0x24, 0x7F},
{0x85, 0x34, 0x7F},
{0x89, 0x24, 0x7F},
{0x8A, 0x34, 0x7F},
{0x8B, 0x34, 0x7F},
{0x8C, 0x24, 0x7F},
{0x8D, 0x24, 0x7F},
{0x7D, 0x04, 0x67},
{0x7E, 0x04, 0x67},
{0x81, 0x04, 0x67},
{0x9C, 0x24, 0x7F},
{0x9D, 0x34, 0x7F},
{0x9E, 0x2C, 0x7F},
{0x9F, 0x34, 0x7F},
{0xA0, 0x04, 0x67},
{0x4F, 0x04, 0x67},
{0x51, 0x04, 0x67},
{0x3A, 0x24, 0x7F},
{0x92, 0x4C, 0x7F},
{0x93, 0x4C, 0x7F},
{0x94, 0x44, 0x7F},
{0x95, 0x04, 0x67},
{0x96, 0x34, 0x7F},
{0x97, 0x04, 0x67},
{0x98, 0x34, 0x7F},
{0x9A, 0x04, 0x67},
{0x3E, 0x24, 0x7F},
{0x6A, 0x04, 0x67},
{0x6B, 0x04, 0x67},
{0x6C, 0x2C, 0x7F},
{0x6D, 0x04, 0x67},
{0x6E, 0x04, 0x67},
{0x6F, 0x24, 0x7F},
{0x91, 0x24, 0x7F},
{0x70, 0x04, 0x7F},
{0x71, 0x04, 0x67},
{0x72, 0x04, 0x67},
{0x65, 0x34, 0x7F},
{0x66, 0x04, 0x67},
{0x67, 0x04, 0x267},
{0x5E, 0x05, 0x07},
{0x17, 0x05, 0x07},
{0x18, 0x05, 0x07},
{0x19, 0x05, 0x07},
{0x1A, 0x05, 0x07},
{0x1B, 0x05, 0x07},
{0x26, 0x05, 0x07},
{0x27, 0x05, 0x07},
{0x28, 0x05, 0x07},
{0x29, 0x05, 0x07},
{0x2B, 0x05, 0x07},
{0x90, 0x05, 0x07},
{0x32, 0x05, 0x07},
{0x75, 0x05, 0x07},
{0x76, 0x05, 0x07},
{0x79, 0x05, 0x07},
{0x7A, 0x05, 0x07},
{0x8E, 0x05, 0x07},
{0xAB, 0x05, 0x07},
{0xAD, 0x05, 0x07},
{0xAE, 0x05, 0x07},
{0x07, 0x05, 0x07},
{0x87, 0x05, 0x07},
{0x83, 0x05, 0x07},
{0x84, 0x05, 0x07},
{0x7B, 0x05, 0x07},
{0x7F, 0x05, 0x07},
{0x58, 0x00, 0x00},
{0x59, 0x00, 0x00},
{0x50, 0x05, 0x07},
{0x4E, 0x05, 0x07},
{0x99, 0x05, 0x07},
{0x12, 0x05, 0x07},
{0x13, 0x05, 0x07},
{0x14, 0x05, 0x07},
{0x15, 0x05, 0x07},
{0x16, 0x05, 0x07},
{0x73, 0x05, 0x07},
{0x74, 0x05, 0x07},
{0x22, 0x05, 0x07},
{0x23, 0x05, 0x07},
{0x20, 0x05, 0x07},
{0x21, 0x05, 0x07},
{0x24, 0x05, 0x07},
{0x25, 0x05, 0x07},
{0x62, 0x05, 0x07},
{0x68, 0x05, 0x07},
{0x69, 0x05, 0x07},
{0x63, 0x05, 0x07},
};
static constexpr u32 PinmuxNumInitialConfigsIowa = (sizeof(PinmuxInitialConfigsIowa) / sizeof(PinmuxInitialConfigsIowa[0]));
@@ -0,0 +1,72 @@
/*
* 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 "boot_types.hpp"
static constexpr PinmuxInitialConfig PinmuxInitialDrivePadConfigs[] = {
{0x04, 0x01010000, 0x01F1F000},
{0x0D, 0x01010000, 0x01F1F000},
{0x10, 0x01010000, 0x01F1F000},
{0x12, 0x01010000, 0x01F1F000},
{0x13, 0x01010000, 0x01F1F000},
{0x14, 0x0001F000, 0x01F1F000},
{0x15, 0x0001F000, 0x01F1F000},
{0x24, 0x01010000, 0x01F1F000},
{0x25, 0x01010000, 0x01F1F000},
{0x26, 0x01010000, 0x01F1F000},
{0x27, 0x01010000, 0x01F1F000},
{0x28, 0x01010000, 0x01F1F000},
{0x29, 0x01010000, 0x01F1F000},
{0x2A, 0x01010000, 0x01F1F000},
{0x2B, 0x01010000, 0x01F1F000},
{0x2C, 0x01F1F000, 0x01F1F000},
{0x2D, 0x01F1F000, 0x01F1F000},
{0x2F, 0x01F1F000, 0x01F1F000},
{0x30, 0x01404000, 0x01F1F000},
{0x31, 0x0001F000, 0x01F1F000},
{0x32, 0x0001F000, 0x01F1F000},
{0x33, 0x0001F000, 0x01F1F000},
{0x34, 0x0001F000, 0x01F1F000},
{0x35, 0x00007000, 0x01F1F000},
{0x36, 0x00007000, 0x01F1F000},
{0x46, 0x01010000, 0x01F1F000},
{0x47, 0x01010000, 0x01F1F000},
{0x4C, 0x01404000, 0x01F1F000},
{0x4D, 0x01404000, 0x01F1F000},
{0x62, 0x0001F000, 0x01F1F000},
{0x63, 0x0001F000, 0x01F1F000},
{0x7C, 0x01414000, 0x01F1F000},
{0x87, 0x01404000, 0x01F1F000},
{0x88, 0x01404000, 0x01F1F000},
{0x89, 0x01404000, 0x01F1F000},
{0x8A, 0x01404000, 0x01F1F000},
{0x6D, 0x00000000, 0xF0000000},
{0x6E, 0x00000000, 0xF0000000},
{0x6F, 0x00000000, 0xF0000000},
{0x70, 0x00000000, 0xF0000000},
{0x71, 0x00000000, 0xF0000000},
{0x72, 0x00000000, 0xF0000000},
{0x73, 0x00000000, 0xF0000000},
{0x74, 0x00000000, 0xF0000000},
{0x75, 0x00000000, 0xF0000000},
{0x76, 0x00000000, 0xF0000000},
{0x69, 0x51212000, 0xF1F1F000},
};
static constexpr u32 PinmuxNumInitialDrivePadConfigs = (sizeof(PinmuxInitialDrivePadConfigs) / sizeof(PinmuxInitialDrivePadConfigs[0]));
@@ -0,0 +1,364 @@
/*
* 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>
struct PinmuxDefinition {
u32 reg_offset;
u32 mask_val;
u32 pm_val;
};
struct PinmuxDrivePadDefinition {
u32 reg_offset;
u32 mask_val;
};
static constexpr PinmuxDefinition PinmuxMap[] = {
{0x00003000, 0x72FF, 0x01}, /* Sdmmc1Clk */
{0x00003004, 0x72FF, 0x02}, /* Sdmmc1Cmd */
{0x00003008, 0x72FF, 0x02}, /* Sdmmc1Dat3 */
{0x0000300C, 0x72FF, 0x02}, /* Sdmmc1Dat2 */
{0x00003010, 0x72FF, 0x02}, /* Sdmmc1Dat1 */
{0x00003014, 0x72FF, 0x01}, /* Sdmmc1Dat0 */
{0x0000301C, 0x72FF, 0x01}, /* Sdmmc3Clk */
{0x00003020, 0x72FF, 0x01}, /* Sdmmc3Cmd */
{0x00003024, 0x72FF, 0x01}, /* Sdmmc3Dat0 */
{0x00003028, 0x72FF, 0x01}, /* Sdmmc3Dat1 */
{0x0000302C, 0x72FF, 0x01}, /* Sdmmc3Dat2 */
{0x00003030, 0x72FF, 0x01}, /* Sdmmc3Dat3 */
{0x00003038, 0x1DFF, 0x01}, /* PexL0RstN */
{0x0000303C, 0x1DFF, 0x01}, /* PexL0ClkreqN */
{0x00003040, 0x1DFF, 0x01}, /* PexWakeN */
{0x00003044, 0x1DFF, 0x01}, /* PexL1RstN */
{0x00003048, 0x1DFF, 0x01}, /* PexL1ClkreqN */
{0x0000304C, 0x19FF, 0x01}, /* SataLedActive */
{0x00003050, 0x1F2FF, 0x01}, /* Spi1Mosi */
{0x00003054, 0x1F2FF, 0x01}, /* Spi1Miso */
{0x00003058, 0x1F2FF, 0x01}, /* Spi1Sck */
{0x0000305C, 0x1F2FF, 0x01}, /* Spi1Cs0 */
{0x00003060, 0x1F2FF, 0x01}, /* Spi1Cs1 */
{0x00003064, 0x72FF, 0x02}, /* Spi2Mosi */
{0x00003068, 0x72FF, 0x02}, /* Spi2Miso */
{0x0000306C, 0x72FF, 0x02}, /* Spi2Sck */
{0x00003070, 0x72FF, 0x02}, /* Spi2Cs0 */
{0x00003074, 0x72FF, 0x01}, /* Spi2Cs1 */
{0x00003078, 0x1F2FF, 0x01}, /* Spi4Mosi */
{0x0000307C, 0x1F2FF, 0x01}, /* Spi4Miso */
{0x00003080, 0x1F2FF, 0x01}, /* Spi4Sck */
{0x00003084, 0x1F2FF, 0x01}, /* Spi4Cs0 */
{0x00003088, 0x72FF, 0x01}, /* QspiSck */
{0x0000308C, 0x72FF, 0x01}, /* QspiCsN */
{0x00003090, 0x72FF, 0x01}, /* QspiIo0 */
{0x00003094, 0x72FF, 0x01}, /* QspiIo1 */
{0x00003098, 0x72FF, 0x01}, /* QspiIo2 */
{0x0000309C, 0x72FF, 0x01}, /* QspiIo3 */
{0x000030A4, 0x19FF, 0x02}, /* Dmic1Clk */
{0x000030A8, 0x19FF, 0x02}, /* Dmic1Dat */
{0x000030AC, 0x19FF, 0x02}, /* Dmic2Clk */
{0x000030B0, 0x19FF, 0x02}, /* Dmic2Dat */
{0x000030B4, 0x19FF, 0x02}, /* Dmic3Clk */
{0x000030B8, 0x19FF, 0x02}, /* Dmic3Dat */
{0x000030BC, 0x1DFF, 0x01}, /* Gen1I2cScl */
{0x000030C0, 0x1DFF, 0x01}, /* Gen1I2cSda */
{0x000030C4, 0x1DFF, 0x01}, /* Gen2I2cScl */
{0x000030C8, 0x1DFF, 0x01}, /* Gen2I2cSda */
{0x000030CC, 0x1DFF, 0x01}, /* Gen3I2cScl */
{0x000030D0, 0x1DFF, 0x01}, /* Gen3I2cSda */
{0x000030D4, 0x1DFF, 0x02}, /* CamI2cScl */
{0x000030D8, 0x1DFF, 0x02}, /* CamI2cSda */
{0x000030DC, 0x1DFF, 0x01}, /* PwrI2cScl */
{0x000030E0, 0x1DFF, 0x01}, /* PwrI2cSda */
{0x000030E4, 0x19FF, 0x01}, /* Uart1Tx */
{0x000030E8, 0x19FF, 0x01}, /* Uart1Rx */
{0x000030EC, 0x19FF, 0x01}, /* Uart1Rts */
{0x000030F0, 0x19FF, 0x01}, /* Uart1Cts */
{0x000030F4, 0x19FF, 0x00}, /* Uart2Tx */
{0x000030F8, 0x19FF, 0x00}, /* Uart2Rx */
{0x000030FC, 0x19FF, 0x02}, /* Uart2Rts */
{0x00003100, 0x19FF, 0x02}, /* Uart2Cts */
{0x00003104, 0x19FF, 0x02}, /* Uart3Tx */
{0x00003108, 0x19FF, 0x02}, /* Uart3Rx */
{0x0000310C, 0x19FF, 0x02}, /* Uart3Rts */
{0x00003110, 0x19FF, 0x02}, /* Uart3Cts */
{0x00003114, 0x19FF, 0x02}, /* Uart4Tx */
{0x00003118, 0x19FF, 0x02}, /* Uart4Rx */
{0x0000311C, 0x19FF, 0x02}, /* Uart4Rts */
{0x00003120, 0x19FF, 0x02}, /* Uart4Cts */
{0x00003124, 0x72FF, 0x01}, /* Dap1Fs */
{0x00003128, 0x72FF, 0x01}, /* Dap1Din */
{0x0000312C, 0x72FF, 0x01}, /* Dap1Dout */
{0x00003130, 0x72FF, 0x01}, /* Dap1Sclk */
{0x00003134, 0x72FF, 0x01}, /* Dap2Fs */
{0x00003138, 0x72FF, 0x01}, /* Dap2Din */
{0x0000313C, 0x72FF, 0x01}, /* Dap2Dout */
{0x00003140, 0x72FF, 0x01}, /* Dap2Sclk */
{0x00003144, 0x72FF, 0x01}, /* Dap4Fs */
{0x00003148, 0x72FF, 0x01}, /* Dap4Din */
{0x0000314C, 0x72FF, 0x01}, /* Dap4Dout */
{0x00003150, 0x72FF, 0x01}, /* Dap4Sclk */
{0x00003154, 0x72FF, 0x01}, /* Cam1Mclk */
{0x00003158, 0x72FF, 0x01}, /* Cam2Mclk */
{0x0000315C, 0x72FF, 0x01}, /* JtagRtck */
{0x00003160, 0x118C, 0xFF}, /* Clk32kIn */
{0x00003164, 0x72FF, 0x02}, /* Clk32kOut */
{0x00003168, 0x1DFF, 0x01}, /* BattBcl */
{0x0000316C, 0x11CC, 0xFF}, /* ClkReq */
{0x00003170, 0x11CC, 0xFF}, /* CpuPwrReq */
{0x00003174, 0x11CC, 0xFF}, /* PwrIntN */
{0x00003178, 0x11CC, 0xFF}, /* Shutdown */
{0x0000317C, 0x11CC, 0xFF}, /* CorePwrReq */
{0x00003180, 0x19FF, 0x01}, /* AudMclk */
{0x00003184, 0x19FF, 0x00}, /* DvfsPwm */
{0x00003188, 0x19FF, 0x00}, /* DvfsClk */
{0x0000318C, 0x19FF, 0x00}, /* GpioX1Aud */
{0x00003190, 0x19FF, 0x00}, /* GpioX3Aud */
{0x00003194, 0x1DFF, 0x00}, /* GpioPcc7 */
{0x00003198, 0x1DFF, 0x01}, /* HdmiCec */
{0x0000319C, 0x1DFF, 0x01}, /* HdmiIntDpHpd */
{0x000031A0, 0x19FF, 0x01}, /* SpdifOut */
{0x000031A4, 0x19FF, 0x01}, /* SpdifIn */
{0x000031A8, 0x1DFF, 0x01}, /* UsbVbusEn0 */
{0x000031AC, 0x1DFF, 0x01}, /* UsbVbusEn1 */
{0x000031B0, 0x19FF, 0x01}, /* DpHpd0 */
{0x000031B4, 0x19FF, 0x00}, /* WifiEn */
{0x000031B8, 0x19FF, 0x00}, /* WifiRst */
{0x000031BC, 0x19FF, 0x00}, /* WifiWakeAp */
{0x000031C0, 0x19FF, 0x00}, /* ApWakeBt */
{0x000031C4, 0x19FF, 0x00}, /* BtRst */
{0x000031C8, 0x19FF, 0x00}, /* BtWakeAp */
{0x000031CC, 0x19FF, 0x00}, /* ApWakeNfc */
{0x000031D0, 0x19FF, 0x00}, /* NfcEn */
{0x000031D4, 0x19FF, 0x00}, /* NfcInt */
{0x000031D8, 0x19FF, 0x00}, /* GpsEn */
{0x000031DC, 0x19FF, 0x00}, /* GpsRst */
{0x000031E0, 0x19FF, 0x01}, /* CamRst */
{0x000031E4, 0x19FF, 0x02}, /* CamAfEn */
{0x000031E8, 0x19FF, 0x02}, /* CamFlashEn */
{0x000031EC, 0x19FF, 0x01}, /* Cam1Pwdn */
{0x000031F0, 0x19FF, 0x01}, /* Cam2Pwdn */
{0x000031F4, 0x19FF, 0x01}, /* Cam1Strobe */
{0x000031F8, 0x19FF, 0x01}, /* LcdTe */
{0x000031FC, 0x19FF, 0x03}, /* LcdBlPwm */
{0x00003200, 0x19FF, 0x00}, /* LcdBlEn */
{0x00003204, 0x19FF, 0x00}, /* LcdRst */
{0x00003208, 0x19FF, 0x01}, /* LcdGpio1 */
{0x0000320C, 0x19FF, 0x02}, /* LcdGpio2 */
{0x00003210, 0x19FF, 0x00}, /* ApReady */
{0x00003214, 0x19FF, 0x00}, /* TouchRst */
{0x00003218, 0x19FF, 0x01}, /* TouchClk */
{0x0000321C, 0x19FF, 0x00}, /* ModemWakeAp */
{0x00003220, 0x19FF, 0x00}, /* TouchInt */
{0x00003224, 0x19FF, 0x00}, /* MotionInt */
{0x00003228, 0x19FF, 0x00}, /* AlsProxInt */
{0x0000322C, 0x19FF, 0x00}, /* TempAlert */
{0x00003230, 0x19FF, 0x00}, /* ButtonPowerOn */
{0x00003234, 0x19FF, 0x00}, /* ButtonVolUp */
{0x00003238, 0x19FF, 0x00}, /* ButtonVolDown */
{0x0000323C, 0x19FF, 0x00}, /* ButtonSlideSw */
{0x00003240, 0x19FF, 0x00}, /* ButtonHome */
{0x00003244, 0x19FF, 0x01}, /* GpioPa6 */
{0x00003248, 0x19FF, 0x00}, /* GpioPe6 */
{0x0000324C, 0x19FF, 0x00}, /* GpioPe7 */
{0x00003250, 0x19FF, 0x00}, /* GpioPh6 */
{0x00003254, 0x72FF, 0x02}, /* GpioPk0 */
{0x00003258, 0x72FF, 0x02}, /* GpioPk1 */
{0x0000325C, 0x72FF, 0x02}, /* GpioPk2 */
{0x00003260, 0x72FF, 0x02}, /* GpioPk3 */
{0x00003264, 0x72FF, 0x01}, /* GpioPk4 */
{0x00003268, 0x72FF, 0x01}, /* GpioPk5 */
{0x0000326C, 0x72FF, 0x01}, /* GpioPk6 */
{0x00003270, 0x72FF, 0x01}, /* GpioPk7 */
{0x00003274, 0x72FF, 0x00}, /* GpioPl0 */
{0x00003278, 0x72FF, 0x01}, /* GpioPl1 */
{0x0000327C, 0x72FF, 0x01}, /* GpioPz0 */
{0x00003280, 0x72FF, 0x02}, /* GpioPz1 */
{0x00003284, 0x72FF, 0x02}, /* GpioPz2 */
{0x00003288, 0x72FF, 0x01}, /* GpioPz3 */
{0x0000328C, 0x72FF, 0x01}, /* GpioPz4 */
{0x00003290, 0x72FF, 0x01}, /* GpioPz5 */
/* 5.0.0+ only */
{0x00003294, 0x1F2FF, 0x02}, /* Sdmmc2Dat0 */
{0x00003298, 0x1F2FF, 0x02}, /* Sdmmc2Dat1 */
{0x0000329C, 0x1F2FF, 0x02}, /* Sdmmc2Dat2 */
{0x000032A0, 0x1F2FF, 0x02}, /* Sdmmc2Dat3 */
{0x000032A4, 0x1F2FF, 0x02}, /* Sdmmc2Dat4 */
{0x000032A8, 0x1F2FF, 0x02}, /* Sdmmc2Dat5 */
{0x000032AC, 0x1F2FF, 0x02}, /* Sdmmc2Dat6 */
{0x000032B0, 0x1F2FF, 0x02}, /* Sdmmc2Dat7 */
{0x000032B4, 0x1F2FF, 0x02}, /* Sdmmc2Clk */
{0x000032B8, 0x1F2FF, 0x00}, /* Sdmmc2Clkb */
{0x000032BC, 0x1F2FF, 0x02}, /* Sdmmc2Cmd */
{0x000032C0, 0x1F2FF, 0x00}, /* Sdmmc2Dqs */
{0x000032C4, 0x1F2FF, 0x00}, /* Sdmmc2Dqsb */
};
static constexpr u32 PinmuxPadNameMax = (sizeof(PinmuxMap) / sizeof(PinmuxMap[0]));
static constexpr PinmuxDrivePadDefinition PinmuxDrivePadMap[] = {
{0x000008E4, 0x01F1F000}, /* AlsProxInt */
{0x000008E8, 0x01F1F000}, /* ApReady */
{0x000008EC, 0x01F1F000}, /* ApWakeBt */
{0x000008F0, 0x01F1F000}, /* ApWakeNfc */
{0x000008F4, 0x01F1F000}, /* AudMclk */
{0x000008F8, 0x01F1F000}, /* BattBcl */
{0x000008FC, 0x01F1F000}, /* BtRst */
{0x00000900, 0x01F1F000}, /* BtWakeAp */
{0x00000904, 0x01F1F000}, /* ButtonHome */
{0x00000908, 0x01F1F000}, /* ButtonPowerOn */
{0x0000090C, 0x01F1F000}, /* ButtonSlideSw */
{0x00000910, 0x01F1F000}, /* ButtonVolDown */
{0x00000914, 0x01F1F000}, /* ButtonVolUp */
{0x00000918, 0x01F1F000}, /* Cam1Mclk */
{0x0000091C, 0x01F1F000}, /* Cam1Pwdn */
{0x00000920, 0x01F1F000}, /* Cam1Strobe */
{0x00000924, 0x01F1F000}, /* Cam2Mclk */
{0x00000928, 0x01F1F000}, /* Cam2Pwdn */
{0x0000092C, 0x01F1F000}, /* CamAfEn */
{0x00000930, 0x01F1F000}, /* CamFlashEn */
{0x00000934, 0x01F1F000}, /* CamI2cScl */
{0x00000938, 0x01F1F000}, /* CamI2cSda */
{0x0000093C, 0x01F1F000}, /* CamRst */
{0x00000940, 0x01F1F000}, /* Clk32kIn */
{0x00000944, 0x01F1F000}, /* Clk32kOut */
{0x00000948, 0x01F1F000}, /* ClkReq */
{0x0000094C, 0x01F1F000}, /* CorePwrReq */
{0x00000950, 0x01F1F000}, /* CpuPwrReq */
{0x00000954, 0xF0000000}, /* Dap1Din */
{0x00000958, 0xF0000000}, /* Dap1Dout */
{0x0000095C, 0xF0000000}, /* Dap1Fs */
{0x00000960, 0xF0000000}, /* Dap1Sclk */
{0x00000964, 0xF0000000}, /* Dap2Din */
{0x00000968, 0xF0000000}, /* Dap2Dout */
{0x0000096C, 0xF0000000}, /* Dap2Fs */
{0x00000970, 0xF0000000}, /* Dap2Sclk */
{0x00000974, 0x01F1F000}, /* Dap4Din */
{0x00000978, 0x01F1F000}, /* Dap4Dout */
{0x0000097C, 0x01F1F000}, /* Dap4Fs */
{0x00000980, 0x01F1F000}, /* Dap4Sclk */
{0x00000984, 0x01F1F000}, /* Dmic1Clk */
{0x00000988, 0x01F1F000}, /* Dmic1Dat */
{0x0000098C, 0x01F1F000}, /* Dmic2Clk */
{0x00000990, 0x01F1F000}, /* Dmic2Dat */
{0x00000994, 0x01F1F000}, /* Dmic3Clk */
{0x00000998, 0x01F1F000}, /* Dmic3Dat */
{0x0000099C, 0x01F1F000}, /* DpHpd */
{0x000009A0, 0x01F1F000}, /* DvfsClk */
{0x000009A4, 0x01F1F000}, /* DvfsPwm */
{0x000009A8, 0x01F1F000}, /* Gen1I2cScl */
{0x000009AC, 0x01F1F000}, /* Gen1I2cSda */
{0x000009B0, 0x01F1F000}, /* Gen2I2cScl */
{0x000009B4, 0x01F1F000}, /* Gen2I2cSda */
{0x000009B8, 0x01F1F000}, /* Gen3I2cScl */
{0x000009BC, 0x01F1F000}, /* Gen3I2cSda */
{0x000009C0, 0x01F1F000}, /* GpioPa6 */
{0x000009C4, 0x01F1F000}, /* GpioPcc7 */
{0x000009C8, 0x01F1F000}, /* GpioPe6 */
{0x000009CC, 0x01F1F000}, /* GpioPe7 */
{0x000009D0, 0x01F1F000}, /* GpioPh6 */
{0x000009D4, 0xF0000000}, /* GpioPk0 */
{0x000009D8, 0xF0000000}, /* GpioPk1 */
{0x000009DC, 0xF0000000}, /* GpioPk2 */
{0x000009E0, 0xF0000000}, /* GpioPk3 */
{0x000009E4, 0xF0000000}, /* GpioPk4 */
{0x000009E8, 0xF0000000}, /* GpioPk5 */
{0x000009EC, 0xF0000000}, /* GpioPk6 */
{0x000009F0, 0xF0000000}, /* GpioPk7 */
{0x000009F4, 0xF0000000}, /* GpioPl0 */
{0x000009F8, 0xF0000000}, /* GpioPl1 */
{0x000009FC, 0x07F7F000}, /* GpioPz0 */
{0x00000A00, 0x07F7F000}, /* GpioPz1 */
{0x00000A04, 0x07F7F000}, /* GpioPz2 */
{0x00000A08, 0x07F7F000}, /* GpioPz3 */
{0x00000A0C, 0x07F7F000}, /* GpioPz4 */
{0x00000A10, 0x07F7F000}, /* GpioPz5 */
{0x00000A14, 0x01F1F000}, /* GpioX1Aud */
{0x00000A18, 0x01F1F000}, /* GpioX3Aud */
{0x00000A1C, 0x01F1F000}, /* GpsEn */
{0x00000A20, 0x01F1F000}, /* GpsRst */
{0x00000A24, 0x01F1F000}, /* HdmiCec */
{0x00000A28, 0x01F1F000}, /* HdmiIntDpHpd */
{0x00000A2C, 0x01F1F000}, /* JtagRtck */
{0x00000A30, 0x01F1F000}, /* LcdBlEn */
{0x00000A34, 0x01F1F000}, /* LcdBlPwm */
{0x00000A38, 0x01F1F000}, /* LcdGpio1 */
{0x00000A3C, 0x01F1F000}, /* LcdGpio2 */
{0x00000A40, 0x01F1F000}, /* LcdRst */
{0x00000A44, 0x01F1F000}, /* LcdTe */
{0x00000A48, 0x01F1F000}, /* ModemWakeAp */
{0x00000A4C, 0x01F1F000}, /* MotionInt */
{0x00000A50, 0x01F1F000}, /* NfcEn */
{0x00000A54, 0x01F1F000}, /* NfcInt */
{0x00000A58, 0x01F1F000}, /* PexL0ClkReqN */
{0x00000A5C, 0x01F1F000}, /* PexL0RstN */
{0x00000A60, 0x01F1F000}, /* PexL1ClkreqN */
{0x00000A64, 0x01F1F000}, /* PexL1RstN */
{0x00000A68, 0x01F1F000}, /* PexWakeN */
{0x00000A6C, 0x01F1F000}, /* PwrI2cScl */
{0x00000A70, 0x01F1F000}, /* PwrI2cSda */
{0x00000A74, 0x01F1F000}, /* PwrIntN */
{0x00000A78, 0x07F7F000}, /* QspiComp */
{0x00000A90, 0xF0000000}, /* QspiSck */
{0x00000A94, 0x01F1F000}, /* SataLedActive */
{0x00000A98, 0xF7F7F000}, /* Sdmmc1Pad */
{0x00000AB0, 0xF7F7F000}, /* Sdmmc3Pad */
{0x00000AC8, 0x01F1F000}, /* Shutdown */
{0x00000ACC, 0x01F1F000}, /* SpdifIn */
{0x00000AD0, 0x01F1F000}, /* SpdifOut */
{0x00000AD4, 0xF0000000}, /* Spi1Cs0 */
{0x00000AD8, 0xF0000000}, /* Spi1Cs1 */
{0x00000ADC, 0xF0000000}, /* Spi1Miso */
{0x00000AE0, 0xF0000000}, /* Spi1Mosi */
{0x00000AE4, 0xF0000000}, /* Spi1Sck */
{0x00000AE8, 0xF0000000}, /* Spi2Cs0 */
{0x00000AEC, 0xF0000000}, /* Spi2Cs1 */
{0x00000AF0, 0xF0000000}, /* Spi2Miso */
{0x00000AF4, 0xF0000000}, /* Spi2Mosi */
{0x00000AF8, 0xF0000000}, /* Spi2Sck */
{0x00000AFC, 0xF0000000}, /* Spi4Cs0 */
{0x00000B00, 0xF0000000}, /* Spi4Miso */
{0x00000B04, 0xF0000000}, /* Spi4Mosi */
{0x00000B08, 0xF0000000}, /* Spi4Sck */
{0x00000B0C, 0x01F1F000}, /* TempAlert */
{0x00000B10, 0x01F1F000}, /* TouchClk */
{0x00000B14, 0x01F1F000}, /* TouchInt */
{0x00000B18, 0x01F1F000}, /* TouchRst */
{0x00000B1C, 0x01F1F000}, /* Uart1Cts */
{0x00000B20, 0x01F1F000}, /* Uart1Rts */
{0x00000B24, 0x01F1F000}, /* Uart1Rx */
{0x00000B28, 0x01F1F000}, /* Uart1Tx */
{0x00000B2C, 0x01F1F000}, /* Uart2Cts */
{0x00000B30, 0x01F1F000}, /* Uart2Rts */
{0x00000B34, 0x01F1F000}, /* Uart2Rx */
{0x00000B38, 0x01F1F000}, /* Uart2Tx */
{0x00000B3C, 0x01F1F000}, /* Uart3Cts */
{0x00000B40, 0x01F1F000}, /* Uart3Rts */
{0x00000B44, 0x01F1F000}, /* Uart3Rx */
{0x00000B48, 0x01F1F000}, /* Uart3Tx */
{0x00000B4C, 0x01F1F000}, /* Uart4Cts */
{0x00000B50, 0x01F1F000}, /* Uart4Rts */
{0x00000B54, 0x01F1F000}, /* Uart4Rx */
{0x00000B58, 0x01F1F000}, /* Uart4Tx */
{0x00000B5C, 0x01F1F000}, /* UsbVbusEn0 */
{0x00000B60, 0x01F1F000}, /* UsbVbusEn1 */
{0x00000B64, 0x01F1F000}, /* WifiEn */
{0x00000B68, 0x01F1F000}, /* WifiRst */
{0x00000B6C, 0x01F1F000}, /* WifiWakeAp */
};
static constexpr u32 PinmuxDrivePadNameMax = (sizeof(PinmuxDrivePadMap) / sizeof(PinmuxDrivePadMap[0]));
@@ -0,0 +1,487 @@
/*
* 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)
return pinmux_val;
u32 pm_config_val = (pinmux_config_val & 0x07);
u32 pm_val = pm_config_val;
/* Adjust PM */
if (pinmux_config_mask_val & 0x07) {
/* Default to safe value */
if (pm_config_val >= 0x06)
pm_val = 0x04;
/* Apply additional changes first */
if (pm_config_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;
}
}
/* Default to safe value */
pm_val = 0x04;
}
/* Translate PM value if necessary */
if ((pm_val & 0xFF) == 0x04)
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 & 0x0C) != (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) & 0x10000);
/* 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 |= ioreset_config_val;
}
}
}
u32 park_config_val = ((pinmux_config_val >> 0x0A) & 0x20);
/* 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 |= park_config_val;
}
}
}
u32 elpdr_config_val = ((pinmux_config_val >> 0x0B) & 0x100);
/* 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 |= elpdr_config_val;
}
}
}
u32 ehsm_config_val = ((pinmux_config_val >> 0x0C) & 0x200);
/* 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 |= ehsm_config_val;
}
}
}
u32 eiohv_config_val = ((pinmux_config_val >> 0x09) & 0x400);
/* 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 |= eiohv_config_val;
}
}
}
u32 eschmt_config_val = ((pinmux_config_val >> 0x0D) & 0x1000);
/* 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 |= eschmt_config_val;
}
}
}
u32 preemp_config_val = ((pinmux_config_val >> 0x0D) & 0x8000);
/* 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 |= preemp_config_val;
}
}
}
u32 drvtype_config_val = ((pinmux_config_val >> 0x0E) & 0x6000);
/* 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 |= 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;
}
@@ -0,0 +1,57 @@
/*
* 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"
static constexpr u32 SmcFunctionId_AtmosphereReadWriteRegister = 0xF0000002;
static constexpr u32 PmcPhysStart = 0x7000E400;
static constexpr u32 PmcPhysEnd = 0x7000EFFF;
static inline 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) {
SecmonArgs args;
args.X[0] = SmcFunctionId_AtmosphereReadWriteRegister;
args.X[1] = phys_addr;
args.X[2] = mask;
args.X[3] = value;
svcCallSecureMonitor(&args);
if (args.X[0] != 0) {
std::abort();
}
return static_cast<u32>(args.X[1]);
}
u32 Boot::ReadPmcRegister(u32 phys_addr) {
if (!IsValidPmcAddress(phys_addr)) {
std::abort();
}
return SmcAtmosphereReadWriteRegister(phys_addr, 0, 0);
}
void Boot::WritePmcRegister(u32 phys_addr, u32 value, u32 mask) {
if (!IsValidPmcAddress(phys_addr)) {
std::abort();
}
SmcAtmosphereReadWriteRegister(phys_addr, value, mask);
}
@@ -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 <switch.h>
#include <stratosphere.hpp>
#include "boot_functions.hpp"
#include "boot_pmic_driver.hpp"
void PmicDriver::ShutdownSystem() {
if (R_FAILED(this->ShutdownSystem(false))) {
std::abort();
}
}
void PmicDriver::RebootSystem() {
if (R_FAILED(this->ShutdownSystem(true))) {
std::abort();
}
}
Result PmicDriver::GetAcOk(bool *out) {
u8 power_status;
Result rc = this->GetPowerStatus(&power_status);
if (R_FAILED(rc)) {
return rc;
}
*out = (power_status & 0x02) != 0;
return ResultSuccess;
}
Result PmicDriver::GetPowerIntr(u8 *out) {
const u8 addr = 0x0B;
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetPowerStatus(u8 *out) {
const u8 addr = 0x15;
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetNvErc(u8 *out) {
const u8 addr = 0x0C;
return Boot::ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr));
}
Result PmicDriver::GetPowerButtonPressed(bool *out) {
u8 power_intr;
Result rc = this->GetPowerIntr(&power_intr);
if (R_FAILED(rc)) {
return rc;
}
*out = (power_intr & 0x08) != 0;
return ResultSuccess;
}
Result PmicDriver::ShutdownSystem(bool reboot) {
const u8 on_off_1_addr = 0x41;
const u8 on_off_2_addr = 0x42;
/* Get value, set or clear software reset mask. */
u8 on_off_2_val = 0;
if (R_FAILED(Boot::ReadI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr)))) {
std::abort();
}
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();
}
/* Get value, set software reset mask. */
u8 on_off_1_val = 0;
if (R_FAILED(Boot::ReadI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr)))) {
std::abort();
}
on_off_1_val |= 0x80;
/* Finalize the battery. */
{
BatteryDriver battery_driver;
this->FinalizeBattery(&battery_driver);
}
/* Actually write the value to trigger shutdown/reset. */
if (R_FAILED(Boot::WriteI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr)))) {
std::abort();
}
/* Allow up to 5 seconds for shutdown/reboot to take place. */
svcSleepThread(5'000'000'000ul);
std::abort();
}
void PmicDriver::FinalizeBattery(BatteryDriver *battery_driver) {
/* Set shutdown timer. */
battery_driver->SetShutdownTimer();
/* Get whether shutdown is enabled. */
bool shutdown_enabled;
if (R_FAILED(battery_driver->GetShutdownEnabled(&shutdown_enabled))) {
return;
}
bool ac_ok;
bool desired_shutdown_enabled;
if (R_FAILED(this->GetAcOk(&ac_ok)) || ac_ok) {
desired_shutdown_enabled = false;
} else {
desired_shutdown_enabled = true;
}
if (shutdown_enabled != desired_shutdown_enabled) {
battery_driver->SetShutdownEnabled(desired_shutdown_enabled);
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c_driver/i2c_api.hpp"
#include "boot_battery_driver.hpp"
class PmicDriver {
private:
I2cSessionImpl i2c_session;
public:
PmicDriver() {
I2cDriver::Initialize();
I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic);
}
~PmicDriver() {
I2cDriver::CloseSession(this->i2c_session);
I2cDriver::Finalize();
}
private:
Result GetPowerStatus(u8 *out);
Result ShutdownSystem(bool reboot);
void FinalizeBattery(BatteryDriver *battery_driver);
public:
void ShutdownSystem();
void RebootSystem();
Result GetAcOk(bool *out);
Result GetPowerIntr(u8 *out);
Result GetNvErc(u8 *out);
Result GetPowerButtonPressed(bool *out);
};
@@ -0,0 +1,75 @@
/*
* 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();
}
}
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#define IRAM_BASE 0x40000000ull
#define IRAM_SIZE 0x40000
#define IRAM_PAYLOAD_MAX_SIZE 0x2E000
#define IRAM_PAYLOAD_BASE 0x40010000ull
class BootRebootManager {
public:
static Result PerformReboot();
static void RebootForFatalError(AtmosphereFatalErrorContext *ctx);
};
@@ -0,0 +1,119 @@
/*
* 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 "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;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_U = 0xC;
static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_L = 0x10;
static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_H = 0x14;
static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_U = 0x18;
static constexpr size_t CLK_RST_CONTROLLER_CCLK_BURST_POLICY = 0x20;
static constexpr size_t CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER = 0x24;
static constexpr size_t CLK_RST_CONTROLLER_SCLK_BURST_POLICY = 0x28;
static constexpr size_t CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER = 0x2C;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SYSTEM_RATE = 0x30;
static constexpr size_t CLK_RST_CONTROLLER_MISC_CLK_ENB = 0x48;
static constexpr size_t CLK_RST_CONTROLLER_OSC_CTRL = 0x50;
static constexpr size_t CLK_RST_CONTROLLER_PLLC_BASE = 0x80;
static constexpr size_t CLK_RST_CONTROLLER_PLLC_MISC = 0x88;
static constexpr size_t CLK_RST_CONTROLLER_PLLM_BASE = 0x90;
static constexpr size_t CLK_RST_CONTROLLER_PLLM_MISC1 = 0x98;
static constexpr size_t CLK_RST_CONTROLLER_PLLM_MISC2 = 0x9C;
static constexpr size_t CLK_RST_CONTROLLER_PLLP_BASE = 0xA0;
static constexpr size_t CLK_RST_CONTROLLER_PLLD_BASE = 0xD0;
static constexpr size_t CLK_RST_CONTROLLER_PLLD_MISC1 = 0xD8;
static constexpr size_t CLK_RST_CONTROLLER_PLLD_MISC = 0xDC;
static constexpr size_t CLK_RST_CONTROLLER_PLLX_BASE = 0xE0;
static constexpr size_t CLK_RST_CONTROLLER_PLLX_MISC = 0xE4;
static constexpr size_t CLK_RST_CONTROLLER_PLLE_BASE = 0xE8;
static constexpr size_t CLK_RST_CONTROLLER_PLLE_MISC = 0xEC;
static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA = 0xF8;
static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB = 0xFC;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_PWM = 0x110;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 = 0x124;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 = 0x128;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_DISP1 = 0x138;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_VI = 0x148;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 = 0x150;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 = 0x154;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 = 0x164;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_UARTA = 0x178;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_UARTB = 0x17C;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X = 0x180;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_UARTC = 0x1A0;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 = 0x1B8;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 = 0x1BC;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_CSITE = 0x1D4;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_EMC = 0x19C;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_TSEC = 0x1F4;
static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_X = 0x280;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_X_SET = 0x284;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_X_CLR = 0x288;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_X = 0x28C;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_X_SET = 0x290;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_X_CLR = 0x294;
static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_Y = 0x298;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_Y_SET = 0x29C;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_Y_CLR = 0x2A0;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_Y = 0x2A4;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_Y_SET = 0x2A8;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_Y_CLR = 0x2AC;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_L_SET = 0x300;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_L_CLR = 0x304;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_H_SET = 0x308;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_H_CLR = 0x30C;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_U_SET = 0x310;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_U_CLR = 0x314;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_L_SET = 0x320;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_L_CLR = 0x324;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_H_SET = 0x328;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_H_CLR = 0x32C;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_U_SET = 0x330;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_U_CLR = 0x334;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_V = 0x358;
static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_W = 0x35C;
static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_V = 0x360;
static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_W = 0x364;
static constexpr size_t CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 = 0x388;
static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC = 0x3A0;
static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD = 0x3A4;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT = 0x3B4;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 = 0x410;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SE = 0x42C;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_V_SET = 0x440;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_W_SET = 0x448;
static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_W_CLR = 0x44C;
static constexpr size_t CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET = 0x450;
static constexpr size_t CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR = 0x454;
static constexpr size_t CLK_RST_CONTROLLER_UTMIP_PLL_CFG2 = 0x488;
static constexpr size_t CLK_RST_CONTROLLER_PLLE_AUX = 0x48C;
static constexpr size_t CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S0 = 0x4A0;
static constexpr size_t CLK_RST_CONTROLLER_PLLX_MISC_3 = 0x518;
static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE = 0x554;
static constexpr size_t CLK_RST_CONTROLLER_SPARE_REG0 = 0x55C;
static constexpr size_t CLK_RST_CONTROLLER_PLLMB_BASE = 0x5E8;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP = 0x620;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_EMC_DLL = 0x664;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL = 0x66C;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM = 0x694;
static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_NVENC = 0x6A0;
static constexpr size_t CLK_RST_CONTROLLER_SE_SUPER_CLK_DIVIDER = 0x704;
@@ -0,0 +1,353 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (C) 2018 CTCaer
* 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 "boot_types.hpp"
#define DC_CMD_GENERAL_INCR_SYNCPT 0x00
#define DC_CMD_GENERAL_INCR_SYNCPT_CNTRL 0x01
#define SYNCPT_CNTRL_NO_STALL (1 << 8)
#define SYNCPT_CNTRL_SOFT_RESET (1 << 0)
#define DC_CMD_CONT_SYNCPT_VSYNC 0x28
#define SYNCPT_VSYNC_ENABLE (1 << 8)
#define DC_CMD_DISPLAY_COMMAND_OPTION0 0x031
#define DC_CMD_DISPLAY_COMMAND 0x32
#define DISP_CTRL_MODE_STOP (0 << 5)
#define DISP_CTRL_MODE_C_DISPLAY (1 << 5)
#define DISP_CTRL_MODE_NC_DISPLAY (2 << 5)
#define DISP_CTRL_MODE_MASK (3 << 5)
#define DC_CMD_DISPLAY_POWER_CONTROL 0x36
#define PW0_ENABLE (1 << 0)
#define PW1_ENABLE (1 << 2)
#define PW2_ENABLE (1 << 4)
#define PW3_ENABLE (1 << 6)
#define PW4_ENABLE (1 << 8)
#define PM0_ENABLE (1 << 16)
#define PM1_ENABLE (1 << 18)
#define DC_CMD_INT_MASK 0x38
#define DC_CMD_INT_ENABLE 0x39
#define DC_CMD_STATE_ACCESS 0x40
#define READ_MUX (1 << 0)
#define WRITE_MUX (1 << 2)
#define DC_CMD_STATE_CONTROL 0x41
#define GENERAL_ACT_REQ (1 << 0)
#define WIN_A_ACT_REQ (1 << 1)
#define WIN_B_ACT_REQ (1 << 2)
#define WIN_C_ACT_REQ (1 << 3)
#define CURSOR_ACT_REQ (1 << 7)
#define GENERAL_UPDATE (1 << 8)
#define WIN_A_UPDATE (1 << 9)
#define WIN_B_UPDATE (1 << 10)
#define WIN_C_UPDATE (1 << 11)
#define CURSOR_UPDATE (1 << 15)
#define NC_HOST_TRIG (1 << 24)
#define DC_CMD_DISPLAY_WINDOW_HEADER 0x42
#define WINDOW_A_SELECT (1 << 4)
#define WINDOW_B_SELECT (1 << 5)
#define WINDOW_C_SELECT (1 << 6)
#define DC_CMD_REG_ACT_CONTROL 0x043
#define DC_COM_CRC_CONTROL 0x300
#define DC_COM_PIN_OUTPUT_ENABLE(x) (0x302 + (x))
#define DC_COM_PIN_OUTPUT_POLARITY(x) (0x306 + (x))
#define DC_COM_DSC_TOP_CTL 0x33E
#define DC_DISP_DISP_WIN_OPTIONS 0x402
#define HDMI_ENABLE (1 << 30)
#define DSI_ENABLE (1 << 29)
#define SOR1_TIMING_CYA (1 << 27)
#define SOR1_ENABLE (1 << 26)
#define SOR_ENABLE (1 << 25)
#define CURSOR_ENABLE (1 << 16)
#define DC_DISP_DISP_MEM_HIGH_PRIORITY 0x403
#define DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER 0x404
#define DC_DISP_DISP_TIMING_OPTIONS 0x405
#define DC_DISP_REF_TO_SYNC 0x406
#define DC_DISP_SYNC_WIDTH 0x407
#define DC_DISP_BACK_PORCH 0x408
#define DC_DISP_ACTIVE 0x409
#define DC_DISP_FRONT_PORCH 0x40A
#define DC_DISP_DISP_CLOCK_CONTROL 0x42E
#define PIXEL_CLK_DIVIDER_PCD1 (0 << 8)
#define PIXEL_CLK_DIVIDER_PCD1H (1 << 8)
#define PIXEL_CLK_DIVIDER_PCD2 (2 << 8)
#define PIXEL_CLK_DIVIDER_PCD3 (3 << 8)
#define PIXEL_CLK_DIVIDER_PCD4 (4 << 8)
#define PIXEL_CLK_DIVIDER_PCD6 (5 << 8)
#define PIXEL_CLK_DIVIDER_PCD8 (6 << 8)
#define PIXEL_CLK_DIVIDER_PCD9 (7 << 8)
#define PIXEL_CLK_DIVIDER_PCD12 (8 << 8)
#define PIXEL_CLK_DIVIDER_PCD16 (9 << 8)
#define PIXEL_CLK_DIVIDER_PCD18 (10 << 8)
#define PIXEL_CLK_DIVIDER_PCD24 (11 << 8)
#define PIXEL_CLK_DIVIDER_PCD13 (12 << 8)
#define SHIFT_CLK_DIVIDER(x) ((x) & 0xff)
#define DC_DISP_DISP_INTERFACE_CONTROL 0x42F
#define DISP_DATA_FORMAT_DF1P1C (0 << 0)
#define DISP_DATA_FORMAT_DF1P2C24B (1 << 0)
#define DISP_DATA_FORMAT_DF1P2C18B (2 << 0)
#define DISP_DATA_FORMAT_DF1P2C16B (3 << 0)
#define DISP_DATA_FORMAT_DF2S (4 << 0)
#define DISP_DATA_FORMAT_DF3S (5 << 0)
#define DISP_DATA_FORMAT_DFSPI (6 << 0)
#define DISP_DATA_FORMAT_DF1P3C24B (7 << 0)
#define DISP_DATA_FORMAT_DF1P3C18B (8 << 0)
#define DISP_ALIGNMENT_MSB (0 << 8)
#define DISP_ALIGNMENT_LSB (1 << 8)
#define DISP_ORDER_RED_BLUE (0 << 9)
#define DISP_ORDER_BLUE_RED (1 << 9)
#define DC_DISP_DISP_COLOR_CONTROL 0x430
#define DITHER_CONTROL_MASK (3 << 8)
#define DITHER_CONTROL_DISABLE (0 << 8)
#define DITHER_CONTROL_ORDERED (2 << 8)
#define DITHER_CONTROL_ERRDIFF (3 << 8)
#define BASE_COLOR_SIZE_MASK (0xf << 0)
#define BASE_COLOR_SIZE_666 (0 << 0)
#define BASE_COLOR_SIZE_111 (1 << 0)
#define BASE_COLOR_SIZE_222 (2 << 0)
#define BASE_COLOR_SIZE_333 (3 << 0)
#define BASE_COLOR_SIZE_444 (4 << 0)
#define BASE_COLOR_SIZE_555 (5 << 0)
#define BASE_COLOR_SIZE_565 (6 << 0)
#define BASE_COLOR_SIZE_332 (7 << 0)
#define BASE_COLOR_SIZE_888 (8 << 0)
#define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431
#define SC1_H_QUALIFIER_NONE (1 << 16)
#define SC0_H_QUALIFIER_NONE (1 << 0)
#define DC_DISP_DATA_ENABLE_OPTIONS 0x432
#define DE_SELECT_ACTIVE_BLANK (0 << 0)
#define DE_SELECT_ACTIVE (1 << 0)
#define DE_SELECT_ACTIVE_IS (2 << 0)
#define DE_CONTROL_ONECLK (0 << 2)
#define DE_CONTROL_NORMAL (1 << 2)
#define DE_CONTROL_EARLY_EXT (2 << 2)
#define DE_CONTROL_EARLY (3 << 2)
#define DE_CONTROL_ACTIVE_BLANK (4 << 2)
#define DC_DISP_DC_MCCIF_FIFOCTRL 0x480
#define DC_DISP_SD_BL_PARAMETERS 0x4D7
#define DC_DISP_SD_BL_CONTROL 0x4DC
#define DC_DISP_BLEND_BACKGROUND_COLOR 0x4E4
#define DC_WIN_CSC_YOF 0x611
#define DC_WIN_CSC_KYRGB 0x612
#define DC_WIN_CSC_KUR 0x613
#define DC_WIN_CSC_KVR 0x614
#define DC_WIN_CSC_KUG 0x615
#define DC_WIN_CSC_KVG 0x616
#define DC_WIN_CSC_KUB 0x617
#define DC_WIN_CSC_KVB 0x618
#define DC_WIN_AD_WIN_OPTIONS 0xB80
#define DC_WIN_BD_WIN_OPTIONS 0xD80
#define DC_WIN_CD_WIN_OPTIONS 0xF80
// The following registers are A/B/C shadows of the 0xB80/0xD80/0xF80 registers (see DISPLAY_WINDOW_HEADER).
#define DC_WIN_WIN_OPTIONS 0x700
#define H_DIRECTION (1 << 0)
#define V_DIRECTION (1 << 2)
#define SCAN_COLUMN (1 << 4)
#define COLOR_EXPAND (1 << 6)
#define CSC_ENABLE (1 << 18)
#define WIN_ENABLE (1 << 30)
#define DC_WIN_COLOR_DEPTH 0x703
#define WIN_COLOR_DEPTH_P1 0x0
#define WIN_COLOR_DEPTH_P2 0x1
#define WIN_COLOR_DEPTH_P4 0x2
#define WIN_COLOR_DEPTH_P8 0x3
#define WIN_COLOR_DEPTH_B4G4R4A4 0x4
#define WIN_COLOR_DEPTH_B5G5R5A 0x5
#define WIN_COLOR_DEPTH_B5G6R5 0x6
#define WIN_COLOR_DEPTH_AB5G5R5 0x7
#define WIN_COLOR_DEPTH_B8G8R8A8 0xC
#define WIN_COLOR_DEPTH_R8G8B8A8 0xD
#define WIN_COLOR_DEPTH_B6x2G6x2R6x2A8 0xE
#define WIN_COLOR_DEPTH_R6x2G6x2B6x2A8 0xF
#define WIN_COLOR_DEPTH_YCbCr422 0x10
#define WIN_COLOR_DEPTH_YUV422 0x11
#define WIN_COLOR_DEPTH_YCbCr420P 0x12
#define WIN_COLOR_DEPTH_YUV420P 0x13
#define WIN_COLOR_DEPTH_YCbCr422P 0x14
#define WIN_COLOR_DEPTH_YUV422P 0x15
#define WIN_COLOR_DEPTH_YCbCr422R 0x16
#define WIN_COLOR_DEPTH_YUV422R 0x17
#define WIN_COLOR_DEPTH_YCbCr422RA 0x18
#define WIN_COLOR_DEPTH_YUV422RA 0x19
#define DC_WIN_BUFFER_CONTROL 0x702
#define DC_WIN_POSITION 0x704
#define DC_WIN_SIZE 0x705
#define H_SIZE(x) (((x) & 0x1fff) << 0)
#define V_SIZE(x) (((x) & 0x1fff) << 16)
#define DC_WIN_PRESCALED_SIZE 0x706
#define H_PRESCALED_SIZE(x) (((x) & 0x7fff) << 0)
#define V_PRESCALED_SIZE(x) (((x) & 0x1fff) << 16)
#define DC_WIN_H_INITIAL_DDA 0x707
#define DC_WIN_V_INITIAL_DDA 0x708
#define DC_WIN_DDA_INC 0x709
#define H_DDA_INC(x) (((x) & 0xffff) << 0)
#define V_DDA_INC(x) (((x) & 0xffff) << 16)
#define DC_WIN_LINE_STRIDE 0x70A
#define LINE_STRIDE(x) (x)
#define UV_LINE_STRIDE(x) (((x) & 0xffff) << 16)
#define DC_WIN_DV_CONTROL 0x70E
// The following registers are A/B/C shadows of the 0xBC0/0xDC0/0xFC0 registers (see DISPLAY_WINDOW_HEADER).
#define DC_WINBUF_START_ADDR 0x800
#define DC_WINBUF_ADDR_H_OFFSET 0x806
#define DC_WINBUF_ADDR_V_OFFSET 0x808
#define DC_WINBUF_SURFACE_KIND 0x80B
#define PITCH (0 << 0)
#define TILED (1 << 0)
#define BLOCK (2 << 0)
#define BLOCK_HEIGHT(x) (((x) & 0x7) << 4)
/*! Display serial interface registers. */
#define _DSIREG(reg) ((reg) * 4)
#define DSI_RD_DATA 0x9
#define DSI_WR_DATA 0xA
#define DSI_POWER_CONTROL 0xB
#define DSI_POWER_CONTROL_ENABLE 1
#define DSI_INT_ENABLE 0xC
#define DSI_INT_STATUS 0xD
#define DSI_INT_MASK 0xE
#define DSI_HOST_CONTROL 0xF
#define DSI_HOST_CONTROL_FIFO_RESET (1 << 21)
#define DSI_HOST_CONTROL_CRC_RESET (1 << 20)
#define DSI_HOST_CONTROL_TX_TRIG_SOL (0 << 12)
#define DSI_HOST_CONTROL_TX_TRIG_FIFO (1 << 12)
#define DSI_HOST_CONTROL_TX_TRIG_HOST (2 << 12)
#define DSI_HOST_CONTROL_RAW (1 << 6)
#define DSI_HOST_CONTROL_HS (1 << 5)
#define DSI_HOST_CONTROL_FIFO_SEL (1 << 4)
#define DSI_HOST_CONTROL_IMM_BTA (1 << 3)
#define DSI_HOST_CONTROL_PKT_BTA (1 << 2)
#define DSI_HOST_CONTROL_CS (1 << 1)
#define DSI_HOST_CONTROL_ECC (1 << 0)
#define DSI_CONTROL 0x10
#define DSI_CONTROL_HS_CLK_CTRL (1 << 20)
#define DSI_CONTROL_CHANNEL(c) (((c) & 0x3) << 16)
#define DSI_CONTROL_FORMAT(f) (((f) & 0x3) << 12)
#define DSI_CONTROL_TX_TRIG(x) (((x) & 0x3) << 8)
#define DSI_CONTROL_LANES(n) (((n) & 0x3) << 4)
#define DSI_CONTROL_DCS_ENABLE (1 << 3)
#define DSI_CONTROL_SOURCE(s) (((s) & 0x1) << 2)
#define DSI_CONTROL_VIDEO_ENABLE (1 << 1)
#define DSI_CONTROL_HOST_ENABLE (1 << 0)
#define DSI_SOL_DELAY 0x11
#define DSI_MAX_THRESHOLD 0x12
#define DSI_TRIGGER 0x13
#define DSI_TRIGGER_HOST (1 << 1)
#define DSI_TRIGGER_VIDEO (1 << 0)
#define DSI_TX_CRC 0x14
#define DSI_STATUS 0x15
#define DSI_INIT_SEQ_CONTROL 0x1A
#define DSI_INIT_SEQ_DATA_0 0x1B
#define DSI_INIT_SEQ_DATA_1 0x1C
#define DSI_INIT_SEQ_DATA_2 0x1D
#define DSI_INIT_SEQ_DATA_3 0x1E
#define DSI_PKT_SEQ_0_LO 0x23
#define DSI_PKT_SEQ_0_HI 0x24
#define DSI_PKT_SEQ_1_LO 0x25
#define DSI_PKT_SEQ_1_HI 0x26
#define DSI_PKT_SEQ_2_LO 0x27
#define DSI_PKT_SEQ_2_HI 0x28
#define DSI_PKT_SEQ_3_LO 0x29
#define DSI_PKT_SEQ_3_HI 0x2A
#define DSI_PKT_SEQ_4_LO 0x2B
#define DSI_PKT_SEQ_4_HI 0x2C
#define DSI_PKT_SEQ_5_LO 0x2D
#define DSI_PKT_SEQ_5_HI 0x2E
#define DSI_DCS_CMDS 0x33
#define DSI_PKT_LEN_0_1 0x34
#define DSI_PKT_LEN_2_3 0x35
#define DSI_PKT_LEN_4_5 0x36
#define DSI_PKT_LEN_6_7 0x37
#define DSI_PHY_TIMING_0 0x3C
#define DSI_PHY_TIMING_1 0x3D
#define DSI_PHY_TIMING_2 0x3E
#define DSI_BTA_TIMING 0x3F
#define DSI_TIMEOUT_0 0x44
#define DSI_TIMEOUT_LRX(x) (((x) & 0xffff) << 16)
#define DSI_TIMEOUT_HTX(x) (((x) & 0xffff) << 0)
#define DSI_TIMEOUT_1 0x45
#define DSI_TIMEOUT_PR(x) (((x) & 0xffff) << 16)
#define DSI_TIMEOUT_TA(x) (((x) & 0xffff) << 0)
#define DSI_TO_TALLY 0x46
#define DSI_PAD_CONTROL_0 0x4B
#define DSI_PAD_CONTROL_VS1_PULLDN_CLK (1 << 24)
#define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16)
#define DSI_PAD_CONTROL_VS1_PDIO_CLK (1 << 8)
#define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0)
#define DSI_PAD_CONTROL_CD 0x4c
#define DSI_VIDEO_MODE_CONTROL 0x4E
#define DSI_PAD_CONTROL_1 0x4F
#define DSI_PAD_CONTROL_2 0x50
#define DSI_PAD_CONTROL_3 0x51
#define DSI_PAD_PREEMP_PD_CLK(x) (((x) & 0x3) << 12)
#define DSI_PAD_PREEMP_PU_CLK(x) (((x) & 0x3) << 8)
#define DSI_PAD_PREEMP_PD(x) (((x) & 0x3) << 4)
#define DSI_PAD_PREEMP_PU(x) (((x) & 0x3) << 0)
#define DSI_PAD_CONTROL_4 0x52
#define DSI_PAD_CONTROL_5_MARIKO 0x53
#define DSI_PAD_CONTROL_6_MARIKO 0x54
#define DSI_PAD_CONTROL_7_MARIKO 0x55
#define DSI_INIT_SEQ_DATA_15 0x5F
#define MIPI_CAL_MIPI_BIAS_PAD_CFG2 0x60
#define DSI_INIT_SEQ_DATA_15_MARIKO 0x62
@@ -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 "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;
static constexpr size_t GPIO_PORT6_CNF_1 = 0x504;
static constexpr size_t GPIO_PORT6_OE_1 = 0x514;
static constexpr size_t GPIO_PORT6_OUT_1 = 0x524;
@@ -0,0 +1,75 @@
/*
* 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 "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;
static constexpr size_t APB_MISC_GP_VGPIO_GPIO_MUX_SEL = 0xB74;
static constexpr size_t PINMUX_AUX_SDMMC1_CLK = 0x00;
static constexpr size_t PINMUX_AUX_SDMMC1_CMD = 0x04;
static constexpr size_t PINMUX_AUX_SDMMC1_DAT3 = 0x08;
static constexpr size_t PINMUX_AUX_SDMMC1_DAT2 = 0x0C;
static constexpr size_t PINMUX_AUX_SDMMC1_DAT1 = 0x10;
static constexpr size_t PINMUX_AUX_SDMMC1_DAT0 = 0x14;
static constexpr size_t PINMUX_AUX_SDMMC3_CLK = 0x1C;
static constexpr size_t PINMUX_AUX_SDMMC3_CMD = 0x20;
static constexpr size_t PINMUX_AUX_SDMMC3_DAT0 = 0x24;
static constexpr size_t PINMUX_AUX_SDMMC3_DAT1 = 0x28;
static constexpr size_t PINMUX_AUX_SDMMC3_DAT2 = 0x2C;
static constexpr size_t PINMUX_AUX_SDMMC3_DAT3 = 0x30;
static constexpr size_t PINMUX_AUX_DMIC3_CLK = 0xB4;
static constexpr size_t PINMUX_AUX_UART2_TX = 0xF4;
static constexpr size_t PINMUX_AUX_UART3_TX = 0x104;
static constexpr size_t PINMUX_AUX_WIFI_EN = 0x1B4;
static constexpr size_t PINMUX_AUX_WIFI_RST = 0x1B8;
static constexpr size_t PINMUX_AUX_NFC_EN = 0x1D0;
static constexpr size_t PINMUX_AUX_NFC_INT = 0x1D4;
static constexpr size_t PINMUX_AUX_LCD_BL_PWM = 0x1FC;
static constexpr size_t PINMUX_AUX_LCD_BL_EN = 0x200;
static constexpr size_t PINMUX_AUX_LCD_RST = 0x204;
static constexpr size_t PINMUX_AUX_GPIO_PE6 = 0x248;
static constexpr size_t PINMUX_AUX_GPIO_PH6 = 0x250;
static constexpr size_t PINMUX_AUX_GPIO_PZ1 = 0x280;
static constexpr u32 PINMUX_FUNC_MASK = (3 << 0);
static constexpr u32 PINMUX_PULL_MASK = (3 << 2);
static constexpr u32 PINMUX_PULL_NONE = (0 << 2);
static constexpr u32 PINMUX_PULL_DOWN = (1 << 2);
static constexpr u32 PINMUX_PULL_UP = (2 << 2);
static constexpr u32 PINMUX_TRISTATE = (1 << 4);
static constexpr u32 PINMUX_PARKED = (1 << 5);
static constexpr u32 PINMUX_INPUT_ENABLE = (1 << 6);
static constexpr u32 PINMUX_LOCK = (1 << 7);
static constexpr u32 PINMUX_LPDR = (1 << 8);
static constexpr u32 PINMUX_HSM = (1 << 9);
static constexpr u32 PINMUX_IO_HV = (1 << 10);
static constexpr u32 PINMUX_OPEN_DRAIN = (1 << 11);
static constexpr u32 PINMUX_SCHMT = (1 << 12);
static constexpr u32 PINMUX_DRIVE_1X = (0 << 13) ;
static constexpr u32 PINMUX_DRIVE_2X = (1 << 13);
static constexpr u32 PINMUX_DRIVE_3X = (2 << 13);
static constexpr u32 PINMUX_DRIVE_4X = (3 << 13);
@@ -0,0 +1,81 @@
/*
* 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 "boot_types.hpp"
static constexpr uintptr_t PmcBase = 0x7000E400ul;
static constexpr size_t APBDEV_PMC_CNTRL = 0x0;
static constexpr u32 PMC_CNTRL_MAIN_RST = (1 << 4);
static constexpr size_t APBDEV_PMC_SEC_DISABLE = 0x4;
static constexpr size_t APBDEV_PMC_WAKE_MASK = 0xC;
static constexpr size_t APBDEV_PMC_WAKE_LVL = 0x10;
static constexpr size_t APBDEV_PMC_DPD_PADS_ORIDE = 0x1C;
static constexpr size_t APBDEV_PMC_PWRGATE_TOGGLE = 0x30;
static constexpr size_t APBDEV_PMC_PWRGATE_STATUS = 0x38;
static constexpr size_t APBDEV_PMC_BLINK_TIMER = 0x40;
static constexpr size_t APBDEV_PMC_NO_IOPOWER = 0x44;
static constexpr size_t APBDEV_PMC_SCRATCH0 = 0x50;
static constexpr size_t APBDEV_PMC_SCRATCH1 = 0x54;
static constexpr size_t APBDEV_PMC_SCRATCH20 = 0xA0;
static constexpr size_t APBDEV_PMC_AUTO_WAKE_LVL_MASK = 0xDC;
static constexpr size_t APBDEV_PMC_PWR_DET_VAL = 0xE4;
static constexpr u32 PMC_PWR_DET_SDMMC1_IO_EN = (1 << 12);
static constexpr size_t APBDEV_PMC_DDR_PWR = 0xE8;
static constexpr size_t APBDEV_PMC_CRYPTO_OP = 0xF4;
static constexpr u32 PMC_CRYPTO_OP_SE_ENABLE = 0;
static constexpr u32 PMC_CRYPTO_OP_SE_DISABLE = 1;
static constexpr size_t APBDEV_PMC_SCRATCH33 = 0x120;
static constexpr size_t APBDEV_PMC_SCRATCH40 = 0x13C;
static constexpr size_t APBDEV_PMC_WAKE2_MASK = 0x160;
static constexpr size_t APBDEV_PMC_WAKE2_LVL = 0x164;
static constexpr size_t APBDEV_PMC_AUTO_WAKE2_LVL_MASK = 0x170;
static constexpr size_t APBDEV_PMC_OSC_EDPD_OVER = 0x1A4;
static constexpr size_t APBDEV_PMC_CLK_OUT_CNTRL = 0x1A8;
static constexpr size_t APBDEV_PMC_RST_STATUS = 0x1B4;
static constexpr size_t APBDEV_PMC_IO_DPD_REQ = 0x1B8;
static constexpr size_t APBDEV_PMC_IO_DPD2_REQ = 0x1C0;
static constexpr size_t APBDEV_PMC_VDDP_SEL = 0x1CC;
static constexpr size_t APBDEV_PMC_DDR_CFG = 0x1D0;
static constexpr size_t APBDEV_PMC_SCRATCH45 = 0x234;
static constexpr size_t APBDEV_PMC_SCRATCH46 = 0x238;
static constexpr size_t APBDEV_PMC_SCRATCH49 = 0x244;
static constexpr size_t APBDEV_PMC_TSC_MULT = 0x2B4;
static constexpr size_t APBDEV_PMC_SEC_DISABLE2 = 0x2C4;
static constexpr size_t APBDEV_PMC_WEAK_BIAS = 0x2C8;
static constexpr size_t APBDEV_PMC_REG_SHORT = 0x2CC;
static constexpr size_t APBDEV_PMC_SEC_DISABLE3 = 0x2D8;
static constexpr size_t APBDEV_PMC_SECURE_SCRATCH21 = 0x334;
static constexpr size_t APBDEV_PMC_SECURE_SCRATCH32 = 0x360;
static constexpr size_t APBDEV_PMC_SECURE_SCRATCH49 = 0x3A4;
static constexpr size_t APBDEV_PMC_CNTRL2 = 0x440;
static constexpr size_t APBDEV_PMC_IO_DPD3_REQ = 0x45C;
static constexpr size_t APBDEV_PMC_IO_DPD4_REQ = 0x464;
static constexpr size_t APBDEV_PMC_UTMIP_PAD_CFG1 = 0x4C4;
static constexpr size_t APBDEV_PMC_UTMIP_PAD_CFG3 = 0x4CC;
static constexpr size_t APBDEV_PMC_WAKE_DEBOUNCE_EN = 0x4D8;
static constexpr size_t APBDEV_PMC_DDR_CNTRL = 0x4E4;
static constexpr size_t APBDEV_PMC_SEC_DISABLE4 = 0x5B0;
static constexpr size_t APBDEV_PMC_SEC_DISABLE5 = 0x5B4;
static constexpr size_t APBDEV_PMC_SEC_DISABLE6 = 0x5B8;
static constexpr size_t APBDEV_PMC_SEC_DISABLE7 = 0x5BC;
static constexpr size_t APBDEV_PMC_SEC_DISABLE8 = 0x5C0;
static constexpr size_t APBDEV_PMC_SCRATCH188 = 0x810;
static constexpr size_t APBDEV_PMC_SCRATCH190 = 0x818;
static constexpr size_t APBDEV_PMC_SCRATCH200 = 0x840;
@@ -0,0 +1,31 @@
/*
* 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 "updater/updater_api.hpp"
static u8 __attribute__((__aligned__(0x1000))) g_boot_image_work_buffer[0x10000];
void Boot::CheckAndRepairBootImages() {
const BootImageUpdateType boot_image_update_type = Updater::GetBootImageUpdateType(Boot::GetHardwareType());
bool repaired_normal, repaired_safe;
Result rc = Updater::VerifyBootImagesAndRepairIfNeeded(&repaired_normal, &repaired_safe, g_boot_image_work_buffer, sizeof(g_boot_image_work_buffer), boot_image_update_type);
if (R_SUCCEEDED(rc) && repaired_normal) {
/* Nintendo only performs a reboot on successful normal repair. */
Boot::RebootSystem();
}
}
@@ -0,0 +1,41 @@
/*
* 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 "boot_functions.hpp"
#include "boot_rtc_driver.hpp"
Result RtcDriver::ReadRtcRegister(u8 *out, u8 address) {
const u8 update_addr = 0x04;
const u8 update_val = 0x10;
Result rc = Boot::WriteI2cRegister(this->i2c_session, &update_val, sizeof(update_val), &update_addr, sizeof(update_addr));
if (R_FAILED(rc)) {
return rc;
}
svcSleepThread(16'000'000ul);
return Boot::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));
}
Result RtcDriver::GetRtcIntrM(u8 *out) {
const u8 addr = 0x01;
return this->ReadRtcRegister(out, addr);
}
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c_driver/i2c_api.hpp"
class RtcDriver {
private:
I2cSessionImpl i2c_session;
public:
RtcDriver() {
I2cDriver::Initialize();
I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc);
}
~RtcDriver() {
I2cDriver::CloseSession(this->i2c_session);
I2cDriver::Finalize();
}
private:
Result ReadRtcRegister(u8 *out, u8 address);
public:
Result GetRtcIntr(u8 *out);
Result GetRtcIntrM(u8 *out);
};
@@ -0,0 +1,47 @@
/*
* 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();
}
}
@@ -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/>.
*/
#include "boot_functions.hpp"
#include "boot_splash_screen_notext.hpp"
/* TODO: Compile-time switch for splash_screen_text.hpp? */
void Boot::ShowSplashScreen() {
const u32 boot_reason = Boot::GetBootReason();
if (boot_reason == 1 || boot_reason == 4) {
return;
}
Boot::InitializeDisplay();
{
/* Splash screen is shown for 2 seconds. */
Boot::ShowDisplay(SplashScreenX, SplashScreenY, SplashScreenW, SplashScreenH, SplashScreen);
svcSleepThread(2'000'000'000ul);
}
Boot::FinalizeDisplay();
}
@@ -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 "boot_types.hpp"
static constexpr size_t SplashScreenX = 535;
static constexpr size_t SplashScreenY = 274;
static constexpr size_t SplashScreenW = 210;
static constexpr size_t SplashScreenH = 172;
static constexpr u32 SplashScreen[] = {0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF202020, 0xFF404040, 0xFF666666, 0xFF666666, 0xFF707070, 0xFF666666, 0xFF666666, 0xFF404040, 0xFF333333, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF101010, 0xFF707070, 0xFFAFAFAF, 0xFFEFEFEF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFEFEFEF, 0xFFBFBFBF, 0xFF707070, 0xFF202020, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFLine truncated
static_assert(sizeof(SplashScreen) == sizeof(u32) * SplashScreenW * SplashScreenH, "Incorrect SplashScreen definition!");
@@ -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 "boot_types.hpp"
static constexpr size_t SplashScreenX = 464;
static constexpr size_t SplashScreenY = 274;
static constexpr size_t SplashScreenW = 351;
static constexpr size_t SplashScreenH = 172;
static constexpr u32 SplashScreen[] = {0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF202020, 0xFF202020, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 0xFFLine truncated
static_assert(sizeof(SplashScreen) == sizeof(u32) * SplashScreenW * SplashScreenH, "Incorrect SplashScreen definition!");
+44
View File
@@ -0,0 +1,44 @@
/*
* 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>
enum HardwareType {
HardwareType_Icosa = 0,
HardwareType_Copper = 1,
HardwareType_Hoag = 2,
HardwareType_Iowa = 3,
};
struct GpioInitialConfig {
u32 pad_name;
GpioDirection direction;
GpioValue value;
};
struct PinmuxInitialConfig {
u32 name;
u32 val;
u32 mask;
};
struct WakePinConfig {
u32 index;
bool enabled;
u32 level;
};
@@ -0,0 +1,41 @@
/*
* 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 "boot_types.hpp"
#include "boot_registers_pmc.hpp"
struct WakeControlConfig {
u32 reg_offset;
u32 mask_val;
bool flag_val;
};
static constexpr WakeControlConfig WakeControlConfigs[] = {
{APBDEV_PMC_CNTRL, 0x0800, true},
{APBDEV_PMC_CNTRL, 0x0400, false},
{APBDEV_PMC_CNTRL, 0x0200, true},
{APBDEV_PMC_CNTRL, 0x0100, false},
{APBDEV_PMC_CNTRL, 0x0040, false},
{APBDEV_PMC_CNTRL, 0x0020, false},
{APBDEV_PMC_CNTRL2, 0x4000, true},
{APBDEV_PMC_CNTRL2, 0x0200, false},
{APBDEV_PMC_CNTRL2, 0x0001, true},
};
static constexpr size_t NumWakeControlConfigs = sizeof(WakeControlConfigs) / sizeof(WakeControlConfigs[0]);
@@ -0,0 +1,74 @@
/*
* 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 "boot_types.hpp"
static constexpr WakePinConfig WakePinConfigs[] = {
{0x00, false, 0x02},
{0x01, false, 0x02},
{0x02, false, 0x02},
{0x03, false, 0x02},
{0x04, true, 0x02},
{0x05, false, 0x02},
{0x06, true, 0x02},
{0x07, true, 0x02},
{0x08, false, 0x01},
{0x0A, true, 0x02},
{0x0B, false, 0x02},
{0x0C, false, 0x02},
{0x0D, false, 0x02},
{0x0E, true, 0x00},
{0x0F, false, 0x02},
{0x11, false, 0x02},
{0x12, false, 0x02},
{0x13, false, 0x02},
{0x14, false, 0x02},
{0x15, false, 0x02},
{0x16, false, 0x02},
{0x17, false, 0x02},
{0x18, false, 0x02},
{0x19, false, 0x02},
{0x1A, false, 0x02},
{0x1B, true, 0x00},
{0x1C, false, 0x02},
{0x21, false, 0x02},
{0x22, true, 0x00},
{0x23, true, 0x02},
{0x24, false, 0x02},
{0x2D, false, 0x02},
{0x2E, false, 0x02},
{0x2F, false, 0x02},
{0x30, true, 0x02},
{0x31, false, 0x02},
{0x32, false, 0x02},
{0x33, true, 0x00},
{0x34, true, 0x00},
{0x35, false, 0x02},
{0x36, false, 0x02},
{0x37, false, 0x02},
{0x38, false, 0x02},
{0x39, false, 0x02},
{0x3A, false, 0x02},
{0x3B, false, 0x02},
{0x3D, false, 0x02},
{0x3E, false, 0x02},
{0x3F, false, 0x02},
};
static constexpr size_t NumWakePinConfigs = sizeof(WakePinConfigs) / sizeof(WakePinConfigs[0]);
@@ -0,0 +1,74 @@
/*
* 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 "boot_types.hpp"
static constexpr WakePinConfig WakePinConfigsCopper[] = {
{0x00, true, 0x02},
{0x01, false, 0x02},
{0x02, false, 0x02},
{0x03, true, 0x02},
{0x04, false, 0x02},
{0x05, true, 0x02},
{0x06, false, 0x02},
{0x07, false, 0x02},
{0x08, true, 0x02},
{0x0A, false, 0x02},
{0x0B, false, 0x02},
{0x0C, false, 0x02},
{0x0D, false, 0x02},
{0x0E, true, 0x00},
{0x0F, false, 0x02},
{0x11, false, 0x02},
{0x12, false, 0x02},
{0x13, false, 0x02},
{0x14, false, 0x02},
{0x15, false, 0x02},
{0x16, false, 0x02},
{0x17, false, 0x02},
{0x18, true, 0x02},
{0x19, false, 0x02},
{0x1A, false, 0x02},
{0x1B, false, 0x00},
{0x1C, false, 0x02},
{0x21, false, 0x02},
{0x22, false, 0x00},
{0x23, false, 0x02},
{0x24, false, 0x02},
{0x2D, false, 0x02},
{0x2E, false, 0x02},
{0x2F, true, 0x02},
{0x30, true, 0x02},
{0x31, false, 0x02},
{0x32, true, 0x02},
{0x33, true, 0x00},
{0x34, true, 0x00},
{0x35, false, 0x02},
{0x36, false, 0x02},
{0x37, false, 0x02},
{0x38, false, 0x02},
{0x39, false, 0x02},
{0x3A, false, 0x02},
{0x3B, false, 0x02},
{0x3D, false, 0x02},
{0x3E, false, 0x02},
{0x3F, false, 0x02},
};
static constexpr size_t NumWakePinConfigsCopper = sizeof(WakePinConfigsCopper) / sizeof(WakePinConfigsCopper[0]);
@@ -0,0 +1,88 @@
/*
* 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_registers_pmc.hpp"
#include "boot_wake_control_configs.hpp"
#include "boot_wake_pin_configuration.hpp"
#include "boot_wake_pin_configuration_copper.hpp"
static void UpdatePmcControlBit(const u32 reg_offset, const u32 mask_val, const bool flag) {
Boot::WritePmcRegister(PmcBase + reg_offset, flag ? UINT32_MAX : 0, mask_val);
Boot::ReadPmcRegister(PmcBase + reg_offset);
}
static void InitializePmcWakeConfiguration(const bool is_blink) {
/* Initialize APBDEV_PMC_WAKE_DEBOUNCE_EN, do a dummy read. */
Boot::WritePmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN, 0);
Boot::ReadPmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN);
/* Initialize APBDEV_PMC_BLINK_TIMER, do a dummy read. */
Boot::WritePmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER, 0x8008800);
Boot::ReadPmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER);
/* Set control bits, do dummy reads. */
for (size_t i = 0; i < NumWakeControlConfigs; i++) {
UpdatePmcControlBit(WakeControlConfigs[i].reg_offset, WakeControlConfigs[i].mask_val, WakeControlConfigs[i].flag_val);
}
/* Set bit 0x80 in APBDEV_PMC_CNTRL based on is_blink, do dummy read. */
UpdatePmcControlBit(APBDEV_PMC_CNTRL, 0x80, is_blink);
/* Set bit 0x100000 in APBDEV_PMC_DPD_PADS_ORIDE based on is_blink, do dummy read. */
UpdatePmcControlBit(APBDEV_PMC_DPD_PADS_ORIDE, 0x100000, is_blink);
}
void Boot::SetWakeEventLevel(u32 index, u32 level) {
u32 pmc_wake_level_reg_offset = index <= 0x1F ? APBDEV_PMC_WAKE_LVL : APBDEV_PMC_WAKE2_LVL;
u32 pmc_wake_level_mask_reg_offset = index <= 0x1F ? APBDEV_PMC_AUTO_WAKE_LVL_MASK : APBDEV_PMC_AUTO_WAKE2_LVL_MASK;
if (level != 2) {
std::swap(pmc_wake_level_reg_offset, pmc_wake_level_mask_reg_offset);
}
const u32 mask_val = (1 << (index & 0x1F));
/* Clear level reg bit. */
UpdatePmcControlBit(pmc_wake_level_reg_offset, mask_val, false);
/* Set or clear mask reg bit. */
UpdatePmcControlBit(pmc_wake_level_mask_reg_offset, mask_val, level > 0);
}
void Boot::SetWakeEventEnabled(u32 index, bool enabled) {
/* Set or clear enabled bit. */
UpdatePmcControlBit(index <= 0x1F ? APBDEV_PMC_WAKE_MASK : APBDEV_PMC_WAKE2_MASK, (1 << (index & 0x1F)), enabled);
}
void Boot::SetInitialWakePinConfiguration() {
InitializePmcWakeConfiguration(false);
/* Set wake event levels, wake event enables. */
const WakePinConfig *configs;
size_t num_configs;
if (Boot::GetHardwareType() == HardwareType_Copper) {
configs = WakePinConfigsCopper;
num_configs = NumWakePinConfigsCopper;
} else {
configs = WakePinConfigs;
num_configs = NumWakePinConfigs;
}
for (size_t i = 0; i < num_configs; i++) {
Boot::SetWakeEventLevel(configs[i].index, configs[i].level);
Boot::SetWakeEventEnabled(configs[i].index, configs[i].enabled);
}
}
@@ -0,0 +1,90 @@
/*
* 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;
}
@@ -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>
/* pcv isn't alive at the time boot runs, but nn::i2c::driver needs nn::pcv. */
/* These are the overrides N puts in 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);
};
@@ -0,0 +1,186 @@
/*
* 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)++;
Result rc = I2cDriver::Send(session, *cur_cmd, num_bytes, option);
if (R_FAILED(rc)) {
return rc;
}
(*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)++;
Result rc = I2cDriver::Receive(session, *cur_dst, num_bytes, option);
if (R_FAILED(rc)) {
return rc;
}
(*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();
}
Result rc = g_cmd_handlers[cmd](&cur_cmd, &cur_dst, session);
if (R_FAILED(rc)) {
return rc;
}
}
return ResultSuccess;
}
void I2cDriver::SuspendBuses() {
GetResourceManager().SuspendBuses();
}
void I2cDriver::ResumeBuses() {
GetResourceManager().ResumeBuses();
}
void I2cDriver::SuspendPowerBus() {
GetResourceManager().SuspendPowerBus();
}
void I2cDriver::ResumePowerBus() {
GetResourceManager().ResumePowerBus();
}
@@ -0,0 +1,38 @@
/*
* 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();
};
@@ -0,0 +1,507 @@
/*
* 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;
Result rc;
/* 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;
}
if (R_FAILED((rc = this->GetAndHandleTransactionResult()))) {
return rc;
}
}
WriteRegister(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8C);
/* Wait for successful completion. */
while (true) {
if (R_FAILED((rc = this->GetAndHandleTransactionResult()))) {
return rc;
}
/* Check PACKET_XFER_COMPLETE */
const u32 interrupt_status = ReadRegister(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0);
if (interrupt_status & 0x80) {
if (R_FAILED((rc = this->GetAndHandleTransactionResult()))) {
return rc;
}
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;
Result rc;
/* 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;
}
if (R_FAILED((rc = this->GetAndHandleTransactionResult()))) {
return rc;
}
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() {
Result rc = this->GetTransactionResult();
this->HandleTransactionResult(rc);
if (R_FAILED(rc)) {
this->ClearInterruptMask();
eventClear(&this->interrupt_event);
return rc;
}
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);
}
@@ -0,0 +1,82 @@
/*
* 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;
};
@@ -0,0 +1,74 @@
/*
* 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) {
Result rc = this->CanEnqueue(SendCommandSize + size);
if (R_FAILED(rc)) { return rc; }
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) {
Result rc = this->CanEnqueue(ReceiveCommandSize);
if (R_FAILED(rc)) { return rc; }
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) {
Result rc = this->CanEnqueue(SleepCommandSize);
if (R_FAILED(rc)) { return rc; }
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;
}
@@ -0,0 +1,53 @@
/*
* 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);
};
@@ -0,0 +1,117 @@
/*
* 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;
}
@@ -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/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include "i2c_driver_session.hpp"
void I2cDriverSession::Open(I2cBus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, I2cBusAccessor *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 I2cDriverSession::Start(){
std::scoped_lock<HosMutex> lk(this->bus_accessor_mutex);
if (this->open) {
if (this->bus_accessor->GetOpenSessions() == 1) {
this->bus_accessor->DoInitialConfig();
}
}
}
void I2cDriverSession::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 I2cDriverSession::IsOpen() const{
return this->open;
}
Result I2cDriverSession::DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, DriverCommand command){
std::scoped_lock<HosMutex> lk(this->bus_accessor_mutex);
Result rc;
if (this->bus_accessor->GetBusy()) {
return ResultI2cBusBusy;
}
this->bus_accessor->OnStartTransaction();
if (R_SUCCEEDED((rc = this->bus_accessor->StartTransaction(command, this->addressing_mode, this->slave_address)))) {
switch (command) {
case DriverCommand_Send:
rc = this->bus_accessor->Send(reinterpret_cast<const u8 *>(src), num_bytes, option, this->addressing_mode, this->slave_address);
break;
case DriverCommand_Receive:
rc = this->bus_accessor->Receive(reinterpret_cast<u8 *>(dst), num_bytes, option, this->addressing_mode, this->slave_address);
break;
default:
std::abort();
}
}
this->bus_accessor->OnStopTransaction();
return rc;
}
Result I2cDriverSession::DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, DriverCommand command){
Result rc;
size_t i = 0;
while (true) {
rc = this->DoTransaction(dst, src, num_bytes, option, command);
if (rc == ResultI2cTimedOut) {
i++;
if (i <= this->max_retries) {
svcSleepThread(this->retry_wait_time);
continue;
}
return ResultI2cBusBusy;
} else if (R_FAILED(rc)) {
return rc;
}
return ResultSuccess;
}
return rc;
}
@@ -0,0 +1,47 @@
/*
* 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_bus_accessor.hpp"
class I2cDriverSession {
private:
HosMutex bus_accessor_mutex;
I2cBusAccessor *bus_accessor = nullptr;
I2cBus bus = I2cBus_I2c1;
u32 slave_address = 0;
AddressingMode addressing_mode = AddressingMode_7Bit;
u32 max_retries = 0;
u64 retry_wait_time = 0;
bool open = false;
public:
I2cDriverSession() {
/* ... */
}
public:
void Open(I2cBus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, I2cBusAccessor *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, DriverCommand command);
Result DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, DriverCommand command);
};
@@ -0,0 +1,141 @@
/*
* 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"
static inline uintptr_t GetIoMapping(u64 io_addr, u64 io_size) {
u64 vaddr;
u64 aligned_addr = (io_addr & ~0xFFFul);
u64 aligned_size = io_size + (io_addr - aligned_addr);
if (R_FAILED(svcQueryIoMapping(&vaddr, aligned_addr, aligned_size))) {
std::abort();
}
return static_cast<uintptr_t>(vaddr + (io_addr - aligned_addr));
}
struct I2cRegisters {
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;
};
static inline I2cRegisters *GetI2cRegisters(I2cBus bus) {
static constexpr uintptr_t s_offsets[] = {
0x0000, 0x0400, 0x0500, 0x0700, 0x1000, 0x1100
};
if (bus >= sizeof(s_offsets) / sizeof(s_offsets[0])) {
std::abort();
}
const uintptr_t registers = GetIoMapping(0x7000c000ul, 0x2000) + s_offsets[static_cast<size_t>(bus)];
return reinterpret_cast<I2cRegisters *>(registers);
}
struct ClkRstRegisters {
public:
volatile u32 *clk_src_reg;
volatile u32 *clk_en_reg;
volatile u32 *rst_reg;
u32 mask;
public:
void SetBus(I2cBus bus) {
static constexpr uintptr_t s_clk_src_offsets[] = {
0x124, 0x198, 0x1b8, 0x3c4, 0x128, 0x65c
};
static constexpr uintptr_t s_clk_en_offsets[] = {
0x010, 0x014, 0x018, 0x360, 0x014, 0x280
};
static constexpr uintptr_t s_rst_offsets[] = {
0x004, 0x008, 0x00c, 0x358, 0x008, 0x28c
};
static constexpr size_t s_bit_shifts[] = {
12, 22, 3, 7, 15, 6
};
if (bus >= sizeof(s_clk_src_offsets) / sizeof(s_clk_src_offsets[0])) {
std::abort();
}
const uintptr_t registers = GetIoMapping(0x60006000ul, 0x1000);
const size_t idx = static_cast<size_t>(bus);
this->clk_src_reg = reinterpret_cast<volatile u32 *>(registers + s_clk_src_offsets[idx]);
this->clk_en_reg = reinterpret_cast<volatile u32 *>(registers + s_clk_en_offsets[idx]);
this->rst_reg = reinterpret_cast<volatile u32 *>(registers + s_rst_offsets[idx]);
this->mask = (1u << s_bit_shifts[idx]);
}
};
static inline void WriteRegister(volatile u32 *reg, u32 val) {
*reg = val;
}
static inline u32 ReadRegister(volatile u32 *reg) {
u32 val = *reg;
return val;
}
static inline void SetRegisterBits(volatile u32 *reg, u32 mask) {
*reg |= mask;
}
static inline void ClearRegisterBits(volatile u32 *reg, u32 mask) {
*reg &= mask;
}
static inline void ReadWriteRegisterBits(volatile u32 *reg, u32 val, u32 mask) {
*reg = (*reg & (~mask)) | (val & mask);
}
@@ -0,0 +1,203 @@
/*
* 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 "boot_pcv.hpp"
#include "i2c_resource_manager.hpp"
void I2cResourceManager::Initialize() {
std::scoped_lock<HosMutex> lk(this->initialize_mutex);
this->ref_cnt++;
}
void I2cResourceManager::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 I2cResourceManager::GetFreeSessionId() const {
for (size_t i = 0; i < MaxDriverSessions; i++) {
if (!this->sessions[i].IsOpen()) {
return i;
}
}
return InvalidSessionId;
}
void I2cResourceManager::OpenSession(I2cSessionImpl *out_session, I2cBus 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 >= MaxBuses) {
std::abort();
}
session_id = GetFreeSessionId();
if (session_id == InvalidSessionId) {
std::abort();
}
if ((bus == I2cBus_I2c2 || bus == I2cBus_I2c3) && (this->bus_accessors[I2cBus_I2c2].GetOpenSessions() == 0 && this->bus_accessors[I2cBus_I2c3].GetOpenSessions() == 0)) {
need_enable_ldo6 = true;
}
out_session->session_id = session_id;
out_session->bus = bus;
this->sessions[session_id].Open(bus, slave_address, addressing_mode, speed_mode, &this->bus_accessors[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 I2cResourceManager::CloseSession(const I2cSessionImpl &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 ((session.bus == I2cBus_I2c2 || session.bus == I2cBus_I2c3) && (this->bus_accessors[I2cBus_I2c2].GetOpenSessions() == 0 && this->bus_accessors[I2cBus_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 I2cResourceManager::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 < MaxBuses; 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 I2cResourceManager::ResumeBuses() {
if (this->ref_cnt == 0) {
std::abort();
}
if (this->suspended) {
if (this->bus_accessors[I2cBus_I2c2].GetOpenSessions() > 0 || this->bus_accessors[I2cBus_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 < MaxBuses; i++) {
if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) {
this->bus_accessors[i].Resume();
}
}
}
this->suspended = false;
}
}
void I2cResourceManager::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 I2cResourceManager::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_types.hpp"
#include "i2c_bus_accessor.hpp"
#include "i2c_driver_session.hpp"
class I2cResourceManager {
public:
static constexpr size_t MaxDriverSessions = 40;
static constexpr size_t MaxBuses = 6;
static constexpr size_t PowerBusId = static_cast<size_t>(I2cBus_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;
I2cDriverSession sessions[MaxDriverSessions];
I2cBusAccessor bus_accessors[MaxBuses];
HosMutex transaction_mutexes[MaxBuses];
public:
I2cResourceManager() {
/* ... */
}
private:
size_t GetFreeSessionId() const;
public:
/* N uses a singleton here, we'll oblige. */
static I2cResourceManager &GetInstance() {
static I2cResourceManager s_instance;
return s_instance;
}
bool IsInitialized() const {
return this->ref_cnt > 0;
}
I2cDriverSession& GetSession(size_t id) {
return this->sessions[id];
}
HosMutex& GetTransactionMutex(I2cBus bus) {
if (bus >= MaxBuses) {
std::abort();
}
return this->transaction_mutexes[bus];
}
void Initialize();
void Finalize();
void OpenSession(I2cSessionImpl *out_session, I2cBus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time);
void CloseSession(const I2cSessionImpl &session);
void SuspendBuses();
void ResumeBuses();
void SuspendPowerBus();
void ResumePowerBus();
};
@@ -0,0 +1,69 @@
/*
* 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>
enum AddressingMode {
AddressingMode_7Bit = 0,
};
enum SpeedMode {
SpeedMode_Normal = 100000,
SpeedMode_Fast = 400000,
SpeedMode_FastPlus = 1000000,
SpeedMode_HighSpeed = 3400000,
};
enum I2cBus {
I2cBus_I2c1 = 0,
I2cBus_I2c2 = 1,
I2cBus_I2c3 = 2,
I2cBus_I2c4 = 3,
I2cBus_I2c5 = 4,
I2cBus_I2c6 = 5,
};
enum DriverCommand {
DriverCommand_Send = 0,
DriverCommand_Receive = 1,
};
struct I2cSessionImpl {
I2cBus bus;
size_t session_id;
};
enum I2cCommand {
I2cCommand_Send = 0,
I2cCommand_Receive = 1,
I2cCommand_SubCommand = 2,
I2cCommand_Count,
};
enum I2cSubCommand {
I2cSubCommand_Sleep = 0,
I2cSubCommand_Count,
};
bool IsI2cDeviceSupported(I2cDevice dev);
I2cBus GetI2cDeviceBus(I2cDevice dev);
u32 GetI2cDeviceSlaveAddress(I2cDevice dev);
AddressingMode GetI2cDeviceAddressingMode(I2cDevice dev);
SpeedMode GetI2cDeviceSpeedMode(I2cDevice dev);
u32 GetI2cDeviceMaxRetries(I2cDevice dev);
u64 GetI2cDeviceRetryWaitTime(I2cDevice dev);
@@ -0,0 +1,720 @@
/*
* 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 "updater_api.hpp"
#include "updater_bis_save.hpp"
Result Updater::ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size) {
if (work_buffer_size < BctSize + EksSize) {
return ResultUpdaterTooSmallWorkBuffer;
}
if (reinterpret_cast<uintptr_t>(work_buffer) & 0xFFF) {
return ResultUpdaterMisalignedWorkBuffer;
}
if (reinterpret_cast<uintptr_t>(work_buffer_size) & 0x1FF) {
return ResultUpdaterMisalignedWorkBuffer;
}
return ResultSuccess;
}
BootImageUpdateType Updater::GetBootImageUpdateType(HardwareType hw_type) {
switch (hw_type) {
case HardwareType_Icosa:
case HardwareType_Copper:
return BootImageUpdateType_Erista;
case HardwareType_Hoag:
case HardwareType_Iowa:
return BootImageUpdateType_Mariko;
default:
std::abort();
}
}
bool Updater::HasEks(BootImageUpdateType boot_image_update_type) {
switch (boot_image_update_type) {
case BootImageUpdateType_Erista:
return true;
case BootImageUpdateType_Mariko:
return false;
default:
std::abort();
}
}
bool Updater::HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type) {
switch (boot_image_update_type) {
case BootImageUpdateType_Erista:
return true;
case BootImageUpdateType_Mariko:
return false;
default:
std::abort();
}
}
u32 Updater::GetNcmTitleType(BootModeType mode) {
switch (mode) {
case BootModeType_Normal:
return NcmContentMetaType_BootImagePackage;
case BootModeType_Safe:
return NcmContentMetaType_BootImagePackageSafe;
default:
std::abort();
}
}
Result Updater::GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size) {
Result rc;
/* Always set output to true before doing anything else. */
out->needs_verify_normal = true;
out->needs_verify_safe = true;
/* Ensure work buffer is big enough for us to do what we want to do. */
if (R_FAILED((rc = ValidateWorkBuffer(work_buffer, work_buffer_size)))) {
return rc;
}
/* Initialize boot0 save accessor. */
BisSave save;
if (R_FAILED((rc = save.Initialize(work_buffer, work_buffer_size)))) {
return rc;
}
ON_SCOPE_EXIT { save.Finalize(); };
/* Load save from NAND. */
if (R_FAILED((rc = save.Load()))) {
return rc;
}
/* Read data from save. */
out->needs_verify_normal = save.GetNeedsVerification(BootModeType_Normal);
out->needs_verify_safe = save.GetNeedsVerification(BootModeType_Safe);
return ResultSuccess;
}
Result Updater::VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
Result rc;
/* Always set output to false before doing anything else. */
*out_repaired_normal = false;
*out_repaired_safe = false;
/* Ensure work buffer is big enough for us to do what we want to do. */
if (R_FAILED((rc = ValidateWorkBuffer(work_buffer, work_buffer_size)))) {
return rc;
}
/* Get verification state from NAND. */
VerificationState verification_state;
if (R_FAILED((rc = GetVerificationState(&verification_state, work_buffer, work_buffer_size)))) {
return rc;
}
/* If we don't need to verify anything, we're done. */
if (!verification_state.needs_verify_normal && !verification_state.needs_verify_safe) {
return ResultSuccess;
}
/* Get a session to ncm. */
DoWithSmSession([&]() {
if (R_FAILED(ncmInitialize())) {
std::abort();
}
});
ON_SCOPE_EXIT { ncmExit(); };
/* Verify normal, verify safe as needed. */
if (verification_state.needs_verify_normal) {
rc = VerifyBootImagesAndRepairIfNeeded(out_repaired_normal, BootModeType_Normal, work_buffer, work_buffer_size, boot_image_update_type);
if (rc == ResultUpdaterBootImagePackageNotFound) {
/* Nintendo considers failure to locate bip a success. TODO: don't do that? */
rc = ResultSuccess;
}
if (R_FAILED(rc)) {
return rc;
}
}
if (verification_state.needs_verify_safe) {
rc = VerifyBootImagesAndRepairIfNeeded(out_repaired_safe, BootModeType_Safe, work_buffer, work_buffer_size, boot_image_update_type);
if (rc == ResultUpdaterBootImagePackageNotFound) {
/* Nintendo considers failure to locate bip a success. TODO: don't do that? */
rc = ResultSuccess;
}
if (R_FAILED(rc)) {
return rc;
}
}
return ResultSuccess;
}
Result Updater::VerifyBootImagesAndRepairIfNeeded(bool *out_repaired, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
Result rc;
/* Get system data id for boot images (819/81A/81B/81C). */
u64 bip_data_id;
if (R_FAILED((rc = GetBootImagePackageDataId(&bip_data_id, mode, work_buffer, work_buffer_size)))) {
return rc;
}
/* Verify the boot images in NAND. */
if (R_FAILED((rc = VerifyBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type)))) {
/* If we failed for a reason other than repair needed, bail out. */
if (rc != ResultUpdaterNeedsRepairBootImages) {
return rc;
}
/* Perform repair. */
*out_repaired = true;
if (R_FAILED((rc = UpdateBootImages(bip_data_id, mode, work_buffer, work_buffer_size, boot_image_update_type)))) {
return rc;
}
}
/* We've either just verified or just repaired. Either way, we don't need to verify any more. */
return SetVerificationNeeded(mode, false, work_buffer, work_buffer_size);
}
Result Updater::GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size) {
Result rc;
/* Ensure we can read content metas. */
constexpr size_t MaxContentMetas = 0x40;
if (work_buffer_size < sizeof(NcmMetaRecord) * MaxContentMetas) {
std::abort();
}
/* Open NAND System meta database, list contents. */
NcmContentMetaDatabase meta_db;
if (R_FAILED((rc = ncmOpenContentMetaDatabase(FsStorageId_NandSystem, &meta_db)))) {
return rc;
}
ON_SCOPE_EXIT { serviceClose(&meta_db.s); };
NcmMetaRecord *records = reinterpret_cast<NcmMetaRecord *>(work_buffer);
const u32 title_type = GetNcmTitleType(mode);
u32 written_entries;
u32 total_entries;
if (R_FAILED((rc = ncmContentMetaDatabaseList(&meta_db, title_type, 0, 0, UINT64_MAX, records, MaxContentMetas * sizeof(*records), &written_entries, &total_entries)))) {
return rc;
}
if (total_entries == 0) {
return ResultUpdaterBootImagePackageNotFound;
}
if (total_entries != written_entries) {
std::abort();
}
/* Output is sorted, return the lowest valid exfat entry. */
if (total_entries > 1) {
for (size_t i = 0; i < total_entries; i++) {
u8 attr;
if (R_FAILED((rc = ncmContentMetaDatabaseGetAttributes(&meta_db, &records[i], &attr)))) {
return rc;
}
if (attr & NcmContentMetaAttribute_Exfat) {
*out_data_id = records[i].titleId;
return ResultSuccess;
}
}
}
/* If there's only one entry or no exfat entries, return that entry. */
*out_data_id = records[0].titleId;
return ResultSuccess;
}
Result Updater::VerifyBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
switch (mode) {
case BootModeType_Normal:
return VerifyBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type);
case BootModeType_Safe:
return VerifyBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type);
default:
std::abort();
}
}
Result Updater::ValidateBctFileHash(Boot0Accessor &accessor, Boot0Partition which, const void *stored_hash, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
Result rc;
/* Ensure work buffer is big enough for us to do what we want to do. */
if (R_FAILED((rc = ValidateWorkBuffer(work_buffer, work_buffer_size)))) {
return rc;
}
void *bct = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(work_buffer) + 0);
void *work = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(work_buffer) + BctSize);
size_t size;
if (R_FAILED((rc = ReadFile(&size, bct, BctSize, GetBctPath(boot_image_update_type))))) {
return rc;
}
if (HasEks(boot_image_update_type)) {
if (R_FAILED((rc = accessor.UpdateEks(bct, work)))) {
return rc;
}
}
if (HasAutoRcmPreserve(boot_image_update_type)) {
if (R_FAILED((rc = accessor.PreserveAutoRcm(bct, work, which)))) {
return rc;
}
}
u8 file_hash[SHA256_HASH_SIZE];
sha256CalculateHash(file_hash, bct, BctSize);
if (std::memcmp(file_hash, stored_hash, SHA256_HASH_SIZE) == 0) {
return ResultSuccess;
}
return ResultUpdaterNeedsRepairBootImages;
}
Result Updater::VerifyBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
Result rc;
/* Ensure work buffer is big enough for us to do what we want to do. */
if (R_FAILED((rc = ValidateWorkBuffer(work_buffer, work_buffer_size)))) {
return rc;
}
if (R_FAILED((rc = romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())))) {
if (rc == ResultFsTargetNotFound) {
return ResultUpdaterBootImagePackageNotFound;
}
return rc;
}
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
/* Read and validate hashes of boot images. */
{
size_t size;
u8 nand_hash[SHA256_HASH_SIZE];
u8 file_hash[SHA256_HASH_SIZE];
Boot0Accessor boot0_accessor;
if (R_FAILED((rc = boot0_accessor.Initialize()))) {
return rc;
}
ON_SCOPE_EXIT { boot0_accessor.Finalize(); };
/* Compare BCT hashes. */
if (R_FAILED((rc = boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalMain)))) {
return rc;
}
if (R_FAILED((rc = ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)))) {
return rc;
}
/* Compare BCT Sub hashes. */
if (R_FAILED((rc = boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalSub)))) {
return rc;
}
if (R_FAILED((rc = ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalSub, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)))) {
return rc;
}
/* Compare Package1 Normal/Sub hashes. */
if (R_FAILED((rc = GetFileHash(&size, file_hash, GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)))) {
return rc;
}
if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) {
return ResultUpdaterNeedsRepairBootImages;
}
if (R_FAILED((rc = boot0_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)))) {
return rc;
}
if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) {
return ResultUpdaterNeedsRepairBootImages;
}
/* Compare Package2 Normal/Sub hashes. */
if (R_FAILED((rc = GetFileHash(&size, file_hash, GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size)))) {
return rc;
}
if (R_FAILED((rc = GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::NormalMain)))) {
return rc;
}
if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) {
return ResultUpdaterNeedsRepairBootImages;
}
if (R_FAILED((rc = GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::NormalSub)))) {
return rc;
}
if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) {
return ResultUpdaterNeedsRepairBootImages;
}
}
return ResultSuccess;
}
Result Updater::VerifyBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
Result rc;
/* Ensure work buffer is big enough for us to do what we want to do. */
if (R_FAILED((rc = ValidateWorkBuffer(work_buffer, work_buffer_size)))) {
return rc;
}
if (R_FAILED((rc = romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())))) {
if (rc == ResultFsTargetNotFound) {
return ResultUpdaterBootImagePackageNotFound;
}
return rc;
}
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
/* Read and validate hashes of boot images. */
{
size_t size;
u8 nand_hash[SHA256_HASH_SIZE];
u8 file_hash[SHA256_HASH_SIZE];
Boot0Accessor boot0_accessor;
if (R_FAILED((rc = boot0_accessor.Initialize()))) {
return rc;
}
ON_SCOPE_EXIT { boot0_accessor.Finalize(); };
Boot1Accessor boot1_accessor;
if (R_FAILED((rc = boot1_accessor.Initialize()))) {
return rc;
}
ON_SCOPE_EXIT { boot1_accessor.Finalize(); };
/* Compare BCT hashes. */
if (R_FAILED((rc = boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeMain)))) {
return rc;
}
if (R_FAILED((rc = ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)))) {
return rc;
}
/* Compare BCT Sub hashes. */
if (R_FAILED((rc = boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeSub)))) {
return rc;
}
if (R_FAILED((rc = ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeSub, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)))) {
return rc;
}
/* Compare Package1 Normal/Sub hashes. */
if (R_FAILED((rc = GetFileHash(&size, file_hash, GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size)))) {
return rc;
}
if (R_FAILED((rc = boot1_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)))) {
return rc;
}
if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) {
return ResultUpdaterNeedsRepairBootImages;
}
if (R_FAILED((rc = boot1_accessor.GetHash(nand_hash, size, work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)))) {
return rc;
}
if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) {
return ResultUpdaterNeedsRepairBootImages;
}
/* Compare Package2 Normal/Sub hashes. */
if (R_FAILED((rc = GetFileHash(&size, file_hash, GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size)))) {
return rc;
}
if (R_FAILED((rc = GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::SafeMain)))) {
return rc;
}
if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) {
return ResultUpdaterNeedsRepairBootImages;
}
if (R_FAILED((rc = GetPackage2Hash(nand_hash, size, work_buffer, work_buffer_size, Package2Type::SafeSub)))) {
return rc;
}
if (std::memcmp(file_hash, nand_hash, SHA256_HASH_SIZE) != 0) {
return ResultUpdaterNeedsRepairBootImages;
}
}
return ResultSuccess;
}
Result Updater::UpdateBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
switch (mode) {
case BootModeType_Normal:
return UpdateBootImagesNormal(data_id, work_buffer, work_buffer_size, boot_image_update_type);
case BootModeType_Safe:
return UpdateBootImagesSafe(data_id, work_buffer, work_buffer_size, boot_image_update_type);
default:
std::abort();
}
}
Result Updater::UpdateBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
Result rc;
/* Ensure work buffer is big enough for us to do what we want to do. */
if (R_FAILED((rc = ValidateWorkBuffer(work_buffer, work_buffer_size)))) {
return rc;
}
if (R_FAILED((rc = romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())))) {
if (rc == ResultFsTargetNotFound) {
return ResultUpdaterBootImagePackageNotFound;
}
return rc;
}
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
{
Boot0Accessor boot0_accessor;
if (R_FAILED((rc = boot0_accessor.Initialize()))) {
return rc;
}
ON_SCOPE_EXIT { boot0_accessor.Finalize(); };
/* Write Package1 sub. */
if (R_FAILED((rc = boot0_accessor.Clear(work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)))) {
return rc;
}
/* Write Package2 sub. */
if (R_FAILED((rc = WritePackage2(work_buffer, work_buffer_size, Package2Type::NormalSub, boot_image_update_type)))) {
return rc;
}
/* Write BCT sub + BCT main, in that order. */
{
void *bct = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(work_buffer) + 0);
void *work = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(work_buffer) + BctSize);
size_t size;
if (R_FAILED((rc = ReadFile(&size, bct, BctSize, GetBctPath(boot_image_update_type))))) {
return rc;
}
if (HasEks(boot_image_update_type)) {
if (R_FAILED((rc = boot0_accessor.UpdateEks(bct, work)))) {
return rc;
}
}
/* Only preserve autorcm if on a unit with unpatched rcm bug. */
if (HasAutoRcmPreserve(boot_image_update_type) && !IsRcmBugPatched()) {
if (R_FAILED((rc = boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalSub)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalMain)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)))) {
return rc;
}
} else {
if (R_FAILED((rc = boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)))) {
return rc;
}
}
}
/* Write Package2 main. */
if (R_FAILED((rc = WritePackage2(work_buffer, work_buffer_size, Package2Type::NormalMain, boot_image_update_type)))) {
return rc;
}
/* Write Package1 main. */
if (R_FAILED((rc = boot0_accessor.Clear(work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot0Partition::Package1NormalMain)))) {
return rc;
}
}
return ResultSuccess;
}
Result Updater::UpdateBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type) {
Result rc;
/* Ensure work buffer is big enough for us to do what we want to do. */
if (R_FAILED((rc = ValidateWorkBuffer(work_buffer, work_buffer_size)))) {
return rc;
}
if (R_FAILED((rc = romfsMountFromDataArchive(data_id, FsStorageId_NandSystem, GetBootImagePackageMountPath())))) {
if (rc == ResultFsTargetNotFound) {
return ResultUpdaterBootImagePackageNotFound;
}
return rc;
}
ON_SCOPE_EXIT { if (R_FAILED(romfsUnmount(GetBootImagePackageMountPath()))) { std::abort(); } };
{
Boot0Accessor boot0_accessor;
if (R_FAILED((rc = boot0_accessor.Initialize()))) {
return rc;
}
ON_SCOPE_EXIT { boot0_accessor.Finalize(); };
Boot1Accessor boot1_accessor;
if (R_FAILED((rc = boot1_accessor.Initialize()))) {
return rc;
}
ON_SCOPE_EXIT { boot1_accessor.Finalize(); };
/* Write Package1 sub. */
if (R_FAILED((rc = boot1_accessor.Clear(work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)))) {
return rc;
}
if (R_FAILED((rc = boot1_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)))) {
return rc;
}
/* Write Package2 sub. */
if (R_FAILED((rc = WritePackage2(work_buffer, work_buffer_size, Package2Type::SafeSub, boot_image_update_type)))) {
return rc;
}
/* Write BCT sub + BCT main, in that order. */
{
void *bct = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(work_buffer) + 0);
void *work = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(work_buffer) + BctSize);
size_t size;
if (R_FAILED((rc = ReadFile(&size, bct, BctSize, GetBctPath(boot_image_update_type))))) {
return rc;
}
if (HasEks(boot_image_update_type)) {
if (R_FAILED((rc = boot0_accessor.UpdateEks(bct, work)))) {
return rc;
}
}
/* Only preserve autorcm if on a unit with unpatched rcm bug. */
if (HasAutoRcmPreserve(boot_image_update_type) && !IsRcmBugPatched()) {
if (R_FAILED((rc = boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeSub)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeMain)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)))) {
return rc;
}
} else {
if (R_FAILED((rc = boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)))) {
return rc;
}
if (R_FAILED((rc = boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)))) {
return rc;
}
}
}
/* Write Package2 main. */
if (R_FAILED((rc = WritePackage2(work_buffer, work_buffer_size, Package2Type::SafeMain, boot_image_update_type)))) {
return rc;
}
/* Write Package1 main. */
if (R_FAILED((rc = boot1_accessor.Clear(work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)))) {
return rc;
}
if (R_FAILED((rc = boot1_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot1Partition::Package1SafeMain)))) {
return rc;
}
}
return ResultSuccess;
}
Result Updater::SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size) {
Result rc;
/* Ensure work buffer is big enough for us to do what we want to do. */
if (R_FAILED((rc = ValidateWorkBuffer(work_buffer, work_buffer_size)))) {
return rc;
}
/* Initialize boot0 save accessor. */
BisSave save;
if (R_FAILED((rc = save.Initialize(work_buffer, work_buffer_size)))) {
return rc;
}
ON_SCOPE_EXIT { save.Finalize(); };
/* Load save from NAND. */
if (R_FAILED((rc = save.Load()))) {
return rc;
}
/* Set whether we need to verify, then save to nand. */
save.SetNeedsVerification(mode, needed);
if (R_FAILED((rc = save.Save()))) {
return rc;
}
return ResultSuccess;
}
Result Updater::GetPackage2Hash(void *dst_hash, size_t package2_size, void *work_buffer, size_t work_buffer_size, Package2Type which) {
Result rc;
Package2Accessor accessor(which);
if (R_FAILED((rc = accessor.Initialize()))) {
return rc;
}
ON_SCOPE_EXIT { accessor.Finalize(); };
return accessor.GetHash(dst_hash, package2_size, work_buffer, work_buffer_size, Package2Partition::Package2);
}
Result Updater::WritePackage2(void *work_buffer, size_t work_buffer_size, Package2Type which, BootImageUpdateType boot_image_update_type) {
Result rc;
Package2Accessor accessor(which);
if (R_FAILED((rc = accessor.Initialize()))) {
return rc;
}
ON_SCOPE_EXIT { accessor.Finalize(); };
return accessor.Write(GetPackage2Path(boot_image_update_type), work_buffer, work_buffer_size, Package2Partition::Package2);
}
@@ -0,0 +1,62 @@
/*
* 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 "updater_types.hpp"
class Boot0Accessor;
enum class Boot0Partition;
enum class Package2Type;
class Updater {
private:
static bool HasEks(BootImageUpdateType boot_image_update_type);
static bool HasAutoRcmPreserve(BootImageUpdateType boot_image_update_type);
static u32 GetNcmTitleType(BootModeType mode);
static Result ValidateWorkBuffer(const void *work_buffer, size_t work_buffer_size);
static Result GetVerificationState(VerificationState *out, void *work_buffer, size_t work_buffer_size);
static Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
static Result GetBootImagePackageDataId(u64 *out_data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size);
static Result VerifyBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
static Result VerifyBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
static Result VerifyBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
static Result UpdateBootImages(u64 data_id, BootModeType mode, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
static Result UpdateBootImagesNormal(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
static Result UpdateBootImagesSafe(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
static Result SetVerificationNeeded(BootModeType mode, bool needed, void *work_buffer, size_t work_buffer_size);
static Result RepairBootImages(u64 data_id, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
/* Path functionality. */
static const char *GetBootImagePackageMountPath();
static const char *GetBctPath(BootImageUpdateType boot_image_update_type);
static const char *GetPackage1Path(BootImageUpdateType boot_image_update_type);
static const char *GetPackage2Path(BootImageUpdateType boot_image_update_type);
/* File helpers. */
static Result ReadFile(size_t *out_size, void *dst, size_t dst_size, const char *path);
static Result GetFileHash(size_t *out_size, void *dst_hash, const char *path, void *work_buffer, size_t work_buffer_size);
/* Package helpers. */
static Result ValidateBctFileHash(Boot0Accessor &accessor, Boot0Partition which, const void *stored_hash, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
static Result GetPackage2Hash(void *dst_hash, size_t package2_size, void *work_buffer, size_t work_buffer_size, Package2Type which);
static Result WritePackage2(void *work_buffer, size_t work_buffer_size, Package2Type which, BootImageUpdateType boot_image_update_type);
public:
static BootImageUpdateType GetBootImageUpdateType(HardwareType hw_type);
static Result VerifyBootImagesAndRepairIfNeeded(bool *out_repaired_normal, bool *out_repaired_safe, void *work_buffer, size_t work_buffer_size, BootImageUpdateType boot_image_update_type);
};
@@ -0,0 +1,185 @@
/*
* 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 "updater_bis_management.hpp"
Result BisAccessor::Initialize() {
Result rc = fsOpenBisStorage(&this->storage, this->partition_id);
if (R_SUCCEEDED(rc)) {
this->active = true;
}
return rc;
}
void BisAccessor::Finalize() {
if (this->active) {
fsStorageClose(&this->storage);
this->active = false;
}
}
Result BisAccessor::Read(void *dst, size_t size, u64 offset) {
if (offset % SectorAlignment) {
std::abort();
}
return fsStorageRead(&this->storage, offset, dst, size);
}
Result BisAccessor::Write(u64 offset, const void *src, size_t size) {
if (offset % SectorAlignment) {
std::abort();
}
return fsStorageWrite(&this->storage, offset, src, size);
}
Result BisAccessor::Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size) {
Result rc;
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
std::abort();
}
FILE *bip_fp = fopen(bip_path, "rb");
if (bip_fp == NULL) {
return ResultUpdaterInvalidBootImagePackage;
}
ON_SCOPE_EXIT { fclose(bip_fp); };
size_t written = 0;
while (true) {
std::memset(work_buffer, 0, work_buffer_size);
size_t read_size = fread(work_buffer, 1, work_buffer_size, bip_fp);
if (read_size != work_buffer_size) {
if (ferror(bip_fp)) {
return fsdevGetLastResult();
}
}
if (written + read_size > size) {
std::abort();
}
size_t aligned_size = ((read_size + SectorAlignment - 1) / SectorAlignment) * SectorAlignment;
if (R_FAILED((rc = this->Write(offset + written, work_buffer, aligned_size)))) {
return rc;
}
written += read_size;
if (read_size != work_buffer_size) {
break;
}
}
return ResultSuccess;
}
Result BisAccessor::Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size) {
Result rc;
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
std::abort();
}
std::memset(work_buffer, 0, work_buffer_size);
size_t written = 0;
while (written < size) {
size_t cur_write_size = std::min(work_buffer_size, size - written);
if (R_FAILED((rc = this->Write(offset + written, work_buffer, cur_write_size)))) {
return rc;
}
written += cur_write_size;
}
return ResultSuccess;
}
Result BisAccessor::GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size) {
Result rc;
if (offset % SectorAlignment != 0 || work_buffer_size % SectorAlignment != 0) {
std::abort();
}
Sha256Context sha_ctx;
sha256ContextCreate(&sha_ctx);
size_t total_read = 0;
while (total_read < hash_size) {
size_t cur_read_size = std::min(work_buffer_size, size - total_read);
size_t cur_update_size = std::min(cur_read_size, hash_size - total_read);
if (R_FAILED((rc = this->Read(work_buffer, cur_read_size, offset + total_read)))) {
return rc;
}
sha256ContextUpdate(&sha_ctx, work_buffer, cur_update_size);
total_read += cur_read_size;
}
sha256ContextGetHash(&sha_ctx, dst);
return ResultSuccess;
}
size_t Boot0Accessor::GetBootloaderVersion(void *bct) {
u32 version = *reinterpret_cast<u32 *>(reinterpret_cast<uintptr_t>(bct) + BctVersionOffset);
if (version > BctVersionMax) {
std::abort();
}
return static_cast<size_t>(version);
}
size_t Boot0Accessor::GetEksIndex(size_t bootloader_version) {
if (bootloader_version > BctVersionMax) {
std::abort();
}
return (bootloader_version > 0) ? bootloader_version - 1 : 0;
}
void Boot0Accessor::CopyEks(void *dst_bct, const void *src_eks, size_t eks_index) {
std::memcpy(reinterpret_cast<u8 *>(dst_bct) + BctEksOffset, reinterpret_cast<const u8 *>(src_eks) + eks_index * EksEntrySize, EksBlobSize);
}
Result Boot0Accessor::UpdateEks(void *dst_bct, void *eks_work_buffer) {
size_t read_size;
Result rc = this->Read(&read_size, eks_work_buffer, EksSize, Boot0Partition::Eks);
if (R_FAILED(rc)) {
return rc;
}
return this->UpdateEksManually(dst_bct, eks_work_buffer);
}
Result Boot0Accessor::UpdateEksManually(void *dst_bct, const void *src_eks) {
this->CopyEks(dst_bct, src_eks, GetEksIndex(GetBootloaderVersion(dst_bct)));
return ResultSuccess;
}
Result Boot0Accessor::PreserveAutoRcm(void *dst_bct, void *work_buffer, Boot0Partition which) {
std::memset(work_buffer, 0, BctSize);
size_t read_size;
Result rc = this->Read(&read_size, work_buffer, BctSize, which);
if (R_FAILED(rc)) {
return rc;
}
void *dst_pubk = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(dst_bct) + BctPubkOffset);
void *src_pubk = reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(work_buffer) + BctPubkOffset);
std::memcpy(dst_pubk, src_pubk, BctPubkSize);
return ResultSuccess;
}
@@ -0,0 +1,242 @@
/*
* 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 "updater_types.hpp"
class BisAccessor {
public:
static constexpr size_t SectorAlignment = 0x200;
private:
FsStorage storage = {};
u32 partition_id;
bool active;
public:
BisAccessor(u32 id) : partition_id(id), active(false) { }
~BisAccessor() {
if (this->active) {
fsStorageClose(&storage);
}
}
public:
Result Initialize();
void Finalize();
protected:
Result Read(void *dst, size_t size, u64 offset);
Result Write(u64 offset, const void *src, size_t size);
Result Write(u64 offset, size_t size, const char *bip_path, void *work_buffer, size_t work_buffer_size);
Result Clear(u64 offset, u64 size, void *work_buffer, size_t work_buffer_size);
Result GetHash(void *dst, u64 offset, u64 size, u64 hash_size, void *work_buffer, size_t work_buffer_size);
};
template<typename EnumType>
struct OffsetSizeEntry {
EnumType which;
u64 offset;
size_t size;
};
enum class Boot0Partition {
BctNormalMain,
BctSafeMain,
BctNormalSub,
BctSafeSub,
BctSave,
Package1NormalMain,
Package1NormalSub,
Eks,
Count,
};
enum class Boot1Partition {
Package1SafeMain,
Package1SafeSub,
Package1RepairMain,
Package1RepairSub,
Count,
};
enum class Package2Partition {
BootConfig,
Package2,
Count,
};
struct Boot0Meta {
using EnumType = Boot0Partition;
using OffsetSizeType = OffsetSizeEntry<EnumType>;
static constexpr size_t NumEntries = static_cast<size_t>(EnumType::Count);
static constexpr OffsetSizeType Entries[NumEntries] = {
{Boot0Partition::BctNormalMain, 0 * BctSize, BctSize},
{Boot0Partition::BctSafeMain, 1 * BctSize, BctSize},
{Boot0Partition::BctNormalSub, 2 * BctSize, BctSize},
{Boot0Partition::BctSafeSub, 3 * BctSize, BctSize},
{Boot0Partition::BctSave, 63 * BctSize, BctSize},
{Boot0Partition::Package1NormalMain, 0x100000, 0x40000},
{Boot0Partition::Package1NormalSub, 0x140000, 0x40000},
{Boot0Partition::Eks, 0x180000, EksSize},
};
};
struct Boot1Meta {
using EnumType = Boot1Partition;
using OffsetSizeType = OffsetSizeEntry<EnumType>;
static constexpr size_t NumEntries = static_cast<size_t>(EnumType::Count);
static constexpr OffsetSizeType Entries[NumEntries] = {
{Boot1Partition::Package1SafeMain, 0x00000, 0x40000},
{Boot1Partition::Package1SafeSub, 0x40000, 0x40000},
{Boot1Partition::Package1RepairMain, 0x80000, 0x40000},
{Boot1Partition::Package1RepairSub, 0xC0000, 0x40000},
};
};
struct Package2Meta {
using EnumType = Package2Partition;
using OffsetSizeType = OffsetSizeEntry<EnumType>;
static constexpr size_t NumEntries = static_cast<size_t>(EnumType::Count);
static constexpr OffsetSizeType Entries[NumEntries] = {
{Package2Partition::BootConfig, 0x0000, 0x004000},
{Package2Partition::Package2, 0x4000, 0x7FC000},
};
};
template<typename Meta>
class PartitionAccessor : public BisAccessor {
public:
using EnumType = typename Meta::EnumType;
using OffsetSizeType = typename Meta::OffsetSizeType;
public:
PartitionAccessor(u32 id) : BisAccessor(id) { }
private:
constexpr const OffsetSizeType *FindEntry(EnumType which) {
for (size_t i = 0; i < Meta::NumEntries; i++) {
if (Meta::Entries[i].which == which) {
return &Meta::Entries[i];
}
}
std::abort();
}
public:
Result Read(size_t *out_size, void *dst, size_t size, EnumType which) {
const auto entry = FindEntry(which);
if (size < entry->size) {
std::abort();
}
Result rc = BisAccessor::Read(dst, entry->size, entry->offset);
if (R_FAILED(rc)) {
return rc;
}
*out_size = entry->size;
return ResultSuccess;
}
Result Write(const void *src, size_t size, EnumType which) {
const auto entry = FindEntry(which);
if (size > entry->size || size % BisAccessor::SectorAlignment != 0) {
std::abort();
}
return BisAccessor::Write(entry->offset, src, size);
}
Result Write(const char *bip_path, void *work_buffer, size_t work_buffer_size, EnumType which) {
const auto entry = FindEntry(which);
return BisAccessor::Write(entry->offset, entry->size, bip_path, work_buffer, work_buffer_size);
}
Result Clear(void *work_buffer, size_t work_buffer_size, EnumType which) {
const auto entry = FindEntry(which);
return BisAccessor::Clear(entry->offset, entry->size, work_buffer, work_buffer_size);
}
Result GetHash(void *dst, u64 hash_size, void *work_buffer, size_t work_buffer_size, EnumType which) {
const auto entry = FindEntry(which);
return BisAccessor::GetHash(dst, entry->offset, entry->size, hash_size, work_buffer, work_buffer_size);
}
};
static constexpr u32 BisStorageId_Boot0 = 0;
static constexpr u32 BisStorageId_Boot1 = 10;
enum class Package2Type {
NormalMain,
NormalSub,
SafeMain,
SafeSub,
RepairMain,
RepairSub,
};
static constexpr u32 GetPackage2StorageId(Package2Type which) {
switch (which) {
case Package2Type::NormalMain:
return 21;
case Package2Type::NormalSub:
return 22;
case Package2Type::SafeMain:
return 23;
case Package2Type::SafeSub:
return 24;
case Package2Type::RepairMain:
return 25;
case Package2Type::RepairSub:
return 26;
default:
std::abort();
}
}
class Boot0Accessor : public PartitionAccessor<Boot0Meta> {
public:
static constexpr u32 PartitionId = 0;
static constexpr size_t BctPubkOffset = 0x210;
static constexpr size_t BctPubkSize = 0x100;
static constexpr size_t BctEksOffset = 0x450;
static constexpr size_t BctVersionOffset = 0x2330;
static constexpr size_t BctVersionMax = 0x20;
public:
Boot0Accessor() : PartitionAccessor<Boot0Meta>(PartitionId) { }
private:
static size_t GetBootloaderVersion(void *bct);
static size_t GetEksIndex(size_t bootloader_version);
static void CopyEks(void *dst_bct, const void *src_eks, size_t eks_index);
public:
Result UpdateEks(void *dst_bct, void *eks_work_buffer);
Result UpdateEksManually(void *dst_bct, const void *src_eks);
Result PreserveAutoRcm(void *dst_bct, void *work_buffer, Boot0Partition which);
};
class Boot1Accessor : public PartitionAccessor<Boot1Meta> {
public:
static constexpr u32 PartitionId = 10;
public:
Boot1Accessor() : PartitionAccessor<Boot1Meta>(PartitionId) { }
};
class Package2Accessor : public PartitionAccessor<Package2Meta> {
public:
Package2Accessor(Package2Type which) : PartitionAccessor<Package2Meta>(GetPackage2StorageId(which)) { }
};
@@ -0,0 +1,69 @@
/*
* 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 "updater_bis_save.hpp"
size_t BisSave::GetVerificationFlagOffset(BootModeType mode) {
switch (mode) {
case BootModeType_Normal:
return 0;
case BootModeType_Safe:
return 1;
default:
return 2;
}
}
Result BisSave::Initialize(void *work_buffer, size_t work_buffer_size) {
if (work_buffer_size < SaveSize || reinterpret_cast<uintptr_t>(work_buffer) & 0xFFF || work_buffer_size & 0x1FF) {
std::abort();
}
Result rc = this->accessor.Initialize();
if (R_FAILED(rc)) {
return rc;
}
this->save_buffer = work_buffer;
return ResultSuccess;
}
void BisSave::Finalize() {
this->accessor.Finalize();
}
Result BisSave::Load() {
size_t read_size;
return this->accessor.Read(&read_size, this->save_buffer, SaveSize, Boot0Partition::BctSave);
}
Result BisSave::Save() {
return this->accessor.Write(this->save_buffer, SaveSize, Boot0Partition::BctSave);
}
bool BisSave::GetNeedsVerification(BootModeType mode) {
return reinterpret_cast<const u8 *>(this->save_buffer)[GetVerificationFlagOffset(mode)] != 0;
}
void BisSave::SetNeedsVerification(BootModeType mode, bool needs_verification) {
reinterpret_cast<u8 *>(this->save_buffer)[GetVerificationFlagOffset(mode)] = needs_verification ? 1 : 0;
}
@@ -0,0 +1,42 @@
/*
* 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 "updater_types.hpp"
#include "updater_bis_management.hpp"
class BisSave {
public:
static constexpr size_t SaveSize = BctSize;
private:
Boot0Accessor accessor;
void *save_buffer;
public:
BisSave() : save_buffer(nullptr) { }
private:
static size_t GetVerificationFlagOffset(BootModeType mode);
public:
Result Initialize(void *work_buffer, size_t work_buffer_size);
void Finalize();
Result Load();
Result Save();
bool GetNeedsVerification(BootModeType mode);
void SetNeedsVerification(BootModeType mode, bool needs_verification);
};
@@ -0,0 +1,72 @@
/*
* 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 "updater_api.hpp"
Result Updater::ReadFile(size_t *out_size, void *dst, size_t dst_size, const char *path) {
FILE *fp = fopen(path, "rb");
if (fp == NULL) {
return ResultUpdaterInvalidBootImagePackage;
}
ON_SCOPE_EXIT { fclose(fp); };
std::memset(dst, 0, dst_size);
size_t read_size = fread(dst, 1, dst_size, fp);
if (ferror(fp)) {
return fsdevGetLastResult();
}
*out_size = read_size;
return ResultSuccess;
}
Result Updater::GetFileHash(size_t *out_size, void *dst_hash, const char *path, void *work_buffer, size_t work_buffer_size) {
FILE *fp = fopen(path, "rb");
if (fp == NULL) {
return ResultUpdaterInvalidBootImagePackage;
}
ON_SCOPE_EXIT { fclose(fp); };
Sha256Context sha_ctx;
sha256ContextCreate(&sha_ctx);
size_t total_size = 0;
while (true) {
size_t read_size = fread(work_buffer, 1, work_buffer_size, fp);
if (ferror(fp)) {
return fsdevGetLastResult();
}
if (read_size == 0) {
break;
}
sha256ContextUpdate(&sha_ctx, work_buffer, read_size);
total_size += read_size;
if (read_size != work_buffer_size) {
break;
}
}
sha256ContextGetHash(&sha_ctx, dst_hash);
*out_size = total_size;
return ResultSuccess;
}
@@ -0,0 +1,110 @@
/*
* 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 <sys/stat.h>
#include "updater_api.hpp"
static const char *BootImagePackageMountPath = "bip";
static const char *BctPathNx = "bip:/nx/bct";
static const char *Package1PathNx = "bip:/nx/package1";
static const char *Package2PathNx = "bip:/nx/package2";
static const char *BctPathA = "bip:/a/bct";
static const char *Package1PathA = "bip:/a/package1";
static const char *Package2PathA = "bip:/a/package2";
const char *Updater::GetBootImagePackageMountPath() {
return BootImagePackageMountPath;
}
static const char *ChooseCandidatePath(const char **candidates, size_t num_candidates) {
if (num_candidates == 0) {
std::abort();
}
for (size_t i = 0; i < num_candidates; i++) {
struct stat buf;
if (stat(candidates[i], &buf) != 0) {
continue;
}
if (!S_ISREG(buf.st_mode)) {
continue;
}
return candidates[i];
}
/* Nintendo just uses the last candidate if they all fail...should we abort? */
return candidates[num_candidates - 1];
}
const char *Updater::GetBctPath(BootImageUpdateType boot_image_update_type) {
switch (boot_image_update_type) {
case BootImageUpdateType_Erista:
{
const char *candidates[] = {BctPathNx};
return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0]));
}
case BootImageUpdateType_Mariko:
{
const char *candidates[] = {BctPathA, BctPathNx};
return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0]));
}
default:
std::abort();
}
}
const char *Updater::GetPackage1Path(BootImageUpdateType boot_image_update_type) {
switch (boot_image_update_type) {
case BootImageUpdateType_Erista:
{
const char *candidates[] = {Package1PathNx};
return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0]));
}
case BootImageUpdateType_Mariko:
{
const char *candidates[] = {Package1PathA, Package1PathNx};
return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0]));
}
default:
std::abort();
}
}
const char *Updater::GetPackage2Path(BootImageUpdateType boot_image_update_type) {
switch (boot_image_update_type) {
case BootImageUpdateType_Erista:
{
const char *candidates[] = {Package2PathNx};
return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0]));
}
case BootImageUpdateType_Mariko:
{
const char *candidates[] = {Package2PathA, Package2PathNx};
return ChooseCandidatePath(candidates, sizeof(candidates) / sizeof(candidates[0]));
}
default:
std::abort();
}
}
@@ -0,0 +1,42 @@
/*
* 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>
/* TODO: Better way to do this? */
#include "../boot_types.hpp"
enum BootImageUpdateType {
BootImageUpdateType_Erista,
BootImageUpdateType_Mariko,
};
enum BootModeType {
BootModeType_Normal,
BootModeType_Safe,
};
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,7 +22,7 @@ void StopSoundTask::StopSound() {
/* Talk to the ALC5639 over I2C, and disable audio output. */
{
I2cSession audio;
if (R_SUCCEEDED(i2cOpenSession(&audio, I2cDevice_AudioCodec))) {
if (R_SUCCEEDED(i2cOpenSession(&audio, I2cDevice_Alc5639))) {
struct {
u16 dev;
u8 val;
+1 -1
View File
@@ -65,7 +65,7 @@ struct AsyncOperationKey {
struct BootReasonValue {
u8 power_intr;
u8 rtc_intr;
u8 _0x3;
u8 nv_erc;
u8 boot_reason;
};
static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");