Merge pull request #536 from Atmosphere-NX/boot_dev

Boot: reimplement the boot sysmodule
This commit is contained in:
SciresM 2019-05-10 02:37:03 -07:00 committed by GitHub
commit 27cde7da7a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
94 changed files with 10813 additions and 2681 deletions

View file

@ -38,11 +38,6 @@
#include "sc7.h" #include "sc7.h"
#include "exocfg.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_LOG_SMCS 0
#define DEBUG_PANIC_ON_FAILURE 0 #define DEBUG_PANIC_ON_FAILURE 0
@ -97,7 +92,7 @@ typedef struct {
uint32_t num_handlers; uint32_t num_handlers;
} smc_table_t; } 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}, {0, 4, NULL},
{0xC3000401, 4, smc_set_config_user}, {0xC3000401, 4, smc_set_config_user},
{0xC3000002, 1, smc_get_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}, {0xC3000011, 4, smc_load_titlekey},
{0xC3000012, 4, smc_unwrap_aes_wrapped_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}, {0, 4, NULL},
{0xC4000001, 4, smc_cpu_suspend}, {0xC4000001, 4, smc_cpu_suspend},
{0x84000002, 4, smc_cpu_off}, {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}, {0xC3000007, 1, smc_configure_carveout},
{0xC3000008, 1, smc_read_write_register} {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. */ /* 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}, {0, 4, NULL},
{0xF0000201, 0, smc_ams_iram_copy}, {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] = { static smc_table_t g_smc_tables[SMC_HANDLER_COUNT + 1] = {
{ /* SMC_HANDLER_USER */ { /* SMC_HANDLER_USER */

View file

@ -96,7 +96,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 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 \ BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \
exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \ exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \
sept-primary.bin sept-secondary.enc \ sept-primary.bin sept-secondary.enc \

View file

@ -220,10 +220,8 @@ SECTIONS
======================= */ ======================= */
PROVIDE(__ams_mitm_kip_start__ = ams_mitm_kip - __start__); PROVIDE(__ams_mitm_kip_start__ = ams_mitm_kip - __start__);
PROVIDE(__ams_mitm_kip_size__ = ams_mitm_kip_end - ams_mitm_kip); PROVIDE(__ams_mitm_kip_size__ = ams_mitm_kip_end - ams_mitm_kip);
PROVIDE(__boot_100_kip_start__ = boot_100_kip - __start__); PROVIDE(__boot_kip_start__ = boot_kip - __start__);
PROVIDE(__boot_100_kip_size__ = boot_100_kip_end - boot_100_kip); PROVIDE(__boot_kip_size__ = boot_kip_end - boot_kip);
PROVIDE(__boot_200_kip_start__ = boot_200_kip - __start__);
PROVIDE(__boot_200_kip_size__ = boot_200_kip_end - boot_200_kip);
PROVIDE(__exosphere_bin_start__ = exosphere_bin - __start__); PROVIDE(__exosphere_bin_start__ = exosphere_bin - __start__);
PROVIDE(__exosphere_bin_size__ = exosphere_bin_end - exosphere_bin); PROVIDE(__exosphere_bin_size__ = exosphere_bin_end - exosphere_bin);
PROVIDE(__fusee_primary_bin_start__ = fusee_primary_bin - __start__); PROVIDE(__fusee_primary_bin_start__ = fusee_primary_bin - __start__);

View file

@ -103,20 +103,12 @@ _content_headers:
.asciz "ams_mitm" .asciz "ams_mitm"
.align 5 .align 5
/* boot_100 content header */ /* boot content header */
.word __boot_100_kip_start__ .word __boot_kip_start__
.word __boot_100_kip_size__ .word __boot_kip_size__
.word CONTENT_TYPE_KIP .word CONTENT_TYPE_KIP
.word 0xCCCCCCCC .word 0xCCCCCCCC
.asciz "boot_100" .asciz "boot"
.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"
.align 5 .align 5
/* exosphere content header */ /* exosphere content header */

View file

@ -33,8 +33,7 @@
#include "pm_kip.h" #include "pm_kip.h"
#include "sm_kip.h" #include "sm_kip.h"
#include "ams_mitm_kip.h" #include "ams_mitm_kip.h"
#include "boot_100_kip.h" #include "boot_kip.h"
#include "boot_200_kip.h"
#include "spl_kip.h" #include "spl_kip.h"
#undef u8 #undef u8
#undef u32 #undef u32
@ -47,18 +46,14 @@ static bool g_stratosphere_sm_enabled = true;
static bool g_stratosphere_pm_enabled = true; static bool g_stratosphere_pm_enabled = true;
static bool g_stratosphere_ams_mitm_enabled = true; static bool g_stratosphere_ams_mitm_enabled = true;
static bool g_stratosphere_spl_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[], boot_kip[], ams_mitm_kip[];
extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_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;
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;
/* GCC doesn't consider the size as const... we have to write it ourselves. */ /* GCC doesn't consider the size as const... we have to write it ourselves. */
ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) { 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; uint32_t num_processes = 0;
uint8_t *data; uint8_t *data;
@ -66,14 +61,6 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) {
return g_stratosphere_ini1; 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); size_t size = sizeof(ini1_header_t);
/* Calculate our processes' sizes. */ /* Calculate our processes' sizes. */

View file

@ -16,6 +16,12 @@ ifneq (, $(strip $(shell git status --porcelain 2>/dev/null)))
AMSREV := $(AMSREV)-dirty AMSREV := $(AMSREV)-dirty
endif 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 # TARGET is the name of the output
@ -27,7 +33,7 @@ endif
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR)) TARGET := $(notdir $(CURDIR))
BUILD := build BUILD := build
SOURCES := source SOURCES := source source/i2c_driver source/updater
DATA := data DATA := data
INCLUDES := include ../../common/include INCLUDES := include ../../common/include
EXEFS_SRC := exefs_src EXEFS_SRC := exefs_src
@ -69,14 +75,14 @@ export OUTPUT := $(CURDIR)/$(TARGET)
export TOPDIR := $(CURDIR) export TOPDIR := $(CURDIR)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 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) export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 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 # use CXX for linking C++ projects, CC for standard C
@ -116,19 +122,23 @@ else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif endif
.PHONY: $(BUILD) clean all .PHONY: $(BUILD) check_fusee clean all
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
all: $(BUILD) all: $(BUILD)
$(BUILD): check_fusee:
@$(MAKE) -C $(TOPDIR)/../../fusee/fusee-primary all
$(BUILD): check_fusee
@[ -d $@ ] || mkdir -p $@ @[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
clean: clean:
@echo 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 # main targets
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
all : $(OUTPUT)_100.kip $(OUTPUT)_200.kip all : $(OUTPUT).kip
$(OUTPUT)_100.kip : $(OUTPUT)_100.elf $(OUTPUT).kip : $(OUTPUT).elf
$(OUTPUT)_200.kip : $(OUTPUT)_200.elf
$(OUTPUT)_100.kip : APP_JSON = $(TOPDIR)/boot_100.json $(OUTPUT).elf : $(OFILES)
$(OUTPUT)_200.kip : APP_JSON = $(TOPDIR)/boot_200.json
$(OUTPUT)_100.elf : $(OFILES)
$(OUTPUT)_200.elf : $(OFILES)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data # 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 %.bin.o : %.bin
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@echo $(notdir $<) @echo $(notdir $<)

View file

@ -1,7 +1,7 @@
{ {
"name": "boot", "name": "boot",
"title_id": "0x0100000000000005", "title_id": "0x0100000000000005",
"main_thread_stack_size": "0x1000", "main_thread_stack_size": "0x8000",
"main_thread_priority": 27, "main_thread_priority": 27,
"default_cpu_id": 3, "default_cpu_id": 3,
"process_category": 1, "process_category": 1,

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
]
}
]
}

View file

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

View file

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -22,7 +22,7 @@ void StopSoundTask::StopSound() {
/* Talk to the ALC5639 over I2C, and disable audio output. */ /* Talk to the ALC5639 over I2C, and disable audio output. */
{ {
I2cSession audio; I2cSession audio;
if (R_SUCCEEDED(i2cOpenSession(&audio, I2cDevice_AudioCodec))) { if (R_SUCCEEDED(i2cOpenSession(&audio, I2cDevice_Alc5639))) {
struct { struct {
u16 dev; u16 dev;
u8 val; u8 val;

@ -1 +1 @@
Subproject commit 9dfe7709d950ef440548b123e43ea69ce52684b4 Subproject commit e8707a6581ecf33a8e35601c8f2e11018bbdc7ec

View file

@ -65,7 +65,7 @@ struct AsyncOperationKey {
struct BootReasonValue { struct BootReasonValue {
u8 power_intr; u8 power_intr;
u8 rtc_intr; u8 rtc_intr;
u8 _0x3; u8 nv_erc;
u8 boot_reason; u8 boot_reason;
}; };
static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");