mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 12:21:18 +00:00
sdmmc: implement driver suitable for fs + bootloader
* sdmmc: begin skeletoning sdmmc driver * sdmmc: add most of SdHostStandardController * sdmmc: implement most of SdmmcController * sdmmc: Sdmmc2Controller * sdmmc: skeleton implementation of Sdmmc1Controller * sdmmc: complete abstract logic for Sdmmc1 power controller * sdmmc: implement gpio handling for sdmmc1-register-control * sdmmc: implement pinmux handling for sdmmc1-register-control * sdmmc: fix building for arm32 and in stratosphere context * sdmmc: implement voltage enable/set for sdmmc1-register-control * util: move T(V)SNPrintf from kernel to util * sdmmc: implement BaseDeviceAccessor * sdmmc: implement MmcDeviceAccessor * sdmmc: implement clock reset controller for register api * sdmmc: fix bug in WaitWhileCommandInhibit, add mmc accessors * exo: add sdmmc test program * sdmmc: fix speed mode extension, add CheckMmcConnection for debug * sdmmc: add DeviceDetector, gpio: implement client api * gpio: modernize client api instead of doing it the lazy way * sdmmc: SdCardDeviceAccessor impl * sdmmc: update test program to read first two sectors of sd card * sdmmc: fix vref sel * sdmmc: finish outward-facing api (untested) * ams: changes for libvapours including tegra register defs * sdmmc: remove hwinit
This commit is contained in:
parent
ac04e02a08
commit
166318ba77
143 changed files with 13696 additions and 1569 deletions
|
@ -648,7 +648,7 @@ namespace ams::secmon {
|
|||
reg::Read (MC + MC_SMMU_TLB_CONFIG);
|
||||
|
||||
/* Flush the entire page table cache, and read TLB_CONFIG to ensure the flush takes. */
|
||||
reg::Write(MC + MC_SMMU_PTC_FLUSH, 0);
|
||||
reg::Write(MC + MC_SMMU_PTC_FLUSH_0, 0);
|
||||
reg::Read (MC + MC_SMMU_TLB_CONFIG);
|
||||
|
||||
/* Flush the entire translation lookaside buffer, and read TLB_CONFIG to ensure the flush takes. */
|
||||
|
@ -907,7 +907,7 @@ namespace ams::secmon {
|
|||
reg::Write(MC + MC_SMMU_PPCS1_ASID, MC_REG_BITS_ENUM(SMMU_PPCS1_ASID_PPCS1_SMMU_ENABLE, ENABLE), MC_REG_BITS_VALUE(SMMU_PPCS1_ASID_PPCS1_ASID, BpmpAsid));
|
||||
|
||||
/* Flush the entire page table cache, and read TLB_CONFIG to ensure the flush takes. */
|
||||
reg::Write(MC + MC_SMMU_PTC_FLUSH, 0);
|
||||
reg::Write(MC + MC_SMMU_PTC_FLUSH_0, 0);
|
||||
reg::Read (MC + MC_SMMU_TLB_CONFIG);
|
||||
|
||||
/* Flush the entire translation lookaside buffer, and read TLB_CONFIG to ensure the flush takes. */
|
||||
|
|
|
@ -22,7 +22,7 @@ SetRegisterAllowed(MC_SMMU_CONFIG); /* 0x010 */
|
|||
SetRegisterAllowed(MC_SMMU_PTB_ASID); /* 0x01C */
|
||||
SetRegisterAllowed(MC_SMMU_PTB_DATA); /* 0x020 */
|
||||
SetRegisterAllowed(MC_SMMU_TLB_FLUSH); /* 0x030 */
|
||||
SetRegisterAllowed(MC_SMMU_PTC_FLUSH); /* 0x034 */
|
||||
SetRegisterAllowed(MC_SMMU_PTC_FLUSH_0); /* 0x034 */
|
||||
SetRegisterAllowed(MC_EMEM_CFG); /* 0x050 */
|
||||
SetRegisterAllowed(MC_EMEM_ADR_CFG); /* 0x054 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_CFG); /* 0x090 */
|
||||
|
@ -53,7 +53,7 @@ SetRegisterAllowed(MC_SMMU_DCB_ASID); /* 0x244 */
|
|||
SetRegisterAllowed(MC_SMMU_HC_ASID); /* 0x250 */
|
||||
SetRegisterAllowed(MC_SMMU_HDA_ASID); /* 0x254 */
|
||||
SetRegisterAllowed(MC_SMMU_ISP2_ASID); /* 0x258 */
|
||||
SetRegisterAllowed(MC_SMMU_NVENC_ASID); /* 0x264 */
|
||||
SetRegisterAllowed(MC_SMMU_MSENC_NVENC_ASID); /* 0x264 */
|
||||
SetRegisterAllowed(MC_SMMU_NV_ASID); /* 0x268 */
|
||||
SetRegisterAllowed(MC_SMMU_NV2_ASID); /* 0x26C */
|
||||
SetRegisterAllowed(MC_SMMU_PPCS_ASID); /* 0x270 */
|
||||
|
|
108
exosphere/sdmmc_test/Makefile
Normal file
108
exosphere/sdmmc_test/Makefile
Normal file
|
@ -0,0 +1,108 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
# Define the atmosphere board and cpu
|
||||
#---------------------------------------------------------------------------------
|
||||
export ATMOSPHERE_BOARD := nx-hac-001
|
||||
export ATMOSPHERE_CPU := arm7tdmi
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# pull in common atmosphere configuration
|
||||
#---------------------------------------------------------------------------------
|
||||
include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/exosphere.mk
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#---------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c)
|
||||
CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp)
|
||||
SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
#---------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#---------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(subst -,_,$(BINFILES))))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I.
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib -L$(dir)/$(ATMOSPHERE_LIBRARY_DIR))
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
all: $(BUILD) check_libexo
|
||||
|
||||
$(BUILD): check_libexo
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
check_libexo:
|
||||
@$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).bin
|
||||
|
||||
$(OUTPUT).bin : $(OUTPUT).elf
|
||||
$(OBJCOPY) -S -O binary --set-section-flags .bss=alloc,load,contents $< $@
|
||||
@echo built ... $(notdir $@)
|
||||
|
||||
$(OUTPUT).elf : $(OFILES) ../../../libraries/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/libexosphere.a
|
||||
|
||||
%.elf:
|
||||
@echo linking $(notdir $@)
|
||||
$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
@$(NM) -CSn $@ > $(notdir $*.lst)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#---------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h: %.bin
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
194
exosphere/sdmmc_test/sdmmc_test.ld
Normal file
194
exosphere/sdmmc_test/sdmmc_test.ld
Normal file
|
@ -0,0 +1,194 @@
|
|||
OUTPUT_ARCH(arm)
|
||||
ENTRY(_ZN3ams10sdmmc_test5StartEv)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
NULL : ORIGIN = 0, LENGTH = 4K
|
||||
test_fw : ORIGIN = 0x40010000, LENGTH = 32K
|
||||
}
|
||||
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* =========== CODE section =========== */
|
||||
PROVIDE(__start__ = ORIGIN(test_fw));
|
||||
. = __start__;
|
||||
__code_start = . ;
|
||||
|
||||
.crt0 :
|
||||
{
|
||||
KEEP (*(.crt0 .crt0.*))
|
||||
. = ALIGN(8);
|
||||
} >test_fw
|
||||
|
||||
.vectors :
|
||||
{
|
||||
KEEP (*(.vectors .vectors.*))
|
||||
. = ALIGN(8);
|
||||
} >test_fw
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
||||
*(.text.exit .text.exit.*)
|
||||
*(.text.startup .text.startup.*)
|
||||
*(.text.hot .text.hot.*)
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
. = ALIGN(8);
|
||||
} >test_fw
|
||||
|
||||
.init :
|
||||
{
|
||||
KEEP( *(.init) )
|
||||
. = ALIGN(8);
|
||||
} >test_fw
|
||||
|
||||
.plt :
|
||||
{
|
||||
*(.plt)
|
||||
*(.iplt)
|
||||
. = ALIGN(8);
|
||||
} >test_fw
|
||||
|
||||
.fini :
|
||||
{
|
||||
KEEP( *(.fini) )
|
||||
. = ALIGN(8);
|
||||
} >test_fw
|
||||
|
||||
|
||||
/* =========== RODATA section =========== */
|
||||
. = ALIGN(8);
|
||||
__rodata_start = . ;
|
||||
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||
. = ALIGN(8);
|
||||
} >test_fw
|
||||
|
||||
.eh_frame_hdr : { __eh_frame_hdr_start = .; *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) __eh_frame_hdr_end = .; } >test_fw
|
||||
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) } >test_fw
|
||||
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } >test_fw
|
||||
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) } >test_fw
|
||||
|
||||
.hash : { *(.hash) } >test_fw
|
||||
|
||||
/* =========== DATA section =========== */
|
||||
. = ALIGN(8);
|
||||
__data_start = . ;
|
||||
|
||||
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) } >test_fw
|
||||
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } >test_fw
|
||||
.gnu_extab : ONLY_IF_RW { *(.gnu_extab*) } >test_fw
|
||||
.exception_ranges : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) } >test_fw
|
||||
|
||||
.preinit_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array))
|
||||
PROVIDE (__preinit_array_end = .);
|
||||
} >test_fw
|
||||
|
||||
.init_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array))
|
||||
PROVIDE (__init_array_end = .);
|
||||
} >test_fw
|
||||
|
||||
.fini_array ALIGN(8) :
|
||||
{
|
||||
PROVIDE (__fini_array_start = .);
|
||||
KEEP (*(.fini_array))
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
PROVIDE (__fini_array_end = .);
|
||||
} >test_fw
|
||||
|
||||
.ctors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.ctors)) /* MUST be first -- GCC requires it */
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
} >test_fw
|
||||
|
||||
.dtors ALIGN(8) :
|
||||
{
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
} >test_fw
|
||||
|
||||
__got_start__ = .;
|
||||
|
||||
.got : { *(.got) *(.igot) } >test_fw
|
||||
.got.plt : { *(.got.plt) *(.igot.plt) } >test_fw
|
||||
|
||||
__got_end__ = .;
|
||||
|
||||
.data ALIGN(8) :
|
||||
{
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
SORT(CONSTRUCTORS)
|
||||
} >test_fw
|
||||
|
||||
__bss_start__ = .;
|
||||
.bss ALIGN(8) :
|
||||
{
|
||||
*(.dynbss)
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(16);
|
||||
} >test_fw
|
||||
__bss_end__ = .;
|
||||
|
||||
__end__ = ABSOLUTE(.) ;
|
||||
|
||||
__total_size__ = (__end__ - __start__);
|
||||
|
||||
__stack_top__ = 0x40031000;
|
||||
__stack_bottom__ = 0x40030000;
|
||||
|
||||
/* ==================
|
||||
==== Metadata ====
|
||||
================== */
|
||||
|
||||
/* Discard sections that difficult post-processing */
|
||||
/DISCARD/ : { *(.group .comment .note .interp) }
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
}
|
7
exosphere/sdmmc_test/sdmmc_test.specs
Normal file
7
exosphere/sdmmc_test/sdmmc_test.specs
Normal file
|
@ -0,0 +1,7 @@
|
|||
%rename link old_link
|
||||
|
||||
*link:
|
||||
%(old_link) -T %:getenv(TOPDIR /sdmmc_test.ld) --gc-sections --nmagic -nostdlib -nostartfiles
|
||||
|
||||
*startfile:
|
||||
crti%O%s crtbegin%O%s
|
146
exosphere/sdmmc_test/source/sdmmc_test_main.cpp
Normal file
146
exosphere/sdmmc_test/source/sdmmc_test_main.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::sdmmc_test {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const uintptr_t PMC = secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
|
||||
|
||||
constexpr inline auto Port = sdmmc::Port_SdCard0;
|
||||
alignas(8) constinit u8 g_sd_work_buffer[sdmmc::SdCardWorkBufferSize];
|
||||
|
||||
constexpr inline u32 SectorIndex = 0;
|
||||
constexpr inline u32 SectorCount = 2;
|
||||
|
||||
NORETURN void PmcMainReboot() {
|
||||
/* Write enable to MAIN_RESET. */
|
||||
reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE));
|
||||
|
||||
/* Wait forever until we're reset. */
|
||||
AMS_INFINITE_LOOP();
|
||||
}
|
||||
|
||||
void CheckResult(const Result result) {
|
||||
volatile u32 * const DEBUG = reinterpret_cast<volatile u32 *>(0x4003C000);
|
||||
if (R_FAILED(result)) {
|
||||
DEBUG[1] = result.GetValue();
|
||||
PmcMainReboot();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Main() {
|
||||
/* Perform butchered hwinit. */
|
||||
/* TODO: replace with simpler, non-C logic. */
|
||||
/* nx_hwinit(); */
|
||||
|
||||
/* Clear output buffer for debug. */
|
||||
std::memset((void *)0x40038000, 0xAA, 0x400);
|
||||
|
||||
/* Normally, these pins get configured by boot sysmodule during initial pinmux config. */
|
||||
/* However, they're required to access the SD card. */
|
||||
{
|
||||
const uintptr_t apb_misc = dd::QueryIoMapping(0x70000000, 0x4000);
|
||||
|
||||
reg::ReadWrite(apb_misc + PINMUX_AUX_SDMMC1_CLK, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_DOWN),
|
||||
PINMUX_REG_BITS_ENUM(AUX_SDMMC1_CLK_PM, SDMMC1));
|
||||
|
||||
reg::ReadWrite(apb_misc + PINMUX_AUX_SDMMC1_CMD, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_UP),
|
||||
PINMUX_REG_BITS_ENUM(AUX_SDMMC1_CMD_PM, SDMMC1));
|
||||
|
||||
reg::ReadWrite(apb_misc + PINMUX_AUX_SDMMC1_DAT3, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_UP),
|
||||
PINMUX_REG_BITS_ENUM(AUX_SDMMC1_DAT3_PM, SDMMC1));
|
||||
|
||||
reg::ReadWrite(apb_misc + PINMUX_AUX_SDMMC1_DAT2, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_UP),
|
||||
PINMUX_REG_BITS_ENUM(AUX_SDMMC1_DAT2_PM, SDMMC1));
|
||||
|
||||
reg::ReadWrite(apb_misc + PINMUX_AUX_SDMMC1_DAT1, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_UP),
|
||||
PINMUX_REG_BITS_ENUM(AUX_SDMMC1_DAT1_PM, SDMMC1));
|
||||
|
||||
reg::ReadWrite(apb_misc + PINMUX_AUX_SDMMC1_DAT0, PINMUX_REG_BITS_ENUM(AUX_E_INPUT, ENABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_UP),
|
||||
PINMUX_REG_BITS_ENUM(AUX_SDMMC1_DAT0_PM, SDMMC1));
|
||||
|
||||
reg::ReadWrite(apb_misc + PINMUX_AUX_DMIC3_CLK, PINMUX_REG_BITS_ENUM(AUX_E_OD, DISABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_E_INPUT, DISABLE),
|
||||
PINMUX_REG_BITS_ENUM(AUX_TRISTATE, PASSTHROUGH),
|
||||
PINMUX_REG_BITS_ENUM(AUX_SDMMC1_DAT0_PM, RSVD2));
|
||||
}
|
||||
|
||||
/* Debug signaler. */
|
||||
volatile u32 * const DEBUG = reinterpret_cast<volatile u32 *>(0x4003C000);
|
||||
DEBUG[0] = 0;
|
||||
DEBUG[1] = 0xAAAAAAAA;
|
||||
|
||||
/* Initialize sdmmc library. */
|
||||
sdmmc::Initialize(Port);
|
||||
DEBUG[0] = 1;
|
||||
|
||||
sdmmc::SetSdCardWorkBuffer(Port, g_sd_work_buffer, sizeof(g_sd_work_buffer));
|
||||
DEBUG[0] = 2;
|
||||
|
||||
Result result = sdmmc::Activate(Port);
|
||||
DEBUG[0] = 3;
|
||||
CheckResult(result);
|
||||
|
||||
/* Read the first two sectors from disk. */
|
||||
void * const sector_dst = reinterpret_cast<void *>(0x40038000);
|
||||
result = sdmmc::Read(sector_dst, SectorCount * sdmmc::SectorSize, Port, SectorIndex, SectorCount);
|
||||
DEBUG[0] = 4;
|
||||
CheckResult(result);
|
||||
|
||||
/* Get the connection status. */
|
||||
sdmmc::SpeedMode speed_mode;
|
||||
sdmmc::BusWidth bus_width;
|
||||
result = sdmmc::CheckSdCardConnection(std::addressof(speed_mode), std::addressof(bus_width), Port);
|
||||
|
||||
/* Save status for debug. */
|
||||
DEBUG[0] = 5;
|
||||
DEBUG[1] = result.GetValue();
|
||||
DEBUG[2] = static_cast<u32>(speed_mode);
|
||||
DEBUG[3] = static_cast<u32>(bus_width);
|
||||
|
||||
/* Perform a reboot. */
|
||||
PmcMainReboot();
|
||||
}
|
||||
|
||||
NORETURN void ExceptionHandler() {
|
||||
PmcMainReboot();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ams::diag {
|
||||
|
||||
void AbortImpl() {
|
||||
sdmmc_test::ExceptionHandler();
|
||||
}
|
||||
|
||||
}
|
37
exosphere/sdmmc_test/source/sdmmc_test_start.s
Normal file
37
exosphere/sdmmc_test/source/sdmmc_test_start.s
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.section .crt0.text._ZN3ams10sdmmc_test5StartEv, "ax", %progbits
|
||||
.align 3
|
||||
.global _ZN3ams10sdmmc_test5StartEv
|
||||
_ZN3ams10sdmmc_test5StartEv:
|
||||
/* Switch to system mode, mask all interrupts, clear all flags */
|
||||
msr cpsr_cxsf, #0xDF
|
||||
|
||||
/* Set the stack pointer. */
|
||||
ldr sp, =__stack_top__
|
||||
|
||||
/* Set our link register to the exception handler. */
|
||||
ldr lr, =_ZN3ams10sdmmc_test16ExceptionHandlerEv
|
||||
|
||||
/* Call init array functions. */
|
||||
bl __libc_init_array
|
||||
|
||||
/* Invoke main. */
|
||||
b _ZN3ams10sdmmc_test4MainEv
|
||||
|
||||
/* Infinite loop. */
|
||||
2: b 2b
|
|
@ -30,7 +30,7 @@ _ZN3ams8warmboot5StartEv:
|
|||
|
||||
/* Invoke main. */
|
||||
ldr r0, =_metadata
|
||||
bl _ZN3ams8warmboot4MainEPKNS0_8MetadataE
|
||||
b _ZN3ams8warmboot4MainEPKNS0_8MetadataE
|
||||
|
||||
/* Infinite loop. */
|
||||
1: b 1b
|
|
@ -140,9 +140,9 @@ ATMOSPHERE_GCH_IDENTIFIER ?= ams_placeholder_gch_identifier
|
|||
# Rules for compiling pre-compiled headers
|
||||
#---------------------------------------------------------------------------------
|
||||
%.hpp.gch/$(ATMOSPHERE_GCH_IDENTIFIER): %.hpp | %.hpp.gch
|
||||
$(SILENTMSG) Precompiling $(notdir $<) for $(ATMOSPHERE_GCH_IDENTIFIER)
|
||||
@echo Precompiling $(notdir $<) for $(ATMOSPHERE_GCH_IDENTIFIER)
|
||||
$(SILENTCMD)$(CXX) -w -x c++-header -MMD -MP -MQ$@ -MF $(DEPSDIR)/$(notdir $*).d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
||||
%.hpp.gch: %.hpp
|
||||
$(SILENTMSG) Precompiling $(notdir $<)
|
||||
@echo Precompiling $(notdir $<)
|
||||
$(SILENTCMD)$(CXX) -w -x c++-header -MMD -MP -MQ$@ -MF $(DEPSDIR)/$(notdir $*).d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER)
|
||||
|
|
|
@ -112,6 +112,7 @@ $(OFILES) : $(GCH_FILES)
|
|||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
libc.o: CFLAGS += -fno-builtin -fno-lto
|
||||
libgcc_division.arch.arm.o: CFLAGS += -fno-builtin -fno-lto
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
%_bin.h %.bin.o : %.bin
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <vapours.hpp>
|
||||
|
||||
#include <exosphere/common.hpp>
|
||||
#include <exosphere/reg.hpp>
|
||||
#include <exosphere/hw.hpp>
|
||||
#include <exosphere/util.hpp>
|
||||
#include <exosphere/mmu.hpp>
|
||||
|
@ -41,4 +40,3 @@
|
|||
#include <exosphere/actmon.hpp>
|
||||
#include <exosphere/pmc.hpp>
|
||||
#include <exosphere/secmon.hpp>
|
||||
#include <exosphere/tegra.hpp>
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
#include <exosphere/tegra/tegra_ahb_arbc.hpp>
|
||||
#include <exosphere/tegra/tegra_apb_misc.hpp>
|
||||
#include <exosphere/tegra/tegra_avp_cache.hpp>
|
||||
#include <exosphere/tegra/tegra_clkrst.hpp>
|
||||
#include <exosphere/tegra/tegra_emc.hpp>
|
||||
#include <exosphere/tegra/tegra_evp.hpp>
|
||||
#include <exosphere/tegra/tegra_flow_ctlr.hpp>
|
||||
#include <exosphere/tegra/tegra_ictlr.hpp>
|
||||
#include <exosphere/tegra/tegra_mc.hpp>
|
||||
#include <exosphere/tegra/tegra_mselect.hpp>
|
||||
#include <exosphere/tegra/tegra_pinmux.hpp>
|
||||
#include <exosphere/tegra/tegra_pg_up.hpp>
|
||||
#include <exosphere/tegra/tegra_pmc.hpp>
|
||||
#include <exosphere/tegra/tegra_sb.hpp>
|
||||
#include <exosphere/tegra/tegra_sysctr0.hpp>
|
||||
#include <exosphere/tegra/tegra_timer.hpp>
|
|
@ -20,14 +20,6 @@ namespace ams::util {
|
|||
|
||||
void SetRegisterAddress(uintptr_t address);
|
||||
|
||||
u32 GetMicroSeconds();
|
||||
void WaitMicroSeconds(int us);
|
||||
|
||||
void ClearMemory(void *ptr, size_t size);
|
||||
|
||||
template<typename T, typename U> requires std::integral<T> && std::integral<U>
|
||||
constexpr T DivideUp(T x, U y) {
|
||||
return (x + (y - 1)) / y;
|
||||
}
|
||||
|
||||
}
|
27
libraries/libexosphere/source/libc/libexo_cxx.cpp
Normal file
27
libraries/libexosphere/source/libc/libexo_cxx.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* cxx implementation details to be stubbed here, as needed. */
|
||||
void __cxa_pure_virtual() { AMS_ABORT("pure virtual function call"); }
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
160
libraries/libexosphere/source/libc/libgcc_division.arch.arm.c
Normal file
160
libraries/libexosphere/source/libc/libgcc_division.arch.arm.c
Normal file
|
@ -0,0 +1,160 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
/*
|
||||
* Copyright (c) 2014, STMicroelectronics International N.V.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Form ABI specifications:
|
||||
* int __aeabi_idiv(int numerator, int denominator);
|
||||
* unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator);
|
||||
*
|
||||
* typedef struct { int quot; int rem; } idiv_return;
|
||||
* typedef struct { unsigned quot; unsigned rem; } uidiv_return;
|
||||
*
|
||||
* __value_in_regs idiv_return __aeabi_idivmod(int numerator,
|
||||
* int *denominator);
|
||||
* __value_in_regs uidiv_return __aeabi_uidivmod(unsigned *numerator,
|
||||
* unsigned denominator);
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* struct qr - stores qutient/remainder to handle divmod EABI interfaces. */
|
||||
struct qr {
|
||||
unsigned q; /* computed quotient */
|
||||
unsigned r; /* computed remainder */
|
||||
unsigned q_n; /* specficies if quotient shall be negative */
|
||||
unsigned r_n; /* specficies if remainder shall be negative */
|
||||
};
|
||||
|
||||
static void uint_div_qr(unsigned numerator, unsigned denominator,
|
||||
struct qr *qr);
|
||||
|
||||
/* returns in R0 and R1 by tail calling an asm function */
|
||||
unsigned __aeabi_uidivmod(unsigned numerator, unsigned denominator);
|
||||
|
||||
unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator);
|
||||
|
||||
/* returns in R0 and R1 by tail calling an asm function */
|
||||
signed __aeabi_idivmod(signed numerator, signed denominator);
|
||||
|
||||
signed __aeabi_idiv(signed numerator, signed denominator);
|
||||
|
||||
/*
|
||||
* __ste_idivmod_ret_t __aeabi_idivmod(signed numerator, signed denominator)
|
||||
* Numerator and Denominator are received in R0 and R1.
|
||||
* Where __ste_idivmod_ret_t is returned in R0 and R1.
|
||||
*
|
||||
* __ste_uidivmod_ret_t __aeabi_uidivmod(unsigned numerator,
|
||||
* unsigned denominator)
|
||||
* Numerator and Denominator are received in R0 and R1.
|
||||
* Where __ste_uidivmod_ret_t is returned in R0 and R1.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
signed ret_idivmod_values(signed quotient, signed remainder);
|
||||
unsigned ret_uidivmod_values(unsigned quotient, unsigned remainder);
|
||||
#else
|
||||
#error "Compiler not supported"
|
||||
#endif
|
||||
|
||||
static void division_qr(unsigned n, unsigned p, struct qr *qr)
|
||||
{
|
||||
unsigned i = 1, q = 0;
|
||||
if (p == 0) {
|
||||
qr->r = 0xFFFFFFFF; /* division by 0 */
|
||||
return;
|
||||
}
|
||||
|
||||
while ((p >> 31) == 0) {
|
||||
i = i << 1; /* count the max division steps */
|
||||
p = p << 1; /* increase p until it has maximum size*/
|
||||
}
|
||||
|
||||
while (i > 0) {
|
||||
q = q << 1; /* write bit in q at index (size-1) */
|
||||
if (n >= p)
|
||||
{
|
||||
n -= p;
|
||||
q++;
|
||||
}
|
||||
p = p >> 1; /* decrease p */
|
||||
i = i >> 1; /* decrease remaining size in q */
|
||||
}
|
||||
qr->r = n;
|
||||
qr->q = q;
|
||||
}
|
||||
|
||||
static void uint_div_qr(unsigned numerator, unsigned denominator, struct qr *qr)
|
||||
{
|
||||
|
||||
division_qr(numerator, denominator, qr);
|
||||
|
||||
/* negate quotient and/or remainder according to requester */
|
||||
if (qr->q_n)
|
||||
qr->q = -qr->q;
|
||||
if (qr->r_n)
|
||||
qr->r = -qr->r;
|
||||
}
|
||||
|
||||
unsigned __aeabi_uidiv(unsigned numerator, unsigned denominator)
|
||||
{
|
||||
struct qr qr = { .q_n = 0, .r_n = 0 };
|
||||
|
||||
uint_div_qr(numerator, denominator, &qr);
|
||||
|
||||
return qr.q;
|
||||
}
|
||||
|
||||
unsigned __aeabi_uidivmod(unsigned numerator, unsigned denominator)
|
||||
{
|
||||
struct qr qr = { .q_n = 0, .r_n = 0 };
|
||||
|
||||
uint_div_qr(numerator, denominator, &qr);
|
||||
|
||||
return ret_uidivmod_values(qr.q, qr.r);
|
||||
}
|
||||
|
||||
signed __aeabi_idiv(signed numerator, signed denominator)
|
||||
{
|
||||
struct qr qr = { .q_n = 0, .r_n = 0 };
|
||||
|
||||
if (((numerator < 0) && (denominator > 0)) ||
|
||||
((numerator > 0) && (denominator < 0)))
|
||||
qr.q_n = 1; /* quotient shall be negate */
|
||||
if (numerator < 0) {
|
||||
numerator = -numerator;
|
||||
qr.r_n = 1; /* remainder shall be negate */
|
||||
}
|
||||
if (denominator < 0)
|
||||
denominator = -denominator;
|
||||
|
||||
uint_div_qr(numerator, denominator, &qr);
|
||||
|
||||
return qr.q;
|
||||
}
|
||||
|
||||
signed __aeabi_idivmod(signed numerator, signed denominator)
|
||||
{
|
||||
struct qr qr = { .q_n = 0, .r_n = 0 };
|
||||
|
||||
if (((numerator < 0) && (denominator > 0)) ||
|
||||
((numerator > 0) && (denominator < 0)))
|
||||
qr.q_n = 1; /* quotient shall be negate */
|
||||
if (numerator < 0) {
|
||||
numerator = -numerator;
|
||||
qr.r_n = 1; /* remainder shall be negate */
|
||||
}
|
||||
if (denominator < 0)
|
||||
denominator = -denominator;
|
||||
|
||||
uint_div_qr(numerator, denominator, &qr);
|
||||
|
||||
return ret_idivmod_values(qr.q, qr.r);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||
/*
|
||||
* Copyright (c) 2014, STMicroelectronics International N.V.
|
||||
*/
|
||||
|
||||
/*
|
||||
* signed ret_idivmod_values(signed quot, signed rem);
|
||||
* return quotient and remaining the EABI way (regs r0,r1)
|
||||
*/
|
||||
.section .text.ret_idivmod_values, "ax", %progbits
|
||||
.globl ret_idivmod_values
|
||||
.align 0
|
||||
.syntax unified
|
||||
ret_idivmod_values:
|
||||
bx lr
|
||||
.type ret_idivmod_values, %function
|
||||
.size ret_idivmod_values, .-ret_idivmod_values
|
||||
|
||||
/*
|
||||
* unsigned ret_uidivmod_values(unsigned quot, unsigned rem);
|
||||
* return quotient and remaining the EABI way (regs r0,r1)
|
||||
*/
|
||||
.section .text.ret_uidivmod_values, "ax", %progbits
|
||||
.globl ret_uidivmod_values
|
||||
.align 0
|
||||
.syntax unified
|
||||
ret_uidivmod_values:
|
||||
bx lr
|
||||
.type ret_uidivmod_values, %function
|
||||
.size ret_uidivmod_values, .-ret_uidivmod_values
|
|
@ -0,0 +1,35 @@
|
|||
/* Copyright (C) 1995-2018 Free Software Foundation, Inc.
|
||||
This file is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 3, or (at your option) any
|
||||
later version.
|
||||
This file is distributed in the hope that 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.
|
||||
Under Section 7 of GPL version 3, you are granted additional
|
||||
permissions described in the GCC Runtime Library Exception, version
|
||||
3.1, as published by the Free Software Foundation.
|
||||
You should have received a copy of the GNU General Public License and
|
||||
a copy of the GCC Runtime Library Exception along with this program;
|
||||
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
.section .text.__gnu_thumb1_case_uqi, "ax", %progbits
|
||||
.globl __gnu_thumb1_case_uqi
|
||||
.align 0
|
||||
.thumb_func
|
||||
.syntax unified
|
||||
__gnu_thumb1_case_uqi:
|
||||
push {r1}
|
||||
mov r1, lr
|
||||
lsrs r1, r1, #1
|
||||
lsls r1, r1, #1
|
||||
ldrb r1, [r1, r0]
|
||||
lsls r1, r1, #1
|
||||
add lr, lr, r1
|
||||
pop {r1}
|
||||
bx lr
|
||||
.type __gnu_thumb1_case_uqi, %function
|
||||
.size __gnu_thumb1_case_uqi, .-__gnu_thumb1_case_uqi
|
|
@ -14,7 +14,6 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <mesosphere.hpp>
|
||||
#include "kern_mc_registers.hpp"
|
||||
|
||||
#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING) || defined(MESOSPHERE_BUILD_FOR_AUDITING)
|
||||
#define MESOSPHERE_ENABLE_MEMORY_CONTROLLER_INTERRUPT
|
||||
|
|
|
@ -78,13 +78,11 @@ namespace ams::kern::board::nintendo::nx {
|
|||
|
||||
void WaitOtherCpuPowerOff() {
|
||||
constexpr u64 PmcPhysicalAddress = 0x7000E400;
|
||||
constexpr u64 APBDEV_PMC_PWRGATE_STATUS = PmcPhysicalAddress + 0x38;
|
||||
|
||||
constexpr u32 PWRGATE_STATUS_CE123_MASK = ((1u << 3) - 1) << 9;
|
||||
|
||||
u32 value;
|
||||
do {
|
||||
bool res = smc::ReadWriteRegister(std::addressof(value), APBDEV_PMC_PWRGATE_STATUS, 0, 0);
|
||||
bool res = smc::ReadWriteRegister(std::addressof(value), PmcPhysicalAddress + APBDEV_PMC_PWRGATE_STATUS, 0, 0);
|
||||
MESOSPHERE_ASSERT(res);
|
||||
} while ((value & PWRGATE_STATUS_CE123_MASK) != 0);
|
||||
}
|
||||
|
|
|
@ -19,11 +19,7 @@
|
|||
|
||||
#include "kern_bpmp_api.hpp"
|
||||
#include "kern_atomics_registers.hpp"
|
||||
#include "kern_clkrst_registers.hpp"
|
||||
#include "kern_evp_registers.hpp"
|
||||
#include "kern_flow_registers.hpp"
|
||||
#include "kern_ictlr_registers.hpp"
|
||||
#include "kern_pmc_registers.hpp"
|
||||
#include "kern_sema_registers.hpp"
|
||||
|
||||
namespace ams::kern::board::nintendo::nx::lps {
|
||||
|
|
|
@ -1,526 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define MC_INTSTATUS 0x0
|
||||
#define MC_INTMASK 0x4
|
||||
#define MC_ERR_STATUS 0x8
|
||||
#define MC_ERR_ADR 0xc
|
||||
#define MC_SMMU_CONFIG 0x10
|
||||
#define MC_SMMU_TLB_CONFIG 0x14
|
||||
#define MC_SMMU_PTC_CONFIG 0x18
|
||||
#define MC_SMMU_PTB_ASID 0x1c
|
||||
#define MC_SMMU_PTB_DATA 0x20
|
||||
#define MC_SMMU_TLB_FLUSH 0x30
|
||||
#define MC_SMMU_PTC_FLUSH_0 0x34
|
||||
#define MC_SMMU_PTC_FLUSH_1 0x9b8
|
||||
#define MC_SMMU_ASID_SECURITY 0x38
|
||||
#define MC_SMMU_ASID_SECURITY_1 0x3c
|
||||
#define MC_SMMU_ASID_SECURITY_2 0x9e0
|
||||
#define MC_SMMU_ASID_SECURITY_3 0x9e4
|
||||
#define MC_SMMU_ASID_SECURITY_4 0x9e8
|
||||
#define MC_SMMU_ASID_SECURITY_5 0x9ec
|
||||
#define MC_SMMU_ASID_SECURITY_6 0x9f0
|
||||
#define MC_SMMU_ASID_SECURITY_7 0x9f4
|
||||
#define MC_SMMU_AFI_ASID 0x238
|
||||
#define MC_SMMU_AVPC_ASID 0x23c
|
||||
#define MC_SMMU_DC_ASID 0x240
|
||||
#define MC_SMMU_DCB_ASID 0x244
|
||||
#define MC_SMMU_HC_ASID 0x250
|
||||
#define MC_SMMU_HDA_ASID 0x254
|
||||
#define MC_SMMU_ISP2_ASID 0x258
|
||||
#define MC_SMMU_MSENC_NVENC_ASID 0x264
|
||||
#define MC_SMMU_NV_ASID 0x268
|
||||
#define MC_SMMU_NV2_ASID 0x26c
|
||||
#define MC_SMMU_PPCS_ASID 0x270
|
||||
#define MC_SMMU_SATA_ASID 0x274
|
||||
#define MC_SMMU_VDE_ASID 0x27c
|
||||
#define MC_SMMU_VI_ASID 0x280
|
||||
#define MC_SMMU_VIC_ASID 0x284
|
||||
#define MC_SMMU_XUSB_HOST_ASID 0x288
|
||||
#define MC_SMMU_XUSB_DEV_ASID 0x28c
|
||||
#define MC_SMMU_TSEC_ASID 0x294
|
||||
#define MC_SMMU_PPCS1_ASID 0x298
|
||||
#define MC_SMMU_DC1_ASID 0xa88
|
||||
#define MC_SMMU_SDMMC1A_ASID 0xa94
|
||||
#define MC_SMMU_SDMMC2A_ASID 0xa98
|
||||
#define MC_SMMU_SDMMC3A_ASID 0xa9c
|
||||
#define MC_SMMU_SDMMC4A_ASID 0xaa0
|
||||
#define MC_SMMU_ISP2B_ASID 0xaa4
|
||||
#define MC_SMMU_GPU_ASID 0xaa8
|
||||
#define MC_SMMU_GPUB_ASID 0xaac
|
||||
#define MC_SMMU_PPCS2_ASID 0xab0
|
||||
#define MC_SMMU_NVDEC_ASID 0xab4
|
||||
#define MC_SMMU_APE_ASID 0xab8
|
||||
#define MC_SMMU_SE_ASID 0xabc
|
||||
#define MC_SMMU_NVJPG_ASID 0xac0
|
||||
#define MC_SMMU_HC1_ASID 0xac4
|
||||
#define MC_SMMU_SE1_ASID 0xac8
|
||||
#define MC_SMMU_AXIAP_ASID 0xacc
|
||||
#define MC_SMMU_ETR_ASID 0xad0
|
||||
#define MC_SMMU_TSECB_ASID 0xad4
|
||||
#define MC_SMMU_TSEC1_ASID 0xad8
|
||||
#define MC_SMMU_TSECB1_ASID 0xadc
|
||||
#define MC_SMMU_NVDEC1_ASID 0xae0
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_0 0x228
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_1 0x22c
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_2 0x230
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_3 0x234
|
||||
#define MC_SMMU_TRANSLATION_ENABLE_4 0xb98
|
||||
#define MC_PCFIFO_CLIENT_CONFIG0 0xdd0
|
||||
#define MC_PCFIFO_CLIENT_CONFIG1 0xdd4
|
||||
#define MC_PCFIFO_CLIENT_CONFIG2 0xdd8
|
||||
#define MC_PCFIFO_CLIENT_CONFIG3 0xddc
|
||||
#define MC_PCFIFO_CLIENT_CONFIG4 0xde0
|
||||
#define MC_EMEM_CFG 0x50
|
||||
#define MC_EMEM_ADR_CFG 0x54
|
||||
#define MC_EMEM_ADR_CFG_DEV0 0x58
|
||||
#define MC_EMEM_ADR_CFG_DEV1 0x5c
|
||||
#define MC_EMEM_ADR_CFG_CHANNEL_MASK 0x60
|
||||
#define MC_EMEM_ADR_CFG_BANK_MASK_0 0x64
|
||||
#define MC_EMEM_ADR_CFG_BANK_MASK_1 0x68
|
||||
#define MC_EMEM_ADR_CFG_BANK_MASK_2 0x6c
|
||||
#define MC_SECURITY_CFG0 0x70
|
||||
#define MC_SECURITY_CFG1 0x74
|
||||
#define MC_SECURITY_CFG3 0x9bc
|
||||
#define MC_SECURITY_RSV 0x7c
|
||||
#define MC_EMEM_ARB_CFG 0x90
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
|
||||
#define MC_EMEM_ARB_TIMING_RCD 0x98
|
||||
#define MC_EMEM_ARB_TIMING_RP 0x9c
|
||||
#define MC_EMEM_ARB_TIMING_RC 0xa0
|
||||
#define MC_EMEM_ARB_TIMING_RAS 0xa4
|
||||
#define MC_EMEM_ARB_TIMING_FAW 0xa8
|
||||
#define MC_EMEM_ARB_TIMING_RRD 0xac
|
||||
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
|
||||
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
|
||||
#define MC_EMEM_ARB_TIMING_R2R 0xb8
|
||||
#define MC_EMEM_ARB_TIMING_W2W 0xbc
|
||||
#define MC_EMEM_ARB_TIMING_R2W 0xc0
|
||||
#define MC_EMEM_ARB_TIMING_W2R 0xc4
|
||||
#define MC_EMEM_ARB_TIMING_RFCPB 0x6c0
|
||||
#define MC_EMEM_ARB_TIMING_CCDMW 0x6c4
|
||||
#define MC_EMEM_ARB_REFPB_HP_CTRL 0x6f0
|
||||
#define MC_EMEM_ARB_REFPB_BANK_CTRL 0x6f4
|
||||
#define MC_EMEM_ARB_DA_TURNS 0xd0
|
||||
#define MC_EMEM_ARB_DA_COVERS 0xd4
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
#define MC_EMEM_ARB_MISC1 0xdc
|
||||
#define MC_EMEM_ARB_MISC2 0xc8
|
||||
#define MC_EMEM_ARB_RING1_THROTTLE 0xe0
|
||||
#define MC_EMEM_ARB_RING3_THROTTLE 0xe4
|
||||
#define MC_EMEM_ARB_NISO_THROTTLE 0x6b0
|
||||
#define MC_EMEM_ARB_OVERRIDE 0xe8
|
||||
#define MC_EMEM_ARB_RSV 0xec
|
||||
#define MC_CLKEN_OVERRIDE 0xf4
|
||||
#define MC_TIMING_CONTROL_DBG 0xf8
|
||||
#define MC_TIMING_CONTROL 0xfc
|
||||
#define MC_STAT_CONTROL 0x100
|
||||
#define MC_STAT_STATUS 0x104
|
||||
#define MC_STAT_EMC_CLOCK_LIMIT 0x108
|
||||
#define MC_STAT_EMC_CLOCK_LIMIT_MSBS 0x10c
|
||||
#define MC_STAT_EMC_CLOCKS 0x110
|
||||
#define MC_STAT_EMC_CLOCKS_MSBS 0x114
|
||||
#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO 0x118
|
||||
#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO 0x158
|
||||
#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI 0x11c
|
||||
#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI 0x15c
|
||||
#define MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER 0xa20
|
||||
#define MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER 0xa24
|
||||
#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_LO 0x198
|
||||
#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_LO 0x1a8
|
||||
#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_HI 0x19c
|
||||
#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_HI 0x1ac
|
||||
#define MC_STAT_EMC_FILTER_SET0_VIRTUAL_ADR_LIMIT_UPPER 0xa28
|
||||
#define MC_STAT_EMC_FILTER_SET1_VIRTUAL_ADR_LIMIT_UPPER 0xa2c
|
||||
#define MC_STAT_EMC_FILTER_SET0_ASID 0x1a0
|
||||
#define MC_STAT_EMC_FILTER_SET1_ASID 0x1b0
|
||||
#define MC_STAT_EMC_FILTER_SET0_SLACK_LIMIT 0x120
|
||||
#define MC_STAT_EMC_FILTER_SET1_SLACK_LIMIT 0x160
|
||||
#define MC_STAT_EMC_FILTER_SET0_CLIENT_0 0x128
|
||||
#define MC_STAT_EMC_FILTER_SET1_CLIENT_0 0x168
|
||||
#define MC_STAT_EMC_FILTER_SET0_CLIENT_1 0x12c
|
||||
#define MC_STAT_EMC_FILTER_SET1_CLIENT_1 0x16c
|
||||
#define MC_STAT_EMC_FILTER_SET0_CLIENT_2 0x130
|
||||
#define MC_STAT_EMC_FILTER_SET1_CLIENT_2 0x170
|
||||
#define MC_STAT_EMC_FILTER_SET0_CLIENT_3 0x134
|
||||
#define MC_STAT_EMC_FILTER_SET0_CLIENT_4 0xb88
|
||||
#define MC_STAT_EMC_FILTER_SET1_CLIENT_3 0x174
|
||||
#define MC_STAT_EMC_FILTER_SET1_CLIENT_4 0xb8c
|
||||
#define MC_STAT_EMC_SET0_COUNT 0x138
|
||||
#define MC_STAT_EMC_SET0_COUNT_MSBS 0x13c
|
||||
#define MC_STAT_EMC_SET1_COUNT 0x178
|
||||
#define MC_STAT_EMC_SET1_COUNT_MSBS 0x17c
|
||||
#define MC_STAT_EMC_SET0_SLACK_ACCUM 0x140
|
||||
#define MC_STAT_EMC_SET0_SLACK_ACCUM_MSBS 0x144
|
||||
#define MC_STAT_EMC_SET1_SLACK_ACCUM 0x180
|
||||
#define MC_STAT_EMC_SET1_SLACK_ACCUM_MSBS 0x184
|
||||
#define MC_STAT_EMC_SET0_HISTO_COUNT 0x148
|
||||
#define MC_STAT_EMC_SET0_HISTO_COUNT_MSBS 0x14c
|
||||
#define MC_STAT_EMC_SET1_HISTO_COUNT 0x188
|
||||
#define MC_STAT_EMC_SET1_HISTO_COUNT_MSBS 0x18c
|
||||
#define MC_STAT_EMC_SET0_MINIMUM_SLACK_OBSERVED 0x150
|
||||
#define MC_STAT_EMC_SET1_MINIMUM_SLACK_OBSERVED 0x190
|
||||
#define MC_STAT_EMC_SET0_IDLE_CYCLE_COUNT 0x1b8
|
||||
#define MC_STAT_EMC_SET0_IDLE_CYCL_COUNT_MSBS 0x1bc
|
||||
#define MC_STAT_EMC_SET1_IDLE_CYCLE_COUNT 0x1c8
|
||||
#define MC_STAT_EMC_SET1_IDLE_CYCL_COUNT_MSBS 0x1cc
|
||||
#define MC_STAT_EMC_SET0_IDLE_CYCLE_PARTITION_SELECT 0x1c0
|
||||
#define MC_STAT_EMC_SET1_IDLE_CYCLE_PARTITION_SELECT 0x1d0
|
||||
#define MC_CLIENT_HOTRESET_CTRL 0x200
|
||||
#define MC_CLIENT_HOTRESET_CTRL_1 0x970
|
||||
#define MC_CLIENT_HOTRESET_STATUS 0x204
|
||||
#define MC_CLIENT_HOTRESET_STATUS_1 0x974
|
||||
#define MC_EMEM_ARB_ISOCHRONOUS_0 0x208
|
||||
#define MC_EMEM_ARB_ISOCHRONOUS_1 0x20c
|
||||
#define MC_EMEM_ARB_ISOCHRONOUS_2 0x210
|
||||
#define MC_EMEM_ARB_ISOCHRONOUS_3 0x214
|
||||
#define MC_EMEM_ARB_ISOCHRONOUS_4 0xb94
|
||||
#define MC_EMEM_ARB_HYSTERESIS_0 0x218
|
||||
#define MC_EMEM_ARB_HYSTERESIS_1 0x21c
|
||||
#define MC_EMEM_ARB_HYSTERESIS_2 0x220
|
||||
#define MC_EMEM_ARB_HYSTERESIS_3 0x224
|
||||
#define MC_EMEM_ARB_HYSTERESIS_4 0xb84
|
||||
#define MC_EMEM_ARB_DHYSTERESIS_0 0xbb0
|
||||
#define MC_EMEM_ARB_DHYSTERESIS_1 0xbb4
|
||||
#define MC_EMEM_ARB_DHYSTERESIS_2 0xbb8
|
||||
#define MC_EMEM_ARB_DHYSTERESIS_3 0xbbc
|
||||
#define MC_EMEM_ARB_DHYSTERESIS_4 0xbc0
|
||||
#define MC_EMEM_ARB_DHYST_CTRL 0xbcc
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0 0xbd0
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1 0xbd4
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2 0xbd8
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3 0xbdc
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4 0xbe0
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5 0xbe4
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6 0xbe8
|
||||
#define MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7 0xbec
|
||||
#define MC_RESERVED_RSV 0x3fc
|
||||
#define MC_DISB_EXTRA_SNAP_LEVELS 0x408
|
||||
#define MC_APB_EXTRA_SNAP_LEVELS 0x2a4
|
||||
#define MC_AHB_EXTRA_SNAP_LEVELS 0x2a0
|
||||
#define MC_USBD_EXTRA_SNAP_LEVELS 0xa18
|
||||
#define MC_ISP_EXTRA_SNAP_LEVELS 0xa08
|
||||
#define MC_AUD_EXTRA_SNAP_LEVELS 0xa10
|
||||
#define MC_MSE_EXTRA_SNAP_LEVELS 0x40c
|
||||
#define MC_GK2_EXTRA_SNAP_LEVELS 0xa40
|
||||
#define MC_A9AVPPC_EXTRA_SNAP_LEVELS 0x414
|
||||
#define MC_FTOP_EXTRA_SNAP_LEVELS 0x2bc
|
||||
#define MC_JPG_EXTRA_SNAP_LEVELS 0xa3c
|
||||
#define MC_HOST_EXTRA_SNAP_LEVELS 0xa14
|
||||
#define MC_SAX_EXTRA_SNAP_LEVELS 0x2c0
|
||||
#define MC_DIS_EXTRA_SNAP_LEVELS 0x2ac
|
||||
#define MC_VICPC_EXTRA_SNAP_LEVELS 0xa1c
|
||||
#define MC_HDAPC_EXTRA_SNAP_LEVELS 0xa48
|
||||
#define MC_AVP_EXTRA_SNAP_LEVELS 0x2a8
|
||||
#define MC_USBX_EXTRA_SNAP_LEVELS 0x404
|
||||
#define MC_PCX_EXTRA_SNAP_LEVELS 0x2b8
|
||||
#define MC_SD_EXTRA_SNAP_LEVELS 0xa04
|
||||
#define MC_DFD_EXTRA_SNAP_LEVELS 0xa4c
|
||||
#define MC_VE_EXTRA_SNAP_LEVELS 0x2d8
|
||||
#define MC_GK_EXTRA_SNAP_LEVELS 0xa00
|
||||
#define MC_VE2_EXTRA_SNAP_LEVELS 0x410
|
||||
#define MC_SDM_EXTRA_SNAP_LEVELS 0xa44
|
||||
#define MC_VIDEO_PROTECT_BOM 0x648
|
||||
#define MC_VIDEO_PROTECT_SIZE_MB 0x64c
|
||||
#define MC_VIDEO_PROTECT_BOM_ADR_HI 0x978
|
||||
#define MC_VIDEO_PROTECT_REG_CTRL 0x650
|
||||
#define MC_ERR_VPR_STATUS 0x654
|
||||
#define MC_ERR_VPR_ADR 0x658
|
||||
#define MC_VIDEO_PROTECT_VPR_OVERRIDE 0x418
|
||||
#define MC_VIDEO_PROTECT_VPR_OVERRIDE1 0x590
|
||||
#define MC_IRAM_BOM 0x65c
|
||||
#define MC_IRAM_TOM 0x660
|
||||
#define MC_IRAM_ADR_HI 0x980
|
||||
#define MC_IRAM_REG_CTRL 0x964
|
||||
#define MC_EMEM_CFG_ACCESS_CTRL 0x664
|
||||
#define MC_TZ_SECURITY_CTRL 0x668
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ_RING3 0x66c
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ_NISO 0x6b4
|
||||
#define MC_EMEM_ARB_RING0_THROTTLE_MASK 0x6bc
|
||||
#define MC_EMEM_ARB_NISO_THROTTLE_MASK 0x6b8
|
||||
#define MC_EMEM_ARB_NISO_THROTTLE_MASK_1 0xb80
|
||||
#define MC_SEC_CARVEOUT_BOM 0x670
|
||||
#define MC_SEC_CARVEOUT_SIZE_MB 0x674
|
||||
#define MC_SEC_CARVEOUT_ADR_HI 0x9d4
|
||||
#define MC_SEC_CARVEOUT_REG_CTRL 0x678
|
||||
#define MC_ERR_SEC_STATUS 0x67c
|
||||
#define MC_ERR_SEC_ADR 0x680
|
||||
#define MC_PC_IDLE_CLOCK_GATE_CONFIG 0x684
|
||||
#define MC_STUTTER_CONTROL 0x688
|
||||
#define MC_RESERVED_RSV_1 0x958
|
||||
#define MC_DVFS_PIPE_SELECT 0x95c
|
||||
#define MC_AHB_PTSA_MIN 0x4e0
|
||||
#define MC_AUD_PTSA_MIN 0x54c
|
||||
#define MC_MLL_MPCORER_PTSA_RATE 0x44c
|
||||
#define MC_RING2_PTSA_RATE 0x440
|
||||
#define MC_USBD_PTSA_RATE 0x530
|
||||
#define MC_USBX_PTSA_MIN 0x528
|
||||
#define MC_USBD_PTSA_MIN 0x534
|
||||
#define MC_APB_PTSA_MAX 0x4f0
|
||||
#define MC_JPG_PTSA_RATE 0x584
|
||||
#define MC_DIS_PTSA_MIN 0x420
|
||||
#define MC_AVP_PTSA_MAX 0x4fc
|
||||
#define MC_AVP_PTSA_RATE 0x4f4
|
||||
#define MC_RING1_PTSA_MIN 0x480
|
||||
#define MC_DIS_PTSA_MAX 0x424
|
||||
#define MC_SD_PTSA_MAX 0x4d8
|
||||
#define MC_MSE_PTSA_RATE 0x4c4
|
||||
#define MC_VICPC_PTSA_MIN 0x558
|
||||
#define MC_PCX_PTSA_MAX 0x4b4
|
||||
#define MC_ISP_PTSA_RATE 0x4a0
|
||||
#define MC_A9AVPPC_PTSA_MIN 0x48c
|
||||
#define MC_RING2_PTSA_MAX 0x448
|
||||
#define MC_AUD_PTSA_RATE 0x548
|
||||
#define MC_HOST_PTSA_MIN 0x51c
|
||||
#define MC_MLL_MPCORER_PTSA_MAX 0x454
|
||||
#define MC_SD_PTSA_MIN 0x4d4
|
||||
#define MC_RING1_PTSA_RATE 0x47c
|
||||
#define MC_JPG_PTSA_MIN 0x588
|
||||
#define MC_HDAPC_PTSA_MIN 0x62c
|
||||
#define MC_AVP_PTSA_MIN 0x4f8
|
||||
#define MC_JPG_PTSA_MAX 0x58c
|
||||
#define MC_VE_PTSA_MAX 0x43c
|
||||
#define MC_DFD_PTSA_MAX 0x63c
|
||||
#define MC_VICPC_PTSA_RATE 0x554
|
||||
#define MC_GK_PTSA_MAX 0x544
|
||||
#define MC_VICPC_PTSA_MAX 0x55c
|
||||
#define MC_SDM_PTSA_MAX 0x624
|
||||
#define MC_SAX_PTSA_RATE 0x4b8
|
||||
#define MC_PCX_PTSA_MIN 0x4b0
|
||||
#define MC_APB_PTSA_MIN 0x4ec
|
||||
#define MC_GK2_PTSA_MIN 0x614
|
||||
#define MC_PCX_PTSA_RATE 0x4ac
|
||||
#define MC_RING1_PTSA_MAX 0x484
|
||||
#define MC_HDAPC_PTSA_RATE 0x628
|
||||
#define MC_MLL_MPCORER_PTSA_MIN 0x450
|
||||
#define MC_GK2_PTSA_MAX 0x618
|
||||
#define MC_AUD_PTSA_MAX 0x550
|
||||
#define MC_GK2_PTSA_RATE 0x610
|
||||
#define MC_ISP_PTSA_MAX 0x4a8
|
||||
#define MC_DISB_PTSA_RATE 0x428
|
||||
#define MC_VE2_PTSA_MAX 0x49c
|
||||
#define MC_DFD_PTSA_MIN 0x638
|
||||
#define MC_FTOP_PTSA_RATE 0x50c
|
||||
#define MC_A9AVPPC_PTSA_RATE 0x488
|
||||
#define MC_VE2_PTSA_MIN 0x498
|
||||
#define MC_USBX_PTSA_MAX 0x52c
|
||||
#define MC_DIS_PTSA_RATE 0x41c
|
||||
#define MC_USBD_PTSA_MAX 0x538
|
||||
#define MC_A9AVPPC_PTSA_MAX 0x490
|
||||
#define MC_USBX_PTSA_RATE 0x524
|
||||
#define MC_FTOP_PTSA_MAX 0x514
|
||||
#define MC_HDAPC_PTSA_MAX 0x630
|
||||
#define MC_SD_PTSA_RATE 0x4d0
|
||||
#define MC_DFD_PTSA_RATE 0x634
|
||||
#define MC_FTOP_PTSA_MIN 0x510
|
||||
#define MC_SDM_PTSA_RATE 0x61c
|
||||
#define MC_AHB_PTSA_RATE 0x4dc
|
||||
#define MC_SMMU_SMMU_PTSA_MAX 0x460
|
||||
#define MC_RING2_PTSA_MIN 0x444
|
||||
#define MC_SDM_PTSA_MIN 0x620
|
||||
#define MC_APB_PTSA_RATE 0x4e8
|
||||
#define MC_MSE_PTSA_MIN 0x4c8
|
||||
#define MC_HOST_PTSA_RATE 0x518
|
||||
#define MC_VE_PTSA_RATE 0x434
|
||||
#define MC_AHB_PTSA_MAX 0x4e4
|
||||
#define MC_SAX_PTSA_MIN 0x4bc
|
||||
#define MC_SMMU_SMMU_PTSA_MIN 0x45c
|
||||
#define MC_ISP_PTSA_MIN 0x4a4
|
||||
#define MC_HOST_PTSA_MAX 0x520
|
||||
#define MC_SAX_PTSA_MAX 0x4c0
|
||||
#define MC_VE_PTSA_MIN 0x438
|
||||
#define MC_GK_PTSA_MIN 0x540
|
||||
#define MC_MSE_PTSA_MAX 0x4cc
|
||||
#define MC_DISB_PTSA_MAX 0x430
|
||||
#define MC_DISB_PTSA_MIN 0x42c
|
||||
#define MC_SMMU_SMMU_PTSA_RATE 0x458
|
||||
#define MC_VE2_PTSA_RATE 0x494
|
||||
#define MC_GK_PTSA_RATE 0x53c
|
||||
#define MC_PTSA_GRANT_DECREMENT 0x960
|
||||
#define MC_LATENCY_ALLOWANCE_AVPC_0 0x2e4
|
||||
#define MC_LATENCY_ALLOWANCE_AXIAP_0 0x3a0
|
||||
#define MC_LATENCY_ALLOWANCE_XUSB_1 0x380
|
||||
#define MC_LATENCY_ALLOWANCE_ISP2B_0 0x384
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCAA_0 0x3bc
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCA_0 0x3b8
|
||||
#define MC_LATENCY_ALLOWANCE_ISP2_0 0x370
|
||||
#define MC_LATENCY_ALLOWANCE_SE_0 0x3e0
|
||||
#define MC_LATENCY_ALLOWANCE_ISP2_1 0x374
|
||||
#define MC_LATENCY_ALLOWANCE_DC_0 0x2e8
|
||||
#define MC_LATENCY_ALLOWANCE_VIC_0 0x394
|
||||
#define MC_LATENCY_ALLOWANCE_DCB_1 0x2f8
|
||||
#define MC_LATENCY_ALLOWANCE_NVDEC_0 0x3d8
|
||||
#define MC_LATENCY_ALLOWANCE_DCB_2 0x2fc
|
||||
#define MC_LATENCY_ALLOWANCE_TSEC_0 0x390
|
||||
#define MC_LATENCY_ALLOWANCE_DC_2 0x2f0
|
||||
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB 0x694
|
||||
#define MC_LATENCY_ALLOWANCE_PPCS_1 0x348
|
||||
#define MC_LATENCY_ALLOWANCE_XUSB_0 0x37c
|
||||
#define MC_LATENCY_ALLOWANCE_PPCS_0 0x344
|
||||
#define MC_LATENCY_ALLOWANCE_TSECB_0 0x3f0
|
||||
#define MC_LATENCY_ALLOWANCE_AFI_0 0x2e0
|
||||
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B 0x698
|
||||
#define MC_LATENCY_ALLOWANCE_DC_1 0x2ec
|
||||
#define MC_LATENCY_ALLOWANCE_APE_0 0x3dc
|
||||
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C 0x6a0
|
||||
#define MC_LATENCY_ALLOWANCE_A9AVP_0 0x3a4
|
||||
#define MC_LATENCY_ALLOWANCE_GPU2_0 0x3e8
|
||||
#define MC_LATENCY_ALLOWANCE_DCB_0 0x2f4
|
||||
#define MC_LATENCY_ALLOWANCE_HC_1 0x314
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMC_0 0x3c0
|
||||
#define MC_LATENCY_ALLOWANCE_NVJPG_0 0x3e4
|
||||
#define MC_LATENCY_ALLOWANCE_PTC_0 0x34c
|
||||
#define MC_LATENCY_ALLOWANCE_ETR_0 0x3ec
|
||||
#define MC_LATENCY_ALLOWANCE_MPCORE_0 0x320
|
||||
#define MC_LATENCY_ALLOWANCE_VI2_0 0x398
|
||||
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB 0x69c
|
||||
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB 0x6a4
|
||||
#define MC_LATENCY_ALLOWANCE_SATA_0 0x350
|
||||
#define MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A 0x690
|
||||
#define MC_LATENCY_ALLOWANCE_HC_0 0x310
|
||||
#define MC_LATENCY_ALLOWANCE_DC_3 0x3c8
|
||||
#define MC_LATENCY_ALLOWANCE_GPU_0 0x3ac
|
||||
#define MC_LATENCY_ALLOWANCE_SDMMCAB_0 0x3c4
|
||||
#define MC_LATENCY_ALLOWANCE_ISP2B_1 0x388
|
||||
#define MC_LATENCY_ALLOWANCE_NVENC_0 0x328
|
||||
#define MC_LATENCY_ALLOWANCE_HDA_0 0x318
|
||||
#define MC_MIN_LENGTH_APE_0 0xb34
|
||||
#define MC_MIN_LENGTH_DCB_2 0x8a8
|
||||
#define MC_MIN_LENGTH_A9AVP_0 0x950
|
||||
#define MC_MIN_LENGTH_TSEC_0 0x93c
|
||||
#define MC_MIN_LENGTH_DC_1 0x898
|
||||
#define MC_MIN_LENGTH_AXIAP_0 0x94c
|
||||
#define MC_MIN_LENGTH_ISP2B_0 0x930
|
||||
#define MC_MIN_LENGTH_VI2_0 0x944
|
||||
#define MC_MIN_LENGTH_DCB_0 0x8a0
|
||||
#define MC_MIN_LENGTH_DCB_1 0x8a4
|
||||
#define MC_MIN_LENGTH_PPCS_1 0x8f4
|
||||
#define MC_MIN_LENGTH_NVJPG_0 0xb3c
|
||||
#define MC_MIN_LENGTH_HDA_0 0x8c4
|
||||
#define MC_MIN_LENGTH_NVENC_0 0x8d4
|
||||
#define MC_MIN_LENGTH_SDMMC_0 0xb18
|
||||
#define MC_MIN_LENGTH_ISP2B_1 0x934
|
||||
#define MC_MIN_LENGTH_HC_1 0x8c0
|
||||
#define MC_MIN_LENGTH_DC_3 0xb20
|
||||
#define MC_MIN_LENGTH_AVPC_0 0x890
|
||||
#define MC_MIN_LENGTH_VIC_0 0x940
|
||||
#define MC_MIN_LENGTH_ISP2_0 0x91c
|
||||
#define MC_MIN_LENGTH_HC_0 0x8bc
|
||||
#define MC_MIN_LENGTH_SE_0 0xb38
|
||||
#define MC_MIN_LENGTH_NVDEC_0 0xb30
|
||||
#define MC_MIN_LENGTH_SATA_0 0x8fc
|
||||
#define MC_MIN_LENGTH_DC_0 0x894
|
||||
#define MC_MIN_LENGTH_XUSB_1 0x92c
|
||||
#define MC_MIN_LENGTH_DC_2 0x89c
|
||||
#define MC_MIN_LENGTH_SDMMCAA_0 0xb14
|
||||
#define MC_MIN_LENGTH_GPU_0 0xb04
|
||||
#define MC_MIN_LENGTH_ETR_0 0xb44
|
||||
#define MC_MIN_LENGTH_AFI_0 0x88c
|
||||
#define MC_MIN_LENGTH_PPCS_0 0x8f0
|
||||
#define MC_MIN_LENGTH_ISP2_1 0x920
|
||||
#define MC_MIN_LENGTH_XUSB_0 0x928
|
||||
#define MC_MIN_LENGTH_MPCORE_0 0x8cc
|
||||
#define MC_MIN_LENGTH_TSECB_0 0xb48
|
||||
#define MC_MIN_LENGTH_SDMMCA_0 0xb10
|
||||
#define MC_MIN_LENGTH_GPU2_0 0xb40
|
||||
#define MC_MIN_LENGTH_SDMMCAB_0 0xb1c
|
||||
#define MC_MIN_LENGTH_PTC_0 0x8f8
|
||||
#define MC_EMEM_ARB_OVERRIDE_1 0x968
|
||||
#define MC_VIDEO_PROTECT_GPU_OVERRIDE_0 0x984
|
||||
#define MC_VIDEO_PROTECT_GPU_OVERRIDE_1 0x988
|
||||
#define MC_EMEM_ARB_STATS_0 0x990
|
||||
#define MC_EMEM_ARB_STATS_1 0x994
|
||||
#define MC_MTS_CARVEOUT_BOM 0x9a0
|
||||
#define MC_MTS_CARVEOUT_SIZE_MB 0x9a4
|
||||
#define MC_MTS_CARVEOUT_ADR_HI 0x9a8
|
||||
#define MC_MTS_CARVEOUT_REG_CTRL 0x9ac
|
||||
#define MC_ERR_MTS_STATUS 0x9b0
|
||||
#define MC_ERR_MTS_ADR 0x9b4
|
||||
#define MC_ERR_GENERALIZED_CARVEOUT_STATUS 0xc00
|
||||
#define MC_ERR_GENERALIZED_CARVEOUT_ADR 0xc04
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS2 0xd74
|
||||
#define MC_SECURITY_CARVEOUT4_CFG0 0xcf8
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS2 0xd10
|
||||
#define MC_SECURITY_CARVEOUT4_SIZE_128KB 0xd04
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS4 0xc28
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS1 0xc30
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS4 0xc8c
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS0 0xd1c
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS1 0xd70
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS0 0xc2c
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS4 0xd7c
|
||||
#define MC_SECURITY_CARVEOUT3_SIZE_128KB 0xcb4
|
||||
#define MC_SECURITY_CARVEOUT2_CFG0 0xc58
|
||||
#define MC_SECURITY_CARVEOUT1_CFG0 0xc08
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS2 0xc84
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS0 0xc68
|
||||
#define MC_SECURITY_CARVEOUT3_BOM 0xcac
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS2 0xc70
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS3 0xd78
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS0 0xc7c
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS4 0xd18
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS1 0xcbc
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS3 0xc38
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS2 0xc34
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS2 0xcc0
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS2 0xd60
|
||||
#define MC_SECURITY_CARVEOUT3_CFG0 0xca8
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS0 0xcb8
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS3 0xc88
|
||||
#define MC_SECURITY_CARVEOUT2_SIZE_128KB 0xc64
|
||||
#define MC_SECURITY_CARVEOUT5_BOM_HI 0xd50
|
||||
#define MC_SECURITY_CARVEOUT1_SIZE_128KB 0xc14
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS3 0xd14
|
||||
#define MC_SECURITY_CARVEOUT1_BOM 0xc0c
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS4 0xd2c
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS4 0xd68
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS4 0xcc8
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS0 0xd58
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS2 0xd24
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_ACCESS3 0xcc4
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS4 0xc78
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS1 0xc1c
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS0 0xc18
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS3 0xd28
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS1 0xd5c
|
||||
#define MC_SECURITY_CARVEOUT3_BOM_HI 0xcb0
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS3 0xcd8
|
||||
#define MC_SECURITY_CARVEOUT2_BOM_HI 0xc60
|
||||
#define MC_SECURITY_CARVEOUT4_BOM_HI 0xd00
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_ACCESS3 0xd64
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS4 0xcdc
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_FORCE_INTERNAL_ACCESS1 0xc80
|
||||
#define MC_SECURITY_CARVEOUT5_SIZE_128KB 0xd54
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_FORCE_INTERNAL_ACCESS1 0xd20
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS2 0xcd4
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS1 0xd0c
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS3 0xc74
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS0 0xccc
|
||||
#define MC_SECURITY_CARVEOUT4_BOM 0xcfc
|
||||
#define MC_SECURITY_CARVEOUT5_CFG0 0xd48
|
||||
#define MC_SECURITY_CARVEOUT2_BOM 0xc5c
|
||||
#define MC_SECURITY_CARVEOUT5_BOM 0xd4c
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS3 0xc24
|
||||
#define MC_SECURITY_CARVEOUT5_CLIENT_FORCE_INTERNAL_ACCESS0 0xd6c
|
||||
#define MC_SECURITY_CARVEOUT3_CLIENT_FORCE_INTERNAL_ACCESS1 0xcd0
|
||||
#define MC_SECURITY_CARVEOUT1_BOM_HI 0xc10
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_ACCESS2 0xc20
|
||||
#define MC_SECURITY_CARVEOUT1_CLIENT_FORCE_INTERNAL_ACCESS4 0xc3c
|
||||
#define MC_SECURITY_CARVEOUT2_CLIENT_ACCESS1 0xc6c
|
||||
#define MC_SECURITY_CARVEOUT4_CLIENT_ACCESS0 0xd08
|
||||
#define MC_ERR_APB_ASID_UPDATE_STATUS 0x9d0
|
||||
#define MC_DA_CONFIG0 0x9dc
|
|
@ -17,386 +17,9 @@
|
|||
#include "kern_debug_log_impl.hpp"
|
||||
|
||||
namespace ams::kern {
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("-Os")
|
||||
|
||||
namespace {
|
||||
|
||||
/* Useful definitions for our VSNPrintf implementation. */
|
||||
enum FormatSpecifierFlag : u32 {
|
||||
FormatSpecifierFlag_None = 0,
|
||||
FormatSpecifierFlag_EmptySign = (1 << 0),
|
||||
FormatSpecifierFlag_ForceSign = (1 << 1),
|
||||
FormatSpecifierFlag_Hash = (1 << 2),
|
||||
FormatSpecifierFlag_LeftJustify = (1 << 3),
|
||||
FormatSpecifierFlag_ZeroPad = (1 << 4),
|
||||
FormatSpecifierFlag_Char = (1 << 5),
|
||||
FormatSpecifierFlag_Short = (1 << 6),
|
||||
FormatSpecifierFlag_Long = (1 << 7),
|
||||
FormatSpecifierFlag_LongLong = (1 << 8),
|
||||
FormatSpecifierFlag_Uppercase = (1 << 9),
|
||||
FormatSpecifierFlag_HasPrecision = (1 << 10),
|
||||
};
|
||||
|
||||
using FormatSpecifierFlagStorage = std::underlying_type<FormatSpecifierFlag>::type;
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsDigit(char c) {
|
||||
return '0' <= c && c <= '9';
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 ParseU32(const char *&str) {
|
||||
u32 value = 0;
|
||||
do {
|
||||
value = (value * 10) + static_cast<u32>(*(str++) - '0');
|
||||
} while (IsDigit(*str));
|
||||
return value;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t Strnlen(const char *str, size_t max) {
|
||||
const char *cur = str;
|
||||
while (*cur && max--) {
|
||||
cur++;
|
||||
}
|
||||
return static_cast<size_t>(cur - str);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void VSNPrintfImpl(char * const dst, const size_t dst_size, const char *format, ::std::va_list vl) {
|
||||
size_t dst_index = 0;
|
||||
|
||||
auto WriteCharacter = [dst, dst_size, &dst_index](char c) ALWAYS_INLINE_LAMBDA {
|
||||
if (dst_index < dst_size) {
|
||||
dst[dst_index++] = c;
|
||||
}
|
||||
};
|
||||
|
||||
/* Loop over every character in the string, looking for format specifiers. */
|
||||
while (*format) {
|
||||
if (const char c = *(format++); c != '%') {
|
||||
WriteCharacter(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We have to parse a format specifier. */
|
||||
/* Start by parsing flags. */
|
||||
FormatSpecifierFlagStorage flags = FormatSpecifierFlag_None;
|
||||
auto SetFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { flags |= f; };
|
||||
auto ClearFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { flags &= ~f; };
|
||||
auto HasFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { return (flags & f) != 0; };
|
||||
{
|
||||
bool parsed_flags = false;
|
||||
while (!parsed_flags) {
|
||||
switch (*format) {
|
||||
case ' ': SetFlag(FormatSpecifierFlag_EmptySign); format++; break;
|
||||
case '+': SetFlag(FormatSpecifierFlag_ForceSign); format++; break;
|
||||
case '#': SetFlag(FormatSpecifierFlag_Hash); format++; break;
|
||||
case '-': SetFlag(FormatSpecifierFlag_LeftJustify); format++; break;
|
||||
case '0': SetFlag(FormatSpecifierFlag_ZeroPad); format++; break;
|
||||
default:
|
||||
parsed_flags = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Next, parse width. */
|
||||
u32 width = 0;
|
||||
if (IsDigit(*format)) {
|
||||
/* Integer width. */
|
||||
width = ParseU32(format);
|
||||
} else if (*format == '*') {
|
||||
/* Dynamic width. */
|
||||
const int _width = va_arg(vl, int);
|
||||
if (_width >= 0) {
|
||||
width = static_cast<u32>(_width);
|
||||
} else {
|
||||
SetFlag(FormatSpecifierFlag_LeftJustify);
|
||||
width = static_cast<u32>(-_width);
|
||||
}
|
||||
format++;
|
||||
}
|
||||
|
||||
/* Next, parse precision if present. */
|
||||
u32 precision = 0;
|
||||
if (*format == '.') {
|
||||
SetFlag(FormatSpecifierFlag_HasPrecision);
|
||||
format++;
|
||||
|
||||
if (IsDigit(*format)) {
|
||||
/* Integer precision. */
|
||||
precision = ParseU32(format);
|
||||
} else if (*format == '*') {
|
||||
/* Dynamic precision. */
|
||||
const int _precision = va_arg(vl, int);
|
||||
if (_precision > 0) {
|
||||
precision = static_cast<u32>(_precision);
|
||||
}
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse length. */
|
||||
constexpr bool SizeIsLong = sizeof(size_t) == sizeof(long);
|
||||
constexpr bool IntMaxIsLong = sizeof(intmax_t) == sizeof(long);
|
||||
constexpr bool PtrDiffIsLong = sizeof(ptrdiff_t) == sizeof(long);
|
||||
switch (*format) {
|
||||
case 'z':
|
||||
SetFlag(SizeIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
|
||||
format++;
|
||||
break;
|
||||
case 'j':
|
||||
SetFlag(IntMaxIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
|
||||
format++;
|
||||
break;
|
||||
case 't':
|
||||
SetFlag(PtrDiffIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
|
||||
format++;
|
||||
break;
|
||||
case 'h':
|
||||
SetFlag(FormatSpecifierFlag_Short);
|
||||
format++;
|
||||
if (*format == 'h') {
|
||||
SetFlag(FormatSpecifierFlag_Char);
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
SetFlag(FormatSpecifierFlag_Long);
|
||||
format++;
|
||||
if (*format == 'l') {
|
||||
SetFlag(FormatSpecifierFlag_LongLong);
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const char specifier = *(format++);
|
||||
switch (specifier) {
|
||||
case 'p':
|
||||
if constexpr (sizeof(uintptr_t) == sizeof(long long)) {
|
||||
SetFlag(FormatSpecifierFlag_LongLong);
|
||||
} else {
|
||||
SetFlag(FormatSpecifierFlag_Long);
|
||||
}
|
||||
SetFlag(FormatSpecifierFlag_Hash);
|
||||
[[fallthrough]];
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'b':
|
||||
case 'o':
|
||||
case 'x':
|
||||
case 'X':
|
||||
{
|
||||
/* Determine the base to print with. */
|
||||
u32 base;
|
||||
switch (specifier) {
|
||||
case 'b':
|
||||
base = 2;
|
||||
break;
|
||||
case 'o':
|
||||
base = 8;
|
||||
break;
|
||||
case 'X':
|
||||
SetFlag(FormatSpecifierFlag_Uppercase);
|
||||
[[fallthrough]];
|
||||
case 'p':
|
||||
case 'x':
|
||||
base = 16;
|
||||
break;
|
||||
default:
|
||||
base = 10;
|
||||
ClearFlag(FormatSpecifierFlag_Hash);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Precision implies no zero-padding. */
|
||||
if (HasFlag(FormatSpecifierFlag_HasPrecision)) {
|
||||
ClearFlag(FormatSpecifierFlag_ZeroPad);
|
||||
}
|
||||
|
||||
/* Unsigned types don't get signs. */
|
||||
const bool is_unsigned = base != 10 || specifier == 'u';
|
||||
if (is_unsigned) {
|
||||
ClearFlag(FormatSpecifierFlag_EmptySign);
|
||||
ClearFlag(FormatSpecifierFlag_ForceSign);
|
||||
}
|
||||
|
||||
auto PrintInteger = [&](bool negative, uintmax_t value) {
|
||||
constexpr size_t BufferSize = 64; /* Binary digits for 64-bit numbers may use 64 digits. */
|
||||
char buf[BufferSize];
|
||||
size_t len = 0;
|
||||
|
||||
/* No hash flag for zero. */
|
||||
if (value == 0) {
|
||||
ClearFlag(FormatSpecifierFlag_Hash);
|
||||
}
|
||||
|
||||
if (!HasFlag(FormatSpecifierFlag_HasPrecision) || value != 0) {
|
||||
do {
|
||||
const char digit = static_cast<char>(value % base);
|
||||
buf[len++] = (digit < 10) ? ('0' + digit) : ((HasFlag(FormatSpecifierFlag_Uppercase) ? 'A' : 'a') + digit - 10);
|
||||
value /= base;
|
||||
} while (value);
|
||||
}
|
||||
|
||||
/* Determine our prefix length. */
|
||||
size_t prefix_len = 0;
|
||||
const bool has_sign = negative || HasFlag(FormatSpecifierFlag_ForceSign) || HasFlag(FormatSpecifierFlag_EmptySign);
|
||||
if (has_sign) {
|
||||
prefix_len++;
|
||||
}
|
||||
if (HasFlag(FormatSpecifierFlag_Hash)) {
|
||||
prefix_len += (base != 8) ? 2 : 1;
|
||||
}
|
||||
|
||||
/* Determine zero-padding count. */
|
||||
size_t num_zeroes = (len < precision) ? precision - len : 0;
|
||||
if (!HasFlag(FormatSpecifierFlag_LeftJustify) && HasFlag(FormatSpecifierFlag_ZeroPad)) {
|
||||
num_zeroes = (len + prefix_len < width) ? width - len - prefix_len : 0;
|
||||
}
|
||||
|
||||
/* Print out left padding. */
|
||||
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
for (size_t i = len + prefix_len + num_zeroes; i < static_cast<size_t>(width); i++) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* Print out sign. */
|
||||
if (negative) {
|
||||
WriteCharacter('-');
|
||||
} else if (HasFlag(FormatSpecifierFlag_ForceSign)) {
|
||||
WriteCharacter('+');
|
||||
} else if (HasFlag(FormatSpecifierFlag_EmptySign)) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
|
||||
/* Print out base prefix. */
|
||||
if (HasFlag(FormatSpecifierFlag_Hash)) {
|
||||
WriteCharacter('0');
|
||||
if (base == 2) {
|
||||
WriteCharacter('b');
|
||||
} else if (base == 16) {
|
||||
WriteCharacter('x');
|
||||
}
|
||||
}
|
||||
|
||||
/* Print out zeroes. */
|
||||
for (size_t i = 0; i < num_zeroes; i++) {
|
||||
WriteCharacter('0');
|
||||
}
|
||||
|
||||
/* Print out digits. */
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
WriteCharacter(buf[len - 1 - i]);
|
||||
}
|
||||
|
||||
/* Print out right padding. */
|
||||
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
for (size_t i = len + prefix_len + num_zeroes; i < static_cast<size_t>(width); i++) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Output the integer. */
|
||||
if (is_unsigned) {
|
||||
uintmax_t n = 0;
|
||||
if (HasFlag(FormatSpecifierFlag_LongLong)) {
|
||||
n = static_cast<unsigned long long>(va_arg(vl, unsigned long long));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Long)) {
|
||||
n = static_cast<unsigned long>(va_arg(vl, unsigned long));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Char)) {
|
||||
n = static_cast<unsigned char>(va_arg(vl, unsigned int));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Short)) {
|
||||
n = static_cast<unsigned short>(va_arg(vl, unsigned int));
|
||||
} else {
|
||||
n = static_cast<unsigned int>(va_arg(vl, unsigned int));
|
||||
}
|
||||
if (specifier == 'p' && n == 0) {
|
||||
WriteCharacter('(');
|
||||
WriteCharacter('n');
|
||||
WriteCharacter('i');
|
||||
WriteCharacter('l');
|
||||
WriteCharacter(')');
|
||||
} else {
|
||||
PrintInteger(false, n);
|
||||
}
|
||||
} else {
|
||||
intmax_t n = 0;
|
||||
if (HasFlag(FormatSpecifierFlag_LongLong)) {
|
||||
n = static_cast<signed long long>(va_arg(vl, signed long long));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Long)) {
|
||||
n = static_cast<signed long>(va_arg(vl, signed long));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Char)) {
|
||||
n = static_cast<signed char>(va_arg(vl, signed int));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Short)) {
|
||||
n = static_cast<signed short>(va_arg(vl, signed int));
|
||||
} else {
|
||||
n = static_cast<signed int>(va_arg(vl, signed int));
|
||||
}
|
||||
const bool negative = n < 0;
|
||||
const uintmax_t u = (negative) ? static_cast<uintmax_t>(-n) : static_cast<uintmax_t>(n);
|
||||
PrintInteger(negative, u);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
{
|
||||
size_t len = 1;
|
||||
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
while (len++ < width) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
WriteCharacter(static_cast<char>(va_arg(vl, int)));
|
||||
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
while (len++ < width) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
{
|
||||
const char *str = va_arg(vl, char *);
|
||||
if (str == nullptr) {
|
||||
str = "(null)";
|
||||
}
|
||||
|
||||
size_t len = Strnlen(str, precision > 0 ? precision : std::numeric_limits<size_t>::max());
|
||||
if (HasFlag(FormatSpecifierFlag_HasPrecision)) {
|
||||
len = (len < precision) ? len : precision;
|
||||
}
|
||||
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
while (len++ < width) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
while (*str && (!HasFlag(FormatSpecifierFlag_HasPrecision) || (precision--) != 0)) {
|
||||
WriteCharacter(*(str++));
|
||||
}
|
||||
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
while (len++ < width) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
default:
|
||||
WriteCharacter(specifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
WriteCharacter('\0');
|
||||
dst[dst_size - 1] = '\0';
|
||||
}
|
||||
|
||||
KSpinLock g_debug_log_lock;
|
||||
bool g_initialized_impl;
|
||||
|
||||
|
@ -452,9 +75,6 @@ namespace ams::kern {
|
|||
|
||||
}
|
||||
|
||||
|
||||
#pragma GCC pop_options
|
||||
|
||||
void KDebugLog::Initialize() {
|
||||
if (KTargetSystem::IsDebugLoggingEnabled()) {
|
||||
KScopedInterruptDisable di;
|
||||
|
@ -486,7 +106,7 @@ namespace ams::kern {
|
|||
}
|
||||
|
||||
void KDebugLog::VSNPrintf(char *dst, const size_t dst_size, const char *format, ::std::va_list vl) {
|
||||
VSNPrintfImpl(dst, dst_size, format, vl);
|
||||
::ams::util::TVSNPrintf(dst, dst_size, format, vl);
|
||||
}
|
||||
|
||||
Result KDebugLog::PrintUserString(ams::kern::svc::KUserPointer<const char *> user_str, size_t len) {
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include <stratosphere/erpt.hpp>
|
||||
#include <stratosphere/err.hpp>
|
||||
#include <stratosphere/fatal.hpp>
|
||||
#include <stratosphere/gpio.hpp>
|
||||
#include <stratosphere/hid.hpp>
|
||||
#include <stratosphere/hos.hpp>
|
||||
#include <stratosphere/kvdb.hpp>
|
||||
|
@ -60,7 +61,6 @@
|
|||
#include <stratosphere/pgl.hpp>
|
||||
#include <stratosphere/psc.hpp>
|
||||
#include <stratosphere/pm.hpp>
|
||||
#include <stratosphere/reg.hpp>
|
||||
#include <stratosphere/ro.hpp>
|
||||
#include <stratosphere/settings.hpp>
|
||||
#include <stratosphere/sf.hpp>
|
||||
|
|
|
@ -39,19 +39,21 @@ namespace ams::impl {
|
|||
AMS_DEFINE_SYSTEM_THREAD(21, pm, Main);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, pm, ProcessTrack);
|
||||
|
||||
|
||||
/* NCM. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, ncm, MainWaitThreads);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, ncm, ContentManagerServerIpcSession);
|
||||
AMS_DEFINE_SYSTEM_THREAD(21, ncm, LocationResolverServerIpcSession);
|
||||
|
||||
/* FS. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(16, fs, WorkerThreadPool);
|
||||
AMS_DEFINE_SYSTEM_THREAD(17, fs, Main);
|
||||
AMS_DEFINE_SYSTEM_THREAD(17, fs, WorkerRealTimeAccess);
|
||||
AMS_DEFINE_SYSTEM_THREAD(18, fs, WorkerNormalPriorityAccess);
|
||||
AMS_DEFINE_SYSTEM_THREAD(19, fs, WorkerLowPriorityAccess);
|
||||
AMS_DEFINE_SYSTEM_THREAD(30, fs, WorkerBackgroundAccess);
|
||||
AMS_DEFINE_SYSTEM_THREAD(30, fs, PatrolReader);
|
||||
AMS_DEFINE_SYSTEM_THREAD(11, sdmmc, DeviceDetector);
|
||||
AMS_DEFINE_SYSTEM_THREAD(16, fs, WorkerThreadPool);
|
||||
AMS_DEFINE_SYSTEM_THREAD(17, fs, Main);
|
||||
AMS_DEFINE_SYSTEM_THREAD(17, fs, WorkerRealTimeAccess);
|
||||
AMS_DEFINE_SYSTEM_THREAD(18, fs, WorkerNormalPriorityAccess);
|
||||
AMS_DEFINE_SYSTEM_THREAD(19, fs, WorkerLowPriorityAccess);
|
||||
AMS_DEFINE_SYSTEM_THREAD(30, fs, WorkerBackgroundAccess);
|
||||
AMS_DEFINE_SYSTEM_THREAD(30, fs, PatrolReader);
|
||||
|
||||
/* Boot. */
|
||||
AMS_DEFINE_SYSTEM_THREAD(-1, boot, Main);
|
||||
|
|
|
@ -19,16 +19,13 @@
|
|||
|
||||
namespace ams::dd {
|
||||
|
||||
uintptr_t QueryIoMapping(uintptr_t phys_addr, size_t size);
|
||||
|
||||
u32 ReadRegister(uintptr_t phys_addr);
|
||||
void WriteRegister(uintptr_t phys_addr, u32 value);
|
||||
u32 ReadWriteRegister(uintptr_t phys_addr, u32 value, u32 mask);
|
||||
u32 ReadRegister(dd::PhysicalAddress phys_addr);
|
||||
void WriteRegister(dd::PhysicalAddress phys_addr, u32 value);
|
||||
u32 ReadWriteRegister(dd::PhysicalAddress phys_addr, u32 value, u32 mask);
|
||||
|
||||
/* Convenience Helper. */
|
||||
|
||||
inline uintptr_t GetIoMapping(uintptr_t phys_addr, size_t size) {
|
||||
const uintptr_t io_mapping = QueryIoMapping(phys_addr, size);
|
||||
inline uintptr_t GetIoMapping(dd::PhysicalAddress phys_addr, size_t size) {
|
||||
const uintptr_t io_mapping = dd::QueryIoMapping(phys_addr, size);
|
||||
AMS_ABORT_UNLESS(io_mapping);
|
||||
return io_mapping;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* 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
|
||||
|
@ -13,6 +13,7 @@
|
|||
* 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
|
||||
|
||||
#define EVP_COP_RESET_VECTOR 0x200
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::ddsf {
|
||||
|
||||
enum AccessMode {
|
||||
AccessMode_None = (0u << 0),
|
||||
AccessMode_Read = (1u << 0),
|
||||
AccessMode_Write = (1u << 1),
|
||||
|
||||
AccessMode_ReadWrite = AccessMode_Read | AccessMode_Write,
|
||||
};
|
||||
|
||||
}
|
22
libraries/libstratosphere/include/stratosphere/gpio.hpp
Normal file
22
libraries/libstratosphere/include/stratosphere/gpio.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stratosphere/gpio/gpio_types.hpp>
|
||||
#include <stratosphere/gpio/sf/gpio_sf_i_pad_session.hpp>
|
||||
#include <stratosphere/gpio/sf/gpio_sf_i_manager.hpp>
|
||||
#include <stratosphere/gpio/gpio_api.hpp>
|
||||
#include <stratosphere/gpio/gpio_pad_api.hpp>
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* 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
|
||||
|
@ -14,8 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/gpio/gpio_types.hpp>
|
||||
|
||||
#define APBDEV_PMC_DPD_ENABLE 0x024
|
||||
#define APBDEV_PMC_SCRATCH0 0x050
|
||||
#define APBDEV_PMC_SCRATCH4 0x060
|
||||
#define APBDEV_PMC_SCRATCH39 0x138
|
||||
namespace ams::gpio {
|
||||
|
||||
void Initialize();
|
||||
void Finalize();
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
#include <stratosphere/gpio/gpio_types.hpp>
|
||||
#include <stratosphere/gpio/gpio_select_pad_name.hpp>
|
||||
|
||||
namespace ams::gpio {
|
||||
|
||||
struct GpioPadSession {
|
||||
void *_session;
|
||||
os::SystemEventType *_event;
|
||||
};
|
||||
|
||||
Result OpenSession(GpioPadSession *out_session, ams::DeviceCode device_code);
|
||||
void CloseSession(GpioPadSession *session);
|
||||
|
||||
Direction GetDirection(GpioPadSession *session);
|
||||
void SetDirection(GpioPadSession *session, Direction direction);
|
||||
|
||||
GpioValue GetValue(GpioPadSession *session);
|
||||
void SetValue(GpioPadSession *session, GpioValue value);
|
||||
|
||||
InterruptMode GetInterruptMode(GpioPadSession *session);
|
||||
void SetInterruptMode(GpioPadSession *session, InterruptMode mode);
|
||||
|
||||
bool GetInterruptEnable(GpioPadSession *session);
|
||||
void SetInterruptEnable(GpioPadSession *session, bool en);
|
||||
|
||||
InterruptStatus GetInterruptStatus(GpioPadSession *session);
|
||||
void ClearInterruptStatus(GpioPadSession *session);
|
||||
|
||||
int GetDebounceTime(GpioPadSession *session);
|
||||
void SetDebounceTime(GpioPadSession *session, int ms);
|
||||
|
||||
bool GetDebounceEnabled(GpioPadSession *session);
|
||||
void SetDebounceEnabled(GpioPadSession *session, bool en);
|
||||
|
||||
Result BindInterrupt(os::SystemEventType *event, GpioPadSession *session);
|
||||
void UnbindInterrupt(GpioPadSession *session);
|
||||
|
||||
Result IsWakeEventActive(bool *out_is_active, ams::DeviceCode device_code);
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/gpio/gpio_types.hpp>
|
||||
|
||||
namespace ams::gpio {
|
||||
|
||||
enum GpioPadName : u32 {
|
||||
GpioPadName_CodecLdoEnTemp = 1,
|
||||
GpioPadName_ButtonVolUp = 25,
|
||||
GpioPadName_ButtonVolDn = 26,
|
||||
GpioPadName_SdCd = 56,
|
||||
};
|
||||
|
||||
/* TODO: Better place for this? */
|
||||
constexpr inline const DeviceCode DeviceCode_CodecLdoEnTemp = 0x33000002;
|
||||
constexpr inline const DeviceCode DeviceCode_ButtonVolUp = 0x35000002;
|
||||
constexpr inline const DeviceCode DeviceCode_ButtonVolDn = 0x35000003;
|
||||
constexpr inline const DeviceCode DeviceCode_SdCd = 0x3C000002;
|
||||
|
||||
constexpr inline GpioPadName ConvertToGpioPadName(DeviceCode dc) {
|
||||
switch (dc.GetInternalValue()) {
|
||||
case DeviceCode_CodecLdoEnTemp.GetInternalValue(): return GpioPadName_CodecLdoEnTemp;
|
||||
case DeviceCode_ButtonVolUp .GetInternalValue(): return GpioPadName_ButtonVolUp;
|
||||
case DeviceCode_ButtonVolDn .GetInternalValue(): return GpioPadName_ButtonVolDn;
|
||||
case DeviceCode_SdCd .GetInternalValue(): return GpioPadName_SdCd;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline DeviceCode ConvertToDeviceCode(GpioPadName gpn) {
|
||||
switch (gpn) {
|
||||
case GpioPadName_CodecLdoEnTemp: return DeviceCode_CodecLdoEnTemp;
|
||||
case GpioPadName_ButtonVolUp: return DeviceCode_ButtonVolUp;
|
||||
case GpioPadName_ButtonVolDn: return DeviceCode_ButtonVolDn;
|
||||
case GpioPadName_SdCd: return DeviceCode_SdCd;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/gpio/gpio_types.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
#include <stratosphere/gpio/gpio_pad_name.board.nintendo_nx.hpp>
|
||||
#else
|
||||
/* Error? */
|
||||
#endif
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::gpio {
|
||||
|
||||
enum InterruptMode : u32 {
|
||||
InterruptMode_LowLevel = 0,
|
||||
InterruptMode_HighLevel = 1,
|
||||
InterruptMode_RisingEdge = 2,
|
||||
InterruptMode_FallingEdge = 3,
|
||||
InterruptMode_AnyEdge = 4,
|
||||
};
|
||||
|
||||
enum Direction : u32 {
|
||||
Direction_Input = 0,
|
||||
Direction_Output = 1,
|
||||
};
|
||||
|
||||
enum GpioValue : u32 {
|
||||
GpioValue_Low = 0,
|
||||
GpioValue_High = 1,
|
||||
};
|
||||
|
||||
enum InterruptStatus : u32 {
|
||||
InterruptStatus_Inactive = 0,
|
||||
InterruptStatus_Active = 1,
|
||||
};
|
||||
|
||||
using WakeBitFlag = util::BitFlagSet<128>;
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/ddsf/ddsf_types.hpp>
|
||||
#include <stratosphere/gpio/gpio_types.hpp>
|
||||
#include <stratosphere/gpio/gpio_select_pad_name.hpp>
|
||||
#include <stratosphere/gpio/sf/gpio_sf_i_pad_session.hpp>
|
||||
|
||||
namespace ams::gpio::sf {
|
||||
|
||||
#define AMS_GPIO_I_MANAGER_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, OpenSessionForDev, (ams::sf::Out<std::shared_ptr<gpio::sf::IPadSession>> out, s32 pad_descriptor) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, OpenSession, (ams::sf::Out<std::shared_ptr<gpio::sf::IPadSession>> out, gpio::GpioPadName pad_name) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, OpenSessionForTest, (ams::sf::Out<std::shared_ptr<gpio::sf::IPadSession>> out, gpio::GpioPadName pad_name) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, IsWakeEventActive, (ams::sf::Out<bool> out, gpio::GpioPadName pad_name), hos::Version_Min, hos::Version_6_2_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, GetWakeEventActiveFlagSet, (ams::sf::Out<gpio::WakeBitFlag> out), hos::Version_Min, hos::Version_6_2_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, SetWakeEventActiveFlagSetForDebug, (gpio::GpioPadName pad_name, bool is_enabled), hos::Version_Min, hos::Version_6_2_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, SetWakePinDebugMode, (s32 mode) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, OpenSession2, (ams::sf::Out<std::shared_ptr<gpio::sf::IPadSession>> out, DeviceCode device_code, ddsf::AccessMode access_mode), hos::Version_5_0_0 ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, IsWakeEventActive2, (ams::sf::Out<bool> out, DeviceCode device_code), hos::Version_5_0_0 ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, SetWakeEventActiveFlagSetForDebug2, (DeviceCode device_code, bool is_enabled), hos::Version_5_0_0 ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, SetRetryValues, (u32 arg0, u32 arg1), hos::Version_6_0_0 )
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(IManager, AMS_GPIO_I_MANAGER_INTERFACE_INFO)
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <stratosphere/gpio/gpio_types.hpp>
|
||||
|
||||
namespace ams::gpio::sf {
|
||||
|
||||
#define AMS_GPIO_I_PAD_SESSION_INTERFACE_INFO(C, H) \
|
||||
AMS_SF_METHOD_INFO(C, H, 0, Result, SetDirection, (gpio::Direction direction) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 1, Result, GetDirection, (ams::sf::Out<gpio::Direction> out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 2, Result, SetInterruptMode, (gpio::InterruptMode mode) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 3, Result, GetInterruptMode, (ams::sf::Out<gpio::InterruptMode> out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 4, Result, SetInterruptEnable, (bool enable) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 5, Result, GetInterruptEnable, (ams::sf::Out<bool> out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 6, Result, GetInterruptStatus, (ams::sf::Out<gpio::InterruptStatus> out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 7, Result, ClearInterruptStatus, () ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 8, Result, SetValue, (gpio::GpioValue value) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 9, Result, GetValue, (ams::sf::Out<gpio::GpioValue> out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 10, Result, BindInterrupt, (ams::sf::OutCopyHandle out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 11, Result, UnbindInterrupt, () ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 12, Result, SetDebounceEnabled, (bool enable) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 13, Result, GetDebounceEnabled, (ams::sf::Out<bool> out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 14, Result, SetDebounceTime, (s32 ms) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 15, Result, GetDebounceTime, (ams::sf::Out<s32> out) ) \
|
||||
AMS_SF_METHOD_INFO(C, H, 16, Result, SetValueForSleepState, (gpio::GpioValue value), hos::Version_4_0_0) \
|
||||
AMS_SF_METHOD_INFO(C, H, 16, Result, GetValueForSleepState, (ams::sf::Out<gpio::GpioValue> out), hos::Version_6_0_0)
|
||||
|
||||
AMS_SF_DEFINE_INTERFACE(IPadSession, AMS_GPIO_I_PAD_SESSION_INTERFACE_INFO)
|
||||
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::reg {
|
||||
|
||||
inline void Write(volatile u32 *reg, u32 val) {
|
||||
*reg = val;
|
||||
}
|
||||
|
||||
inline void Write(uintptr_t reg, u32 val) {
|
||||
Write(reinterpret_cast<volatile u32 *>(reg), val);
|
||||
}
|
||||
|
||||
inline u32 Read(volatile u32 *reg) {
|
||||
return *reg;
|
||||
}
|
||||
|
||||
inline u32 Read(uintptr_t reg) {
|
||||
return Read(reinterpret_cast<volatile u32 *>(reg));
|
||||
}
|
||||
|
||||
inline void SetBits(volatile u32 *reg, u32 mask) {
|
||||
*reg = *reg | mask;
|
||||
}
|
||||
|
||||
inline void SetBits(uintptr_t reg, u32 mask) {
|
||||
SetBits(reinterpret_cast<volatile u32 *>(reg), mask);
|
||||
}
|
||||
|
||||
inline void ClearBits(volatile u32 *reg, u32 mask) {
|
||||
*reg = *reg & ~mask;
|
||||
}
|
||||
|
||||
inline void ClearBits(uintptr_t reg, u32 mask) {
|
||||
ClearBits(reinterpret_cast<volatile u32 *>(reg), mask);
|
||||
}
|
||||
|
||||
inline void MaskBits(volatile u32 *reg, u32 mask) {
|
||||
*reg = *reg & mask;
|
||||
}
|
||||
|
||||
inline void MaskBits(uintptr_t reg, u32 mask) {
|
||||
MaskBits(reinterpret_cast<volatile u32 *>(reg), mask);
|
||||
}
|
||||
|
||||
inline void ReadWrite(volatile u32 *reg, u32 val, u32 mask) {
|
||||
*reg = (*reg & (~mask)) | (val & mask);
|
||||
}
|
||||
|
||||
inline void ReadWrite(uintptr_t reg, u32 val, u32 mask) {
|
||||
ReadWrite(reinterpret_cast<volatile u32 *>(reg), val, mask);
|
||||
}
|
||||
|
||||
}
|
|
@ -167,20 +167,19 @@ namespace ams::boot2 {
|
|||
}
|
||||
}
|
||||
|
||||
bool GetGpioPadLow(GpioPadName pad) {
|
||||
GpioPadSession button;
|
||||
if (R_FAILED(gpioOpenSession(&button, pad))) {
|
||||
bool GetGpioPadLow(DeviceCode device_code) {
|
||||
gpio::GpioPadSession button;
|
||||
if (R_FAILED(gpio::OpenSession(std::addressof(button), device_code))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure we close even on early return. */
|
||||
ON_SCOPE_EXIT { gpioPadClose(&button); };
|
||||
ON_SCOPE_EXIT { gpio::CloseSession(std::addressof(button)); };
|
||||
|
||||
/* Set direction input. */
|
||||
gpioPadSetDirection(&button, GpioDirection_Input);
|
||||
gpio::SetDirection(std::addressof(button), gpio::Direction_Input);
|
||||
|
||||
GpioValue val;
|
||||
return R_SUCCEEDED(gpioPadGetValue(&button, &val)) && val == GpioValue_Low;
|
||||
return gpio::GetValue(std::addressof(button)) == gpio::GpioValue_Low;
|
||||
}
|
||||
|
||||
bool IsForceMaintenance() {
|
||||
|
@ -197,7 +196,7 @@ namespace ams::boot2 {
|
|||
|
||||
/* Contact GPIO, read plus/minus buttons. */
|
||||
{
|
||||
return GetGpioPadLow(GpioPadName_ButtonVolUp) && GetGpioPadLow(GpioPadName_ButtonVolDown);
|
||||
return GetGpioPadLow(gpio::DeviceCode_ButtonVolUp) && GetGpioPadLow(gpio::DeviceCode_ButtonVolDn);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::dd {
|
||||
|
||||
uintptr_t QueryIoMapping(uintptr_t phys_addr, size_t size) {
|
||||
u64 virtual_addr;
|
||||
const u64 aligned_addr = util::AlignDown(phys_addr, os::MemoryPageSize);
|
||||
const size_t offset = phys_addr - aligned_addr;
|
||||
const u64 aligned_size = size + offset;
|
||||
if (hos::GetVersion() >= hos::Version_10_0_0) {
|
||||
u64 region_size;
|
||||
R_TRY_CATCH(svcQueryIoMapping(&virtual_addr, ®ion_size, aligned_addr, aligned_size)) {
|
||||
/* Official software handles this by returning 0. */
|
||||
R_CATCH(svc::ResultNotFound) { return 0; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
AMS_ASSERT(region_size >= aligned_size);
|
||||
} else {
|
||||
R_TRY_CATCH(svcLegacyQueryIoMapping(&virtual_addr, aligned_addr, aligned_size)) {
|
||||
/* Official software handles this by returning 0. */
|
||||
R_CATCH(svc::ResultNotFound) { return 0; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return static_cast<uintptr_t>(virtual_addr + offset);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
inline u32 ReadWriteRegisterImpl(uintptr_t phys_addr, u32 value, u32 mask) {
|
||||
u32 out_value;
|
||||
R_ABORT_UNLESS(svcReadWriteRegister(&out_value, phys_addr, mask, value));
|
||||
return out_value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u32 ReadRegister(uintptr_t phys_addr) {
|
||||
return ReadWriteRegisterImpl(phys_addr, 0, 0);
|
||||
}
|
||||
|
||||
void WriteRegister(uintptr_t phys_addr, u32 value) {
|
||||
ReadWriteRegisterImpl(phys_addr, value, ~u32());
|
||||
}
|
||||
|
||||
u32 ReadWriteRegister(uintptr_t phys_addr, u32 value, u32 mask) {
|
||||
return ReadWriteRegisterImpl(phys_addr, value, mask);
|
||||
}
|
||||
|
||||
}
|
197
libraries/libstratosphere/source/gpio/gpio_client_api.cpp
Normal file
197
libraries/libstratosphere/source/gpio/gpio_client_api.cpp
Normal file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "gpio_remote_manager_impl.hpp"
|
||||
|
||||
namespace ams::gpio {
|
||||
|
||||
namespace {
|
||||
|
||||
/* TODO: Manager object. */
|
||||
constinit os::SdkMutex g_init_mutex;
|
||||
constinit int g_initialize_count = 0;
|
||||
std::shared_ptr<sf::IManager> g_manager;
|
||||
|
||||
using InternalSession = std::shared_ptr<gpio::sf::IPadSession>;
|
||||
|
||||
InternalSession &GetInterface(GpioPadSession *session) {
|
||||
AMS_ASSERT(session->_session != nullptr);
|
||||
return *static_cast<InternalSession *>(session->_session);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
std::scoped_lock lk(g_init_mutex);
|
||||
|
||||
if ((g_initialize_count++) == 0) {
|
||||
R_ABORT_UNLESS(::gpioInitialize());
|
||||
g_manager = ams::sf::MakeShared<sf::IManager, RemoteManagerImpl>();
|
||||
}
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
std::scoped_lock lk(g_init_mutex);
|
||||
|
||||
AMS_ASSERT(g_initialize_count > 0);
|
||||
|
||||
if ((--g_initialize_count) == 0) {
|
||||
g_manager.reset();
|
||||
::gpioExit();
|
||||
}
|
||||
}
|
||||
|
||||
Result OpenSession(GpioPadSession *out_session, ams::DeviceCode device_code) {
|
||||
/* Allocate the session. */
|
||||
InternalSession *internal_session = new (std::nothrow) InternalSession;
|
||||
AMS_ABORT_UNLESS(internal_session != nullptr);
|
||||
auto session_guard = SCOPE_GUARD { delete internal_session; };
|
||||
|
||||
/* Get the session. */
|
||||
{
|
||||
ams::sf::cmif::ServiceObjectHolder object_holder;
|
||||
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
||||
R_TRY(g_manager->OpenSession2(std::addressof(object_holder), device_code, ddsf::AccessMode_ReadWrite));
|
||||
} else {
|
||||
R_TRY(g_manager->OpenSession(std::addressof(object_holder), ConvertToGpioPadName(device_code)));
|
||||
}
|
||||
*internal_session = object_holder.GetServiceObject<sf::IPadSession>();
|
||||
}
|
||||
|
||||
/* Set output. */
|
||||
out_session->_session = internal_session;
|
||||
out_session->_event = nullptr;
|
||||
|
||||
/* We succeeded. */
|
||||
session_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void CloseSession(GpioPadSession *session) {
|
||||
AMS_ASSERT(session != nullptr);
|
||||
|
||||
/* Unbind the interrupt, if it's still bound. */
|
||||
if (session->_event != nullptr) {
|
||||
gpio::UnbindInterrupt(session);
|
||||
}
|
||||
|
||||
/* Close the session. */
|
||||
delete std::addressof(GetInterface(session));
|
||||
session->_session = nullptr;
|
||||
}
|
||||
|
||||
Result IsWakeEventActive(bool *out_is_active, ams::DeviceCode device_code) {
|
||||
if (hos::GetVersion() >= hos::Version_7_0_0) {
|
||||
R_TRY(g_manager->IsWakeEventActive2(out_is_active, device_code));
|
||||
} else {
|
||||
R_TRY(g_manager->IsWakeEventActive(out_is_active, ConvertToGpioPadName(device_code)));
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Direction GetDirection(GpioPadSession *session) {
|
||||
Direction out;
|
||||
R_ABORT_UNLESS(GetInterface(session)->GetDirection(std::addressof(out)));
|
||||
return out;
|
||||
}
|
||||
|
||||
void SetDirection(GpioPadSession *session, Direction direction) {
|
||||
R_ABORT_UNLESS(GetInterface(session)->SetDirection(direction));
|
||||
}
|
||||
|
||||
GpioValue GetValue(GpioPadSession *session) {
|
||||
GpioValue out;
|
||||
R_ABORT_UNLESS(GetInterface(session)->GetValue(std::addressof(out)));
|
||||
return out;
|
||||
}
|
||||
|
||||
void SetValue(GpioPadSession *session, GpioValue value) {
|
||||
R_ABORT_UNLESS(GetInterface(session)->SetValue(value));
|
||||
}
|
||||
|
||||
InterruptMode GetInterruptMode(GpioPadSession *session) {
|
||||
InterruptMode out;
|
||||
R_ABORT_UNLESS(GetInterface(session)->GetInterruptMode(std::addressof(out)));
|
||||
return out;
|
||||
}
|
||||
|
||||
void SetInterruptMode(GpioPadSession *session, InterruptMode mode) {
|
||||
R_ABORT_UNLESS(GetInterface(session)->SetInterruptMode(mode));
|
||||
}
|
||||
|
||||
bool GetInterruptEnable(GpioPadSession *session) {
|
||||
bool out;
|
||||
R_ABORT_UNLESS(GetInterface(session)->GetInterruptEnable(std::addressof(out)));
|
||||
return out;
|
||||
}
|
||||
|
||||
void SetInterruptEnable(GpioPadSession *session, bool en) {
|
||||
R_ABORT_UNLESS(GetInterface(session)->SetInterruptEnable(en));
|
||||
}
|
||||
|
||||
InterruptStatus GetInterruptStatus(GpioPadSession *session) {
|
||||
InterruptStatus out;
|
||||
R_ABORT_UNLESS(GetInterface(session)->GetInterruptStatus(std::addressof(out)));
|
||||
return out;
|
||||
}
|
||||
|
||||
void ClearInterruptStatus(GpioPadSession *session) {
|
||||
R_ABORT_UNLESS(GetInterface(session)->ClearInterruptStatus());
|
||||
}
|
||||
|
||||
int GetDebounceTime(GpioPadSession *session) {
|
||||
int out;
|
||||
R_ABORT_UNLESS(GetInterface(session)->GetDebounceTime(std::addressof(out)));
|
||||
return out;
|
||||
}
|
||||
|
||||
void SetDebounceTime(GpioPadSession *session, int ms) {
|
||||
R_ABORT_UNLESS(GetInterface(session)->SetDebounceTime(ms));
|
||||
}
|
||||
|
||||
bool GetDebounceEnabled(GpioPadSession *session) {
|
||||
bool out;
|
||||
R_ABORT_UNLESS(GetInterface(session)->GetDebounceEnabled(std::addressof(out)));
|
||||
return out;
|
||||
}
|
||||
|
||||
void SetDebounceEnabled(GpioPadSession *session, bool en) {
|
||||
R_ABORT_UNLESS(GetInterface(session)->SetDebounceEnabled(en));
|
||||
}
|
||||
|
||||
Result BindInterrupt(os::SystemEventType *event, GpioPadSession *session) {
|
||||
AMS_ASSERT(session->_event == nullptr);
|
||||
|
||||
ams::sf::CopyHandle handle;
|
||||
R_TRY(GetInterface(session)->BindInterrupt(std::addressof(handle)));
|
||||
|
||||
os::AttachReadableHandleToSystemEvent(event, handle.GetValue(), true, os::EventClearMode_ManualClear);
|
||||
|
||||
session->_event = event;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void UnbindInterrupt(GpioPadSession *session) {
|
||||
AMS_ASSERT(session->_event != nullptr);
|
||||
|
||||
R_ABORT_UNLESS(GetInterface(session)->UnbindInterrupt());
|
||||
|
||||
os::DestroySystemEvent(session->_event);
|
||||
session->_event = nullptr;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
#include "gpio_remote_pad_session_impl.hpp"
|
||||
|
||||
namespace ams::gpio {
|
||||
|
||||
class RemoteManagerImpl {
|
||||
public:
|
||||
RemoteManagerImpl() { /* ... */ }
|
||||
|
||||
~RemoteManagerImpl() { /* ... */ }
|
||||
public:
|
||||
/* Actual commands. */
|
||||
Result OpenSessionForDev(ams::sf::Out<std::shared_ptr<gpio::sf::IPadSession>> out, s32 pad_descriptor) {
|
||||
/* TODO: libnx bindings */
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
Result OpenSession(ams::sf::Out<std::shared_ptr<gpio::sf::IPadSession>> out, gpio::GpioPadName pad_name) {
|
||||
::GpioPadSession p;
|
||||
R_TRY(::gpioOpenSession(std::addressof(p), static_cast<::GpioPadName>(static_cast<u32>(pad_name))));
|
||||
|
||||
out.SetValue(ams::sf::MakeShared<gpio::sf::IPadSession, RemotePadSessionImpl>(p));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result OpenSessionForTest(ams::sf::Out<std::shared_ptr<gpio::sf::IPadSession>> out, gpio::GpioPadName pad_name) {
|
||||
/* TODO: libnx bindings */
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
Result IsWakeEventActive(ams::sf::Out<bool> out, gpio::GpioPadName pad_name) {
|
||||
return ::gpioIsWakeEventActive2(out.GetPointer(), static_cast<::GpioPadName>(static_cast<u32>(pad_name)));
|
||||
}
|
||||
|
||||
Result GetWakeEventActiveFlagSet(ams::sf::Out<gpio::WakeBitFlag> out) {
|
||||
/* TODO: libnx bindings */
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
Result SetWakeEventActiveFlagSetForDebug(gpio::GpioPadName pad_name, bool is_enabled) {
|
||||
/* TODO: libnx bindings */
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
Result SetWakePinDebugMode(s32 mode) {
|
||||
/* TODO: libnx bindings */
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
Result OpenSession2(ams::sf::Out<std::shared_ptr<gpio::sf::IPadSession>> out, DeviceCode device_code, ddsf::AccessMode access_mode) {
|
||||
::GpioPadSession p;
|
||||
R_TRY(::gpioOpenSession2(std::addressof(p), device_code.GetInternalValue(), access_mode));
|
||||
|
||||
out.SetValue(ams::sf::MakeShared<gpio::sf::IPadSession, RemotePadSessionImpl>(p));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result IsWakeEventActive2(ams::sf::Out<bool> out, DeviceCode device_code) {
|
||||
return ::gpioIsWakeEventActive2(out.GetPointer(), device_code.GetInternalValue());
|
||||
}
|
||||
|
||||
Result SetWakeEventActiveFlagSetForDebug2(DeviceCode device_code, bool is_enabled) {
|
||||
/* TODO: libnx bindings */
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
Result SetRetryValues(u32 arg0, u32 arg1) {
|
||||
/* TODO: libnx bindings */
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
};
|
||||
static_assert(gpio::sf::IsIManager<RemoteManagerImpl>);
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::gpio {
|
||||
|
||||
class RemotePadSessionImpl {
|
||||
private:
|
||||
::GpioPadSession srv;
|
||||
public:
|
||||
RemotePadSessionImpl(::GpioPadSession &p) : srv(p) { /* ... */ }
|
||||
|
||||
~RemotePadSessionImpl() { ::gpioPadClose(std::addressof(this->srv)); }
|
||||
public:
|
||||
/* Actual commands. */
|
||||
Result SetDirection(gpio::Direction direction) {
|
||||
return ::gpioPadSetDirection(std::addressof(this->srv), static_cast<::GpioDirection>(static_cast<u32>(direction)));
|
||||
}
|
||||
|
||||
Result GetDirection(ams::sf::Out<gpio::Direction> out) {
|
||||
static_assert(sizeof(gpio::Direction) == sizeof(::GpioDirection));
|
||||
return ::gpioPadGetDirection(std::addressof(this->srv), reinterpret_cast<::GpioDirection *>(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result SetInterruptMode(gpio::InterruptMode mode) {
|
||||
return ::gpioPadSetInterruptMode(std::addressof(this->srv), static_cast<::GpioInterruptMode>(static_cast<u32>(mode)));
|
||||
}
|
||||
|
||||
Result GetInterruptMode(ams::sf::Out<gpio::InterruptMode> out) {
|
||||
static_assert(sizeof(gpio::InterruptMode) == sizeof(::GpioInterruptMode));
|
||||
return ::gpioPadGetInterruptMode(std::addressof(this->srv), reinterpret_cast<::GpioInterruptMode *>(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result SetInterruptEnable(bool enable) {
|
||||
return ::gpioPadSetInterruptEnable(std::addressof(this->srv), enable);
|
||||
}
|
||||
|
||||
Result GetInterruptEnable(ams::sf::Out<bool> out) {
|
||||
return ::gpioPadGetInterruptEnable(std::addressof(this->srv), out.GetPointer());
|
||||
}
|
||||
|
||||
Result GetInterruptStatus(ams::sf::Out<gpio::InterruptStatus> out) {
|
||||
static_assert(sizeof(gpio::InterruptStatus) == sizeof(::GpioInterruptStatus));
|
||||
return ::gpioPadGetInterruptStatus(std::addressof(this->srv), reinterpret_cast<::GpioInterruptStatus *>(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result ClearInterruptStatus() {
|
||||
return ::gpioPadClearInterruptStatus(std::addressof(this->srv));
|
||||
}
|
||||
|
||||
Result SetValue(gpio::GpioValue value) {
|
||||
return ::gpioPadSetValue(std::addressof(this->srv), static_cast<::GpioValue>(static_cast<u32>(value)));
|
||||
}
|
||||
|
||||
Result GetValue(ams::sf::Out<gpio::GpioValue> out) {
|
||||
static_assert(sizeof(gpio::GpioValue) == sizeof(::GpioValue));
|
||||
return ::gpioPadGetValue(std::addressof(this->srv), reinterpret_cast<::GpioValue *>(out.GetPointer()));
|
||||
}
|
||||
|
||||
Result BindInterrupt(ams::sf::OutCopyHandle out) {
|
||||
::Event ev;
|
||||
R_TRY(::gpioPadBindInterrupt(std::addressof(this->srv), std::addressof(ev)));
|
||||
out.SetValue(ev.revent);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result UnbindInterrupt() {
|
||||
return ::gpioPadUnbindInterrupt(std::addressof(this->srv));
|
||||
}
|
||||
|
||||
Result SetDebounceEnabled(bool enable) {
|
||||
return ::gpioPadSetDebounceEnabled(std::addressof(this->srv), enable);
|
||||
}
|
||||
|
||||
Result GetDebounceEnabled(ams::sf::Out<bool> out) {
|
||||
return ::gpioPadGetDebounceEnabled(std::addressof(this->srv), out.GetPointer());
|
||||
}
|
||||
|
||||
Result SetDebounceTime(s32 ms) {
|
||||
return ::gpioPadSetDebounceTime(std::addressof(this->srv), ms);
|
||||
}
|
||||
|
||||
Result GetDebounceTime(ams::sf::Out<s32> out) {
|
||||
return ::gpioPadGetDebounceTime(std::addressof(this->srv), out.GetPointer());
|
||||
}
|
||||
|
||||
Result SetValueForSleepState(gpio::GpioValue value) {
|
||||
/* TODO: libnx bindings. */
|
||||
AMS_ABORT();
|
||||
}
|
||||
|
||||
Result GetValueForSleepState(ams::sf::Out<gpio::GpioValue> out) {
|
||||
/* TODO: libnx bindings. */
|
||||
AMS_ABORT();
|
||||
}
|
||||
};
|
||||
static_assert(gpio::sf::IsIPadSession<RemotePadSessionImpl>);
|
||||
|
||||
}
|
|
@ -20,12 +20,22 @@
|
|||
#include <vapours/literals.hpp>
|
||||
|
||||
#include <vapours/allocator.hpp>
|
||||
#include <vapours/device_code.hpp>
|
||||
#include <vapours/timespan.hpp>
|
||||
#include <vapours/span.hpp>
|
||||
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
#include <vapours/tegra.hpp>
|
||||
#endif
|
||||
|
||||
#include <vapours/crypto.hpp>
|
||||
#include <vapours/svc.hpp>
|
||||
|
||||
#include <vapours/ams/ams_fatal_error_context.hpp>
|
||||
|
||||
#include <vapours/dd.hpp>
|
||||
#include <vapours/sdmmc.hpp>
|
24
libraries/libvapours/include/vapours/dd.hpp
Normal file
24
libraries/libvapours/include/vapours/dd.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
|
||||
#include <vapours/dd/dd_common_types.hpp>
|
||||
#include <vapours/dd/dd_io_mapping.hpp>
|
||||
#include <vapours/dd/dd_cache.hpp>
|
25
libraries/libvapours/include/vapours/dd/dd_cache.hpp
Normal file
25
libraries/libvapours/include/vapours/dd/dd_cache.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/dd/dd_common_types.hpp>
|
||||
|
||||
namespace ams::dd {
|
||||
|
||||
void InvalidateDataCache(void *addr, size_t size);
|
||||
void StoreDataCache(void *addr, size_t size);
|
||||
void FlushDataCache(void *addr, size_t size);
|
||||
|
||||
}
|
29
libraries/libvapours/include/vapours/dd/dd_common_types.hpp
Normal file
29
libraries/libvapours/include/vapours/dd/dd_common_types.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
|
||||
namespace ams::dd {
|
||||
|
||||
using PhysicalAddress = u64;
|
||||
using DeviceVirtualAddress = u64;
|
||||
|
||||
static_assert(std::same_as<PhysicalAddress, ams::svc::PhysicalAddress>);
|
||||
|
||||
}
|
26
libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp
Normal file
26
libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/dd/dd_common_types.hpp>
|
||||
|
||||
namespace ams::dd {
|
||||
|
||||
uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size);
|
||||
|
||||
u32 ReadIoRegister(dd::PhysicalAddress phys_addr);
|
||||
void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value);
|
||||
|
||||
}
|
49
libraries/libvapours/include/vapours/device_code.hpp
Normal file
49
libraries/libvapours/include/vapours/device_code.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams {
|
||||
|
||||
namespace impl {
|
||||
|
||||
using DeviceCodeType = u32;
|
||||
|
||||
}
|
||||
|
||||
/* TODO: Better understand device code components. */
|
||||
class DeviceCode {
|
||||
private:
|
||||
impl::DeviceCodeType inner_value;
|
||||
public:
|
||||
constexpr DeviceCode(impl::DeviceCodeType v) : inner_value(v) { /* ... */ }
|
||||
|
||||
constexpr impl::DeviceCodeType GetInternalValue() const { return this->inner_value; }
|
||||
|
||||
constexpr bool operator==(const DeviceCode &rhs) const {
|
||||
return this->GetInternalValue() == rhs.GetInternalValue();
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const DeviceCode &rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline const DeviceCode InvalidDeviceCode(0);
|
||||
|
||||
}
|
|
@ -18,6 +18,9 @@
|
|||
|
||||
namespace ams::reg {
|
||||
|
||||
template<typename T>
|
||||
concept UnsignedNonConstIntegral = std::unsigned_integral<T> && !std::is_const<T>::value;
|
||||
|
||||
using BitsValue = std::tuple<u16, u16, u32>;
|
||||
using BitsMask = std::tuple<u16, u16>;
|
||||
|
||||
|
@ -48,100 +51,137 @@ namespace ams::reg {
|
|||
return (EncodeValue(values) | ...);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void Write(volatile u32 *reg, u32 val) { *reg = val; }
|
||||
ALWAYS_INLINE void Write(volatile u32 ®, u32 val) { reg = val; }
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
constexpr ALWAYS_INLINE u32 EncodeMask(const Masks... masks) {
|
||||
return (EncodeMask(masks) | ...);
|
||||
}
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void Write(volatile IntType *reg, std::type_identity_t<IntType> val) { *reg = val; }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void Write(volatile IntType ®, std::type_identity_t<IntType> val) { reg = val; }
|
||||
|
||||
ALWAYS_INLINE void Write(uintptr_t reg, u32 val) { Write(reinterpret_cast<volatile u32 *>(reg), val); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void Write(volatile u32 *reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); }
|
||||
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void Write(volatile IntType *reg, const Values... values) { return Write(reg, static_cast<IntType>((EncodeValue(values) | ...))); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void Write(volatile u32 ®, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); }
|
||||
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void Write(volatile IntType ®, const Values... values) { return Write(reg, static_cast<IntType>((EncodeValue(values) | ...))); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void Write(uintptr_t reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); }
|
||||
|
||||
ALWAYS_INLINE u32 Read(volatile u32 *reg) { return *reg; }
|
||||
ALWAYS_INLINE u32 Read(volatile u32 ®) { return reg; }
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE IntType Read(volatile IntType *reg) { return *reg; }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE IntType Read(volatile IntType ®) { return reg; }
|
||||
|
||||
ALWAYS_INLINE u32 Read(uintptr_t reg) { return Read(reinterpret_cast<volatile u32 *>(reg)); }
|
||||
|
||||
ALWAYS_INLINE u32 Read(volatile u32 *reg, u32 mask) { return *reg & mask; }
|
||||
ALWAYS_INLINE u32 Read(volatile u32 ®, u32 mask) { return reg & mask; }
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE IntType Read(volatile IntType *reg, std::type_identity_t<IntType> mask) { return *reg & mask; }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE IntType Read(volatile IntType ®, std::type_identity_t<IntType> mask) { return reg & mask; }
|
||||
|
||||
ALWAYS_INLINE u32 Read(uintptr_t reg, u32 mask) { return Read(reinterpret_cast<volatile u32 *>(reg), mask); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE u32 Read(volatile u32 *reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); }
|
||||
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE IntType Read(volatile IntType *reg, const Masks... masks) { return Read(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
|
||||
|
||||
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE IntType Read(volatile IntType ®, const Masks... masks) { return Read(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE u32 Read(volatile u32 ®, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); }
|
||||
ALWAYS_INLINE u32 Read(uintptr_t reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE u32 Read(uintptr_t reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); }
|
||||
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE bool HasValue(volatile IntType *reg, const Values... values) { return Read(reg, static_cast<IntType>((EncodeMask(values) | ...))) == static_cast<IntType>(Encode(values...)); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE bool HasValue(volatile u32 *reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE bool HasValue(volatile u32 ®, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); }
|
||||
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE bool HasValue(volatile IntType ®, const Values... values) { return Read(reg, static_cast<IntType>((EncodeMask(values) | ...))) == static_cast<IntType>(Encode(values...)); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE bool HasValue(uintptr_t reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); }
|
||||
|
||||
ALWAYS_INLINE u32 GetValue(volatile u32 *reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); }
|
||||
ALWAYS_INLINE u32 GetValue(volatile u32 ®, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); }
|
||||
ALWAYS_INLINE u32 GetValue(uintptr_t reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); }
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE IntType GetValue(volatile IntType *reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE IntType GetValue(volatile IntType ®, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); }
|
||||
|
||||
ALWAYS_INLINE u32 GetValue(uintptr_t reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void ReadWrite(volatile IntType *reg, std::type_identity_t<IntType> val, std::type_identity_t<IntType> mask) { *reg = (*reg & (~mask)) | (val & mask); }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void ReadWrite(volatile IntType ®, std::type_identity_t<IntType> val, std::type_identity_t<IntType> mask) { reg = ( reg & (~mask)) | (val & mask); }
|
||||
|
||||
ALWAYS_INLINE void ReadWrite(volatile u32 *reg, u32 val, u32 mask) { *reg = (*reg & (~mask)) | (val & mask); }
|
||||
ALWAYS_INLINE void ReadWrite(volatile u32 ®, u32 val, u32 mask) { reg = ( reg & (~mask)) | (val & mask); }
|
||||
ALWAYS_INLINE void ReadWrite(uintptr_t reg, u32 val, u32 mask) { ReadWrite(reinterpret_cast<volatile u32 *>(reg), val, mask); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void ReadWrite(volatile u32 *reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); }
|
||||
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void ReadWrite(volatile IntType *reg, const Values... values) { return ReadWrite(reg, static_cast<IntType>((EncodeValue(values) | ...)), static_cast<IntType>((EncodeMask(values) | ...))); }
|
||||
|
||||
template<typename IntType, typename... Values> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void ReadWrite(volatile IntType ®, const Values... values) { return ReadWrite(reg, static_cast<IntType>((EncodeValue(values) | ...)), static_cast<IntType>((EncodeMask(values) | ...))); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void ReadWrite(volatile u32 ®, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); }
|
||||
ALWAYS_INLINE void ReadWrite(uintptr_t reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); }
|
||||
|
||||
template<typename... Values> requires ((sizeof...(Values) > 0) && (std::is_same<Values, BitsValue>::value && ...))
|
||||
ALWAYS_INLINE void ReadWrite(uintptr_t reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); }
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void SetBits(volatile IntType *reg, std::type_identity_t<IntType> mask) { *reg = *reg | mask; }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void SetBits(volatile IntType ®, std::type_identity_t<IntType> mask) { reg = reg | mask; }
|
||||
|
||||
ALWAYS_INLINE void SetBits(volatile u32 *reg, u32 mask) { *reg = *reg | mask; }
|
||||
ALWAYS_INLINE void SetBits(volatile u32 ®, u32 mask) { reg = reg | mask; }
|
||||
ALWAYS_INLINE void SetBits(uintptr_t reg, u32 mask) { SetBits(reinterpret_cast<volatile u32 *>(reg), mask); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void SetBits(volatile u32 *reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); }
|
||||
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void SetBits(volatile IntType *reg, const Masks... masks) { return SetBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
|
||||
|
||||
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void SetBits(volatile IntType ®, const Masks... masks) { return SetBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void SetBits(volatile u32 ®, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); }
|
||||
ALWAYS_INLINE void SetBits(uintptr_t reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void SetBits(uintptr_t reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); }
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void ClearBits(volatile IntType *reg, std::type_identity_t<IntType> mask) { *reg = *reg & ~mask; }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void ClearBits(volatile IntType ®, std::type_identity_t<IntType> mask) { reg = reg & ~mask; }
|
||||
|
||||
ALWAYS_INLINE void ClearBits(volatile u32 *reg, u32 mask) { *reg = *reg & ~mask; }
|
||||
ALWAYS_INLINE void ClearBits(volatile u32 ®, u32 mask) { reg = reg & ~mask; }
|
||||
ALWAYS_INLINE void ClearBits(uintptr_t reg, u32 mask) { ClearBits(reinterpret_cast<volatile u32 *>(reg), mask); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void ClearBits(volatile u32 *reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); }
|
||||
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void ClearBits(volatile IntType *reg, const Masks... masks) { return ClearBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
|
||||
|
||||
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void ClearBits(volatile IntType ®, const Masks... masks) { return ClearBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void ClearBits(volatile u32 ®, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); }
|
||||
ALWAYS_INLINE void ClearBits(uintptr_t reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void ClearBits(uintptr_t reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); }
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void MaskBits(volatile IntType *reg, std::type_identity_t<IntType> mask) { *reg = *reg & mask; }
|
||||
|
||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||
ALWAYS_INLINE void MaskBits(volatile IntType ®, std::type_identity_t<IntType> mask) { reg = reg & mask; }
|
||||
|
||||
ALWAYS_INLINE void MaskBits(volatile u32 *reg, u32 mask) { *reg = *reg & mask; }
|
||||
ALWAYS_INLINE void MaskBits(volatile u32 ®, u32 mask) { reg = reg & mask; }
|
||||
ALWAYS_INLINE void MaskBits(uintptr_t reg, u32 mask) { MaskBits(reinterpret_cast<volatile u32 *>(reg), mask); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void MaskBits(volatile u32 *reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); }
|
||||
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void MaskBits(volatile IntType *reg, const Masks... masks) { return MaskBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
|
||||
|
||||
template<typename IntType, typename... Masks> requires UnsignedNonConstIntegral<IntType> && ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void MaskBits(volatile IntType ®, const Masks... masks) { return MaskBits(reg, static_cast<IntType>((EncodeMask(masks) | ...))); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void MaskBits(volatile u32 ®, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); }
|
||||
|
||||
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||
ALWAYS_INLINE void MaskBits(uintptr_t reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); }
|
||||
ALWAYS_INLINE void MaskBits(uintptr_t reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); }
|
||||
|
||||
#define REG_BITS_MASK(OFFSET, WIDTH) ::ams::reg::BitsMask{OFFSET, WIDTH}
|
||||
#define REG_BITS_VALUE(OFFSET, WIDTH, VALUE) ::ams::reg::BitsValue{OFFSET, WIDTH, VALUE}
|
|
@ -41,10 +41,12 @@
|
|||
#include <vapours/results/nim_results.hpp>
|
||||
#include <vapours/results/ns_results.hpp>
|
||||
#include <vapours/results/os_results.hpp>
|
||||
#include <vapours/results/pcv_results.hpp>
|
||||
#include <vapours/results/pgl_results.hpp>
|
||||
#include <vapours/results/pm_results.hpp>
|
||||
#include <vapours/results/psc_results.hpp>
|
||||
#include <vapours/results/ro_results.hpp>
|
||||
#include <vapours/results/sdmmc_results.hpp>
|
||||
#include <vapours/results/settings_results.hpp>
|
||||
#include <vapours/results/sf_results.hpp>
|
||||
#include <vapours/results/sm_results.hpp>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* 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
|
||||
|
@ -13,9 +13,14 @@
|
|||
* 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
|
||||
|
||||
#define FLOW_CTLR_HALT_COP_EVENTS 0x004
|
||||
#define FLOW_CTLR_CC4_HVC_CONTROL 0x060
|
||||
#define FLOW_CTLR_CC4_RETENTION_CONTROL 0x064
|
||||
#define FLOW_CTLR_CC4_HVC_RETRY 0x08C
|
||||
#pragma once
|
||||
#include <vapours/results/results_common.hpp>
|
||||
|
||||
namespace ams::pcv {
|
||||
|
||||
R_DEFINE_NAMESPACE_RESULT_MODULE(133);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(IllegalRequest, 16);
|
||||
|
||||
}
|
105
libraries/libvapours/include/vapours/results/sdmmc_results.hpp
Normal file
105
libraries/libvapours/include/vapours/results/sdmmc_results.hpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/results/results_common.hpp>
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
R_DEFINE_NAMESPACE_RESULT_MODULE(24);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(NoDevice, 1);
|
||||
R_DEFINE_ERROR_RESULT(NotActivated, 2);
|
||||
R_DEFINE_ERROR_RESULT(DeviceRemoved, 3);
|
||||
R_DEFINE_ERROR_RESULT(NotAwakened, 4);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(CommunicationError, 32, 126);
|
||||
R_DEFINE_ERROR_RANGE(CommunicationNotAttained, 33, 46);
|
||||
R_DEFINE_ERROR_RESULT(ResponseIndexError, 34);
|
||||
R_DEFINE_ERROR_RESULT(ResponseEndBitError, 35);
|
||||
R_DEFINE_ERROR_RESULT(ResponseCrcError, 36);
|
||||
R_DEFINE_ERROR_RESULT(ResponseTimeoutError, 37);
|
||||
R_DEFINE_ERROR_RESULT(DataEndBitError, 38);
|
||||
R_DEFINE_ERROR_RESULT(DataCrcError, 39);
|
||||
R_DEFINE_ERROR_RESULT(DataTimeoutError, 40);
|
||||
R_DEFINE_ERROR_RESULT(AutoCommandResponseIndexError, 41);
|
||||
R_DEFINE_ERROR_RESULT(AutoCommandResponseEndBitError, 42);
|
||||
R_DEFINE_ERROR_RESULT(AutoCommandResponseCrcError, 43);
|
||||
R_DEFINE_ERROR_RESULT(AutoCommandResponseTimeoutError, 44);
|
||||
R_DEFINE_ERROR_RESULT(CommandCompleteSoftwareTimeout, 45);
|
||||
R_DEFINE_ERROR_RESULT(TransferCompleteSoftwareTimeout, 46);
|
||||
R_DEFINE_ERROR_RANGE(DeviceStatusHasError, 48, 70);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusAddressOutOfRange, 49);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusAddressMisaligned, 50);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusBlockLenError, 51);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusEraseSeqError, 52);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusEraseParam, 53);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusWpViolation, 54);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusLockUnlockFailed, 55);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusComCrcError, 56);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusIllegalCommand, 57);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusDeviceEccFailed, 58);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusCcError, 59);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusError, 60);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusCidCsdOverwrite, 61);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusWpEraseSkip, 62);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusEraseReset, 63);
|
||||
R_DEFINE_ERROR_RESULT(DeviceStatusSwitchError, 64);
|
||||
R_DEFINE_ERROR_RESULT(UnexpectedDeviceState, 72);
|
||||
R_DEFINE_ERROR_RESULT(UnexpectedDeviceCsdValue, 73);
|
||||
R_DEFINE_ERROR_RESULT(AbortTransactionSoftwareTimeout, 74);
|
||||
R_DEFINE_ERROR_RESULT(CommandInhibitCmdSoftwareTimeout, 75);
|
||||
R_DEFINE_ERROR_RESULT(CommandInhibitDatSoftwareTimeout, 76);
|
||||
R_DEFINE_ERROR_RESULT(BusySoftwareTimeout, 77);
|
||||
R_DEFINE_ERROR_RESULT(IssueTuningCommandSoftwareTimeout, 78);
|
||||
R_DEFINE_ERROR_RESULT(TuningFailed, 79);
|
||||
R_DEFINE_ERROR_RESULT(MmcInitializationSoftwareTimeout, 80);
|
||||
R_DEFINE_ERROR_RESULT(MmcNotSupportExtendedCsd, 81);
|
||||
R_DEFINE_ERROR_RESULT(UnexpectedMmcExtendedCsdValue, 82);
|
||||
R_DEFINE_ERROR_RESULT(MmcEraseSoftwareTimeout, 83);
|
||||
R_DEFINE_ERROR_RESULT(SdCardValidationError, 84);
|
||||
R_DEFINE_ERROR_RESULT(SdCardInitializationSoftwareTimeout, 85);
|
||||
R_DEFINE_ERROR_RESULT(SdCardGetValidRcaSoftwareTimeout, 86);
|
||||
R_DEFINE_ERROR_RESULT(UnexpectedSdCardAcmdDisabled, 87);
|
||||
R_DEFINE_ERROR_RESULT(SdCardNotSupportSwitchFunctionStatus, 88);
|
||||
R_DEFINE_ERROR_RESULT(UnexpectedSdCardSwitchFunctionStatus, 89);
|
||||
R_DEFINE_ERROR_RESULT(SdCardNotSupportAccessMode, 90);
|
||||
R_DEFINE_ERROR_RESULT(SdCardNot4BitBusWidthAtUhsIMode, 91);
|
||||
R_DEFINE_ERROR_RESULT(SdCardNotSupportSdr104AndSdr50, 92);
|
||||
R_DEFINE_ERROR_RESULT(SdCardCannotSwitchAccessMode, 93);
|
||||
R_DEFINE_ERROR_RESULT(SdCardFailedSwitchAccessMode, 94);
|
||||
R_DEFINE_ERROR_RESULT(SdCardUnacceptableCurrentConsumption, 95);
|
||||
R_DEFINE_ERROR_RESULT(SdCardNotReadyToVoltageSwitch, 96);
|
||||
R_DEFINE_ERROR_RESULT(SdCardNotCompleteVoltageSwitch, 97);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(HostControllerUnexpected, 128, 158);
|
||||
R_DEFINE_ERROR_RESULT(InternalClockStableSoftwareTimeout, 129);
|
||||
R_DEFINE_ERROR_RESULT(SdHostStandardUnknownAutoCmdError, 130);
|
||||
R_DEFINE_ERROR_RESULT(SdHostStandardUnknownError, 131);
|
||||
R_DEFINE_ERROR_RESULT(SdmmcDllCalibrationSoftwareTimeout, 132);
|
||||
R_DEFINE_ERROR_RESULT(SdmmcDllApplicationSoftwareTimeout, 133);
|
||||
R_DEFINE_ERROR_RESULT(SdHostStandardFailSwitchTo1_8V, 134);
|
||||
R_DEFINE_ERROR_RESULT(DriveStrengthCalibrationNotCompleted, 135);
|
||||
R_DEFINE_ERROR_RESULT(DriveStrengthCalibrationSoftwareTimeout, 136);
|
||||
R_DEFINE_ERROR_RESULT(SdmmcCompShortToGnd, 137);
|
||||
R_DEFINE_ERROR_RESULT(SdmmcCompOpen, 138);
|
||||
|
||||
R_DEFINE_ERROR_RANGE(InternalError, 160, 190);
|
||||
R_DEFINE_ERROR_RESULT(NoWaitedInterrupt, 161);
|
||||
R_DEFINE_ERROR_RESULT(WaitInterruptSoftwareTimeout, 162);
|
||||
|
||||
R_DEFINE_ERROR_RESULT(NotImplemented, 201);
|
||||
}
|
28
libraries/libvapours/include/vapours/sdmmc.hpp
Normal file
28
libraries/libvapours/include/vapours/sdmmc.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/svc.hpp>
|
||||
|
||||
#include <vapours/sdmmc/sdmmc_build_config.hpp>
|
||||
#include <vapours/sdmmc/sdmmc_common.hpp>
|
||||
#include <vapours/sdmmc/sdmmc_mmc.hpp>
|
||||
#include <vapours/sdmmc/sdmmc_sd_card.hpp>
|
||||
#include <vapours/sdmmc/sdmmc_gc_asic.hpp>
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/svc.hpp>
|
||||
#include <vapours/dd.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
|
||||
//#define AMS_SDMMC_THREAD_SAFE
|
||||
//#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
|
||||
//#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
|
||||
//#define AMS_SDMMC_USE_DEVICE_DETECTOR
|
||||
//#define AMS_SDMMC_USE_LOGGER
|
||||
//#define AMS_SDMMC_USE_OS_EVENTS
|
||||
//#define AMS_SDMMC_USE_OS_TIMER
|
||||
#define AMS_SDMMC_USE_UTIL_TIMER
|
||||
//#define AMS_SDMMC_ENABLE_MMC_HS400
|
||||
//#define AMS_SDMMC_ENABLE_SD_UHS_I
|
||||
//#define AMS_SDMMC_SET_PLLC4_BASE
|
||||
//#define AMS_SDMMC_USE_SD_CARD_DETECTOR
|
||||
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
|
||||
//#define AMS_SDMMC_THREAD_SAFE
|
||||
//#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
|
||||
//#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
|
||||
//#define AMS_SDMMC_USE_DEVICE_DETECTOR
|
||||
//#define AMS_SDMMC_USE_LOGGER
|
||||
//#define AMS_SDMMC_USE_OS_EVENTS
|
||||
//#define AMS_SDMMC_USE_OS_TIMER
|
||||
#define AMS_SDMMC_USE_UTIL_TIMER
|
||||
//#define AMS_SDMMC_ENABLE_MMC_HS400
|
||||
//#define AMS_SDMMC_ENABLE_SD_UHS_I
|
||||
//#define AMS_SDMMC_SET_PLLC4_BASE
|
||||
//#define AMS_SDMMC_USE_SD_CARD_DETECTOR
|
||||
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
#define AMS_SDMMC_THREAD_SAFE
|
||||
#define AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS
|
||||
#define AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL
|
||||
#define AMS_SDMMC_USE_DEVICE_DETECTOR
|
||||
#define AMS_SDMMC_USE_LOGGER
|
||||
#define AMS_SDMMC_USE_OS_EVENTS
|
||||
#define AMS_SDMMC_USE_OS_TIMER
|
||||
//#define AMS_SDMMC_USE_UTIL_TIMER
|
||||
#define AMS_SDMMC_ENABLE_MMC_HS400
|
||||
#define AMS_SDMMC_ENABLE_SD_UHS_I
|
||||
#define AMS_SDMMC_SET_PLLC4_BASE
|
||||
#define AMS_SDMMC_USE_SD_CARD_DETECTOR
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::sdmmc!"
|
||||
#endif
|
114
libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp
Normal file
114
libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/sdmmc/sdmmc_build_config.hpp>
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
enum BusPower {
|
||||
BusPower_Off = 0,
|
||||
BusPower_1_8V = 1,
|
||||
BusPower_3_3V = 2,
|
||||
};
|
||||
|
||||
enum BusWidth {
|
||||
BusWidth_1Bit = 0,
|
||||
BusWidth_4Bit = 1,
|
||||
BusWidth_8Bit = 2,
|
||||
};
|
||||
|
||||
enum SpeedMode {
|
||||
SpeedMode_MmcIdentification = 0,
|
||||
SpeedMode_MmcLegacySpeed = 1,
|
||||
SpeedMode_MmcHighSpeed = 2,
|
||||
SpeedMode_MmcHs200 = 3,
|
||||
SpeedMode_MmcHs400 = 4,
|
||||
SpeedMode_SdCardIdentification = 5,
|
||||
SpeedMode_SdCardDefaultSpeed = 6,
|
||||
SpeedMode_SdCardHighSpeed = 7,
|
||||
SpeedMode_SdCardSdr12 = 8,
|
||||
SpeedMode_SdCardSdr25 = 9,
|
||||
SpeedMode_SdCardSdr50 = 10,
|
||||
SpeedMode_SdCardSdr104 = 11,
|
||||
SpeedMode_SdCardDdr50 = 12,
|
||||
SpeedMode_GcAsicFpgaSpeed = 13,
|
||||
SpeedMode_GcAsicSpeed = 14,
|
||||
};
|
||||
|
||||
enum Port {
|
||||
Port_Mmc0 = 0,
|
||||
Port_SdCard0 = 1,
|
||||
Port_GcAsic0 = 2,
|
||||
};
|
||||
|
||||
struct ErrorInfo {
|
||||
u32 num_activation_failures;
|
||||
u32 num_activation_error_corrections;
|
||||
u32 num_read_write_failures;
|
||||
u32 num_read_write_error_corrections;
|
||||
};
|
||||
|
||||
struct DataTransfer {
|
||||
void *buffer;
|
||||
size_t buffer_size;
|
||||
size_t block_size;
|
||||
u32 num_blocks;
|
||||
bool is_read;
|
||||
};
|
||||
|
||||
using DeviceDetectionEventCallback = void (*)(void *);
|
||||
|
||||
constexpr inline size_t SectorSize = 0x200;
|
||||
|
||||
constexpr inline size_t DeviceCidSize = 0x10;
|
||||
constexpr inline size_t DeviceCsdSize = 0x10;
|
||||
|
||||
constexpr inline size_t BufferDeviceVirtualAddressAlignment = alignof(ams::dd::DeviceVirtualAddress);
|
||||
static_assert(BufferDeviceVirtualAddressAlignment >= 8);
|
||||
|
||||
void Initialize(Port port);
|
||||
void Finalize(Port port);
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
void SwitchToPcvClockResetControl();
|
||||
#endif
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
void RegisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address);
|
||||
void UnregisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address);
|
||||
#endif
|
||||
|
||||
void ChangeCheckTransferInterval(Port port, u32 ms);
|
||||
void SetDefaultCheckTransferInterval(Port port);
|
||||
|
||||
Result Activate(Port port);
|
||||
void Deactivate(Port port);
|
||||
|
||||
Result Read(void *dst, size_t dst_size, Port port, u32 sector_index, u32 num_sectors);
|
||||
Result Write(Port port, u32 sector_index, u32 num_sectors, const void *src, size_t src_size);
|
||||
|
||||
Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port);
|
||||
|
||||
Result GetDeviceSpeedMode(SpeedMode *out, Port port);
|
||||
Result GetDeviceMemoryCapacity(u32 *out_num_sectors, Port port);
|
||||
Result GetDeviceStatus(u32 *out_device_status, Port port);
|
||||
Result GetDeviceCid(void *out, size_t out_size, Port port);
|
||||
Result GetDeviceCsd(void *out, size_t out_size, Port port);
|
||||
|
||||
void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size, Port port);
|
||||
|
||||
}
|
34
libraries/libvapours/include/vapours/sdmmc/sdmmc_gc_asic.hpp
Normal file
34
libraries/libvapours/include/vapours/sdmmc/sdmmc_gc_asic.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/sdmmc/sdmmc_build_config.hpp>
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
constexpr inline size_t GcAsicOperationSize = 0x40;
|
||||
|
||||
void PutGcAsicToSleep(Port port);
|
||||
Result AwakenGcAsic(Port port);
|
||||
Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size);
|
||||
Result FinishGcAsicOperation(Port port);
|
||||
Result AbortGcAsicOperation(Port port);
|
||||
Result SleepGcAsic(Port port);
|
||||
Result UpdateGcAsicKey(Port port);
|
||||
|
||||
void SignalGcRemovedEvent(Port port);
|
||||
void ClearGcRemovedEvent(Port port);
|
||||
|
||||
}
|
43
libraries/libvapours/include/vapours/sdmmc/sdmmc_mmc.hpp
Normal file
43
libraries/libvapours/include/vapours/sdmmc/sdmmc_mmc.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/sdmmc/sdmmc_build_config.hpp>
|
||||
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
enum MmcPartition {
|
||||
MmcPartition_UserData = 0,
|
||||
MmcPartition_BootPartition1 = 1,
|
||||
MmcPartition_BootPartition2 = 2,
|
||||
MmcPartition_Unknown = 3,
|
||||
};
|
||||
|
||||
constexpr inline size_t MmcExtendedCsdSize = 0x200;
|
||||
constexpr inline size_t MmcWorkBufferSize = MmcExtendedCsdSize;
|
||||
|
||||
void SetMmcWorkBuffer(Port port, void *buffer, size_t buffer_size);
|
||||
void PutMmcToSleep(Port port);
|
||||
void AwakenMmc(Port port);
|
||||
Result SelectMmcPartition(Port port, MmcPartition mmc_partition);
|
||||
Result EraseMmc(Port port);
|
||||
Result GetMmcBootPartitionCapacity(u32 *out_num_sectors, Port port);
|
||||
Result GetMmcExtendedCsd(void *out_buffer, size_t buffer_size, Port port);
|
||||
|
||||
Result CheckMmcConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port);
|
||||
|
||||
}
|
53
libraries/libvapours/include/vapours/sdmmc/sdmmc_sd_card.hpp
Normal file
53
libraries/libvapours/include/vapours/sdmmc/sdmmc_sd_card.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/sdmmc/sdmmc_build_config.hpp>
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
enum SdCardSwitchFunction {
|
||||
SdCardSwitchFunction_CheckSupportedFunction = 0,
|
||||
SdCardSwitchFunction_CheckDefault = 1,
|
||||
SdCardSwitchFunction_CheckHighSpeed = 2,
|
||||
SdCardSwitchFunction_CheckSdr50 = 3,
|
||||
SdCardSwitchFunction_CheckSdr104 = 4,
|
||||
SdCardSwitchFunction_CheckDdr50 = 5,
|
||||
};
|
||||
|
||||
constexpr inline size_t SdCardScrSize = 0x08;
|
||||
constexpr inline size_t SdCardSwitchFunctionStatusSize = 0x40;
|
||||
constexpr inline size_t SdCardSdStatusSize = 0x40;
|
||||
|
||||
constexpr inline size_t SdCardWorkBufferSize = SdCardSdStatusSize;
|
||||
|
||||
void SetSdCardWorkBuffer(Port port, void *buffer, size_t buffer_size);
|
||||
void PutSdCardToSleep(Port port);
|
||||
void AwakenSdCard(Port port);
|
||||
Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors, Port port);
|
||||
Result GetSdCardScr(void *dst, size_t dst_size, Port port);
|
||||
Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, Port port, SdCardSwitchFunction switch_function);
|
||||
Result GetSdCardCurrentConsumption(u16 *out_current_consumption, Port port, SpeedMode speed_mode);
|
||||
Result GetSdCardSdStatus(void *dst, size_t dst_size, Port port);
|
||||
|
||||
Result CheckSdCardConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port);
|
||||
|
||||
bool IsSdCardInserted(Port port);
|
||||
bool IsSdCardRemoved(Port port);
|
||||
|
||||
void RegisterSdCardDetectionEventCallback(Port port, DeviceDetectionEventCallback callback, void *arg);
|
||||
void UnregisterSdCardDetectionEventCallback(Port port);
|
||||
|
||||
}
|
39
libraries/libvapours/include/vapours/tegra.hpp
Normal file
39
libraries/libvapours/include/vapours/tegra.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#include <vapours/tegra/tegra_ahb_arbc.hpp>
|
||||
#include <vapours/tegra/tegra_apb_misc.hpp>
|
||||
#include <vapours/tegra/tegra_avp_cache.hpp>
|
||||
#include <vapours/tegra/tegra_clkrst.hpp>
|
||||
#include <vapours/tegra/tegra_emc.hpp>
|
||||
#include <vapours/tegra/tegra_evp.hpp>
|
||||
#include <vapours/tegra/tegra_flow_ctlr.hpp>
|
||||
#include <vapours/tegra/tegra_ictlr.hpp>
|
||||
#include <vapours/tegra/tegra_mc.hpp>
|
||||
#include <vapours/tegra/tegra_mselect.hpp>
|
||||
#include <vapours/tegra/tegra_pinmux.hpp>
|
||||
#include <vapours/tegra/tegra_pg_up.hpp>
|
||||
#include <vapours/tegra/tegra_pmc.hpp>
|
||||
#include <vapours/tegra/tegra_sb.hpp>
|
||||
#include <vapours/tegra/tegra_sysctr0.hpp>
|
||||
#include <vapours/tegra/tegra_timer.hpp>
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define AHB_ARBC(x) (0x6000c000 + x)
|
||||
|
|
@ -14,11 +14,24 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define APB_MISC_PP_CONFIG_CTL (0x024)
|
||||
#define APB_MISC_PP_CONFIG_CTL (0x024)
|
||||
|
||||
#define APB_MISC_GP_ASDBGREG (0x810)
|
||||
#define APB_MISC_GP_ASDBGREG (0x810)
|
||||
|
||||
#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL (0xA98)
|
||||
|
||||
#define APB_MISC_GP_EMMC2_PAD_CFGPADCTRL (0xA9C)
|
||||
#define APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL (0xA9C)
|
||||
|
||||
#define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL (0xAB4)
|
||||
#define APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL (0xABC)
|
||||
|
||||
#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00)
|
||||
#define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00)
|
||||
|
@ -41,6 +54,29 @@ DEFINE_APB_MISC_REG_BIT_ENUM(PP_CONFIG_CTL_TBE, 7, DISABLE, ENABLE);
|
|||
|
||||
DEFINE_APB_MISC_REG(GP_ASDBGREG_CFG2TMC_RAM_SVOP_PDP, 24, 2);
|
||||
|
||||
DEFINE_APB_MISC_REG (GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVDN, 12, 7);
|
||||
DEFINE_APB_MISC_REG (GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVUP, 20, 7);
|
||||
DEFINE_APB_MISC_REG (GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWR, 28, 2);
|
||||
DEFINE_APB_MISC_REG (GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWF, 30, 2);
|
||||
|
||||
DEFINE_APB_MISC_REG_BIT_ENUM(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_E_SCH, 0, DISABLE, ENABLE);
|
||||
DEFINE_APB_MISC_REG (GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVDN_COMP, 2, 6);
|
||||
DEFINE_APB_MISC_REG (GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVUP_COMP, 8, 6);
|
||||
DEFINE_APB_MISC_REG (GP_EMMC2_PAD_CFGPADCTRL_MISC2PMC_EMMC2_ALL_PARK, 14, 13);
|
||||
|
||||
DEFINE_APB_MISC_REG (GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVDN, 12, 7);
|
||||
DEFINE_APB_MISC_REG (GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVUP, 20, 7);
|
||||
|
||||
|
||||
DEFINE_APB_MISC_REG_BIT_ENUM(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_E_SCH, 0, DISABLE, ENABLE);
|
||||
DEFINE_APB_MISC_REG (GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVDN_COMP, 2, 6);
|
||||
DEFINE_APB_MISC_REG (GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVUP_COMP, 8, 6);
|
||||
DEFINE_APB_MISC_REG (GP_EMMC4_PAD_CFGPADCTRL_MISC2PMC_EMMC4_ALL_PARK, 14, 13);
|
||||
|
||||
DEFINE_APB_MISC_REG(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CMD_PUPD_PULLU, 1, 1);
|
||||
DEFINE_APB_MISC_REG(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CLK_PUPD_PULLD, 2, 1);
|
||||
DEFINE_APB_MISC_REG(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DQS_PUPD_PULLD, 22, 1);
|
||||
|
||||
#define DEFINE_SLAVE_SECURITY_REG(RINDEX, INDEX, NAME) DEFINE_APB_MISC_REG_BIT_ENUM(SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG##RINDEX##_##NAME##_SECURITY_EN, INDEX, DISABLE, ENABLE)
|
||||
|
||||
DEFINE_SLAVE_SECURITY_REG(0, 29, STM);
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define AVP_CACHE_ADDRESS(x) (0x50040000 + x)
|
||||
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
/* Clock source enums. */
|
||||
#define CLK_RST_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (CLK_RST_CONTROLLER, NAME)
|
||||
|
@ -33,13 +38,18 @@
|
|||
|
||||
#define CLK_RST_CONTROLLER_MISC_CLK_ENB (0x048)
|
||||
#define CLK_RST_CONTROLLER_OSC_CTRL (0x050)
|
||||
#define CLK_RST_CONTROLLER_PLLD_BASE (0x0D0)
|
||||
#define CLK_RST_CONTROLLER_PLLD_MISC1 (0x0D8)
|
||||
#define CLK_RST_CONTROLLER_PLLD_MISC (0x0DC)
|
||||
#define CLK_RST_CONTROLLER_PLLX_BASE (0x0E0)
|
||||
#define CLK_RST_CONTROLLER_PLLX_MISC (0x0E4)
|
||||
#define CLK_RST_CONTROLLER_CCLKG_BURST_POLICY (0x368)
|
||||
#define CLK_RST_CONTROLLER_SUPER_CCLKG_DIVIDER (0x36C)
|
||||
#define CLK_RST_CONTROLLER_CCLKLP_BURST_POLICY (0x370)
|
||||
#define CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER (0x374)
|
||||
#define CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 (0x388)
|
||||
#define CLK_RST_CONTROLLER_SPARE_REG0 (0x55C)
|
||||
#define CLK_RST_CONTROLLER_PLLC4_BASE (0x5A4)
|
||||
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA (0x0F8)
|
||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB (0x0FC)
|
||||
|
@ -74,6 +84,10 @@ DEFINE_CLK_RST_REG(CPU_SOFTRST_CTRL2_CAR2PMC_CPU_ACK_WIDTH, 0, 12);
|
|||
|
||||
DEFINE_CLK_RST_REG_TWO_BIT_ENUM(SPARE_REG0_CLK_M_DIVISOR, 2, CLK_M_DIVISOR1, CLK_M_DIVISOR2, CLK_M_DIVISOR3, CLK_M_DIVISOR4);
|
||||
|
||||
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_IDDQ, 18, OFF, ON);
|
||||
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_LOCK, 27, NOT_LOCK, LOCK_FEQ_AND_PHASE);
|
||||
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_ENABLE, 30, DISABLE, ENABLE);
|
||||
|
||||
/* RST_DEVICES */
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004)
|
||||
#define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008)
|
||||
|
@ -93,15 +107,24 @@ DEFINE_CLK_RST_REG_TWO_BIT_ENUM(SPARE_REG0_CLK_M_DIVISOR, 2, CLK_M_DIVISOR1, CLK
|
|||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364)
|
||||
|
||||
/* CLK_SOURCE */
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT (0x3B4)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF (0x62C)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC (0x630)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DISP1 (0x138)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 (0x150)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 (0x154)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 (0x164)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 (0x1BC)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE (0x1D4)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT (0x3B4)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP (0x620)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF (0x62C)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC (0x630)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL (0x66C)
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM (0x694)
|
||||
|
||||
/* RST_DEV_*_SET */
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_L_SET (0x300)
|
||||
|
@ -163,6 +186,19 @@ DEFINE_CLK_RST_REG(CLK_SOURCE_I2C1_I2C1_CLK_DIVISOR, 0, 8);
|
|||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C5_I2C5_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||
DEFINE_CLK_RST_REG(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 0, 8);
|
||||
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC1_SDMMC1_CLK_SRC, 29, PLLP_OUT0, PLLA_OUT, PLLC_OUT0, PLLC4_OUT2, PLLM_OUT0, PLLE_OUT0, CLK_M, PLLC4_OUT0);
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC2_SDMMC2_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC4_SDMMC4_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC3_SDMMC3_CLK_SRC, 29, PLLP_OUT0, PLLA_OUT, PLLC_OUT0, PLLC4_OUT2, PLLC4_OUT1, PLLE_OUT0, CLK_M, PLLC4_OUT0);
|
||||
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, 29, PLLP_OUT0, _RSVD1_, _RSVD2_, PLLC4_OUT2, _RSVD4_, _RSVD5_, CLK_M, PLLC4_OUT0);
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
|
||||
|
||||
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC1_SDMMC1_CLK_DIVISOR, 0, 8);
|
||||
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC2_SDMMC2_CLK_DIVISOR, 0, 8);
|
||||
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC4_SDMMC4_CLK_DIVISOR, 0, 8);
|
||||
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC3_SDMMC3_CLK_DIVISOR, 0, 8);
|
||||
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||
|
@ -178,6 +214,9 @@ DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, 29, PLLP
|
|||
DEFINE_CLK_RST_REG(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 0, 8);
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||
|
||||
DEFINE_CLK_RST_REG(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 0, 8);
|
||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_LEGACY_TM_CLK_SRC, 29, PLLP_OUT3, PLLC_OUT0, PLLC2_OUT0, CLK_M, PLLP_OUT0, PLLC4_OUT0, PLLC4_OUT1, PLLC4_OUT2);
|
||||
|
||||
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_SET_SET_COP_RST, 1, DISABLE, ENABLE);
|
||||
|
||||
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_CLR_CLR_COP_RST, 1, DISABLE, ENABLE);
|
||||
|
@ -198,6 +237,9 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA
|
|||
HANDLER(L, RTC, 0, 4) \
|
||||
HANDLER(L, TMR, 0, 5) \
|
||||
HANDLER(L, GPIO, 0, 8) \
|
||||
HANDLER(L, SDMMC2, 0, 9) \
|
||||
HANDLER(L, SDMMC1, 0, 14) \
|
||||
HANDLER(L, SDMMC4, 0, 15) \
|
||||
HANDLER(L, USBD, 0, 22) \
|
||||
HANDLER(L, CACHE2, 0, 31) \
|
||||
HANDLER(H, MEM, 1, 0) \
|
||||
|
@ -209,6 +251,7 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA
|
|||
HANDLER(H, I2C5, 1, 15) \
|
||||
HANDLER(H, EMC, 1, 25) \
|
||||
HANDLER(H, USB2, 1, 26) \
|
||||
HANDLER(U, SDMMC3, 2, 5) \
|
||||
HANDLER(U, CSITE, 2, 9) \
|
||||
HANDLER(U, IRAMA, 2, 20) \
|
||||
HANDLER(U, IRAMB, 2, 21) \
|
||||
|
@ -238,6 +281,7 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA
|
|||
HANDLER(X, GPU, 5, 24) \
|
||||
HANDLER(X, DBGAPB, 5, 25) \
|
||||
HANDLER(X, PLLG_REF, 5, 29) \
|
||||
HANDLER(Y, LEGACY_TM, 6, 1) \
|
||||
HANDLER(Y, MC_CCPA, 6, 8) \
|
||||
HANDLER(Y, MC_CDPA, 6, 9) \
|
||||
HANDLER(Y, PLLP_OUT_CPU, 6, 31)
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define EMC_ADDRESS(x) (0x7001B000 + x)
|
||||
#define EMC0_ADDRESS(x) (0x7001E000 + x)
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define EVP_CPU_RESET_VECTOR (0x100)
|
||||
|
|
@ -14,12 +14,21 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define FLOW_CTLR_RAM_REPAIR (0x040)
|
||||
#define FLOW_CTLR_FLOW_DBG_QUAL (0x050)
|
||||
#define FLOW_CTLR_CC4_HVC_CONTROL (0x060)
|
||||
#define FLOW_CTLR_CC4_RETENTION_CONTROL (0x064)
|
||||
#define FLOW_CTLR_CC4_HVC_RETRY (0x08C)
|
||||
#define FLOW_CTLR_L2FLUSH_CONTROL (0x094)
|
||||
#define FLOW_CTLR_BPMP_CLUSTER_CONTROL (0x098)
|
||||
|
||||
#define FLOW_CTLR_RAM_REPAIR (0x040)
|
||||
#define FLOW_CTLR_FLOW_DBG_QUAL (0x050)
|
||||
#define FLOW_CTLR_L2FLUSH_CONTROL (0x094)
|
||||
#define FLOW_CTLR_BPMP_CLUSTER_CONTROL (0x098)
|
||||
|
||||
#define FLOW_CTLR_CPU0_CSR (0x008)
|
||||
#define FLOW_CTLR_CPU1_CSR (0x018)
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define PRI_ICTLR(n) (0x60004000 + n)
|
||||
#define SEC_ICTLR(n) (0x60004100 + n)
|
|
@ -14,8 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <exosphere/reg.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define MC_INTSTATUS (0x000)
|
||||
#define MC_INTMASK (0x004)
|
||||
|
@ -25,7 +29,7 @@
|
|||
#define MC_SMMU_PTB_ASID (0x01C)
|
||||
#define MC_SMMU_PTB_DATA (0x020)
|
||||
#define MC_SMMU_TLB_FLUSH (0x030)
|
||||
#define MC_SMMU_PTC_FLUSH (0x034)
|
||||
#define MC_SMMU_PTC_FLUSH_0 (0x034)
|
||||
#define MC_EMEM_CFG (0x050)
|
||||
#define MC_EMEM_ADR_CFG (0x054)
|
||||
#define MC_EMEM_ARB_CFG (0x090)
|
||||
|
@ -56,7 +60,7 @@
|
|||
#define MC_SMMU_HC_ASID (0x250)
|
||||
#define MC_SMMU_HDA_ASID (0x254)
|
||||
#define MC_SMMU_ISP2_ASID (0x258)
|
||||
#define MC_SMMU_NVENC_ASID (0x264)
|
||||
#define MC_SMMU_MSENC_NVENC_ASID (0x264)
|
||||
#define MC_SMMU_NV_ASID (0x268)
|
||||
#define MC_SMMU_NV2_ASID (0x26C)
|
||||
#define MC_SMMU_PPCS_ASID (0x270)
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define MSELECT(x) (0x50060000 + x)
|
||||
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define PG_UP(x) (0x60000000 + x)
|
||||
|
|
@ -14,7 +14,21 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define PINMUX_AUX_SDMMC1_CLK (0x3000)
|
||||
#define PINMUX_AUX_SDMMC1_CMD (0x3004)
|
||||
#define PINMUX_AUX_SDMMC1_DAT3 (0x3008)
|
||||
#define PINMUX_AUX_SDMMC1_DAT2 (0x300C)
|
||||
#define PINMUX_AUX_SDMMC1_DAT1 (0x3010)
|
||||
#define PINMUX_AUX_SDMMC1_DAT0 (0x3014)
|
||||
|
||||
#define PINMUX_AUX_DMIC3_CLK (0x30B4)
|
||||
|
||||
#define PINMUX_AUX_GEN1_I2C_SCL (0x30BC)
|
||||
#define PINMUX_AUX_GEN1_I2C_SDA (0x30C0)
|
||||
|
@ -34,6 +48,11 @@
|
|||
#define PINMUX_AUX_UART3_RTS (0x310C)
|
||||
#define PINMUX_AUX_UART3_CTS (0x3110)
|
||||
#define PINMUX_AUX_DVFS_PWM (0x3184)
|
||||
#define PINMUX_AUX_NFC_EN (0x31D0)
|
||||
#define PINMUX_AUX_NFC_INT (0x31D4)
|
||||
#define PINMUX_AUX_LCD_BL_PWM (0x31FC)
|
||||
#define PINMUX_AUX_LCD_BL_EN (0x3200)
|
||||
#define PINMUX_AUX_LCD_RST (0x3204)
|
||||
#define PINMUX_AUX_GPIO_PA6 (0x3244)
|
||||
|
||||
#define PINMUX_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PINMUX, NAME)
|
||||
|
@ -56,6 +75,16 @@ DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_LPDR, 8, DISABLE, ENABLE);
|
|||
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_OD, 11, DISABLE, ENABLE);
|
||||
DEFINE_PINMUX_REG_BIT_ENUM(AUX_E_SCHMT, 12, DISABLE, ENABLE);
|
||||
|
||||
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_CLK_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_CMD_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_DAT3_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_DAT2_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_DAT1_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_SDMMC1_DAT0_PM, 0, SDMMC1, RSVD1, RSVD2, RSVD3);
|
||||
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_DMIC3_CLK_PM, 0, DMIC3, I2S5A, RSVD2, RSVD3);
|
||||
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GEN1_I2C_PM, 0, I2C1, RSVD1, RSVD2, RSVD3);
|
||||
DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_PWR_I2C_PM, 0, I2CPMU, RSVD1, RSVD2, RSVD3);
|
||||
|
|
@ -14,8 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <exosphere/reg.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define APBDEV_PMC_CNTRL (0x000)
|
||||
#define APBDEV_PMC_WAKE_MASK (0x00C)
|
||||
|
@ -34,6 +38,7 @@
|
|||
#define APBDEV_PMC_PWR_DET (0x048)
|
||||
#define APBDEV_PMC_SCRATCH0 (0x050)
|
||||
#define APBDEV_PMC_SCRATCH1 (0x054)
|
||||
#define APBDEV_PMC_SCRATCH4 (0x060)
|
||||
#define APBDEV_PMC_SCRATCH12 (0x080)
|
||||
#define APBDEV_PMC_SCRATCH13 (0x084)
|
||||
#define APBDEV_PMC_SCRATCH18 (0x098)
|
||||
|
@ -45,6 +50,7 @@
|
|||
#define APBDEV_PMC_SCRATCH31 (0x118)
|
||||
#define APBDEV_PMC_SCRATCH32 (0x11C)
|
||||
#define APBDEV_PMC_SCRATCH33 (0x120)
|
||||
#define APBDEV_PMC_SCRATCH39 (0x138)
|
||||
#define APBDEV_PMC_SCRATCH40 (0x13C)
|
||||
#define APBDEV_PMC_WAKE2_MASK (0x160)
|
||||
#define APBDEV_PMC_WAKE2_LVL (0x164)
|
||||
|
@ -198,6 +204,10 @@ DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE2, 29, OFF, ON);
|
|||
|
||||
DEFINE_PMC_REG(PWRGATE_STATUS_CE123, 9, 3);
|
||||
|
||||
DEFINE_PMC_REG_BIT_ENUM(NO_IOPOWER_SDMMC1, 12, DISABLE, ENABLE);
|
||||
DEFINE_PMC_REG_BIT_ENUM(PWR_DET_SDMMC1, 12, DISABLE, ENABLE);
|
||||
DEFINE_PMC_REG_BIT_ENUM(PWR_DET_VAL_SDMMC1, 12, DISABLE, ENABLE);
|
||||
|
||||
DEFINE_PMC_REG(SET_SW_CLAMP_CRAIL, 0, 1);
|
||||
|
||||
DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3);
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define SB_CSR (0x200)
|
||||
#define SB_PFCFG (0x208)
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define SYSCTR0_CNTCR (0x000)
|
||||
#define SYSCTR0_CNTCV0 (0x008)
|
|
@ -14,7 +14,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
#include <vapours/literals.hpp>
|
||||
#include <vapours/util.hpp>
|
||||
#include <vapours/results.hpp>
|
||||
#include <vapours/reg.hpp>
|
||||
|
||||
#define TIMERUS_USEC_CFG (0x014)
|
||||
#define TIMER_SHARED_TIMER_SECURE_CFG (0x1A4)
|
|
@ -34,8 +34,10 @@
|
|||
#include <vapours/util/util_intrusive_list.hpp>
|
||||
#include <vapours/util/util_intrusive_red_black_tree.hpp>
|
||||
#include <vapours/util/util_tinymt.hpp>
|
||||
#include <vapours/util/util_timer.hpp>
|
||||
#include <vapours/util/util_uuid.hpp>
|
||||
#include <vapours/util/util_bounded_map.hpp>
|
||||
#include <vapours/util/util_overlap.hpp>
|
||||
#include <vapours/util/util_string_util.hpp>
|
||||
#include <vapours/util/util_variadic.hpp>
|
||||
#include <vapours/util/util_format_string.hpp>
|
||||
|
|
|
@ -73,4 +73,9 @@ namespace ams::util {
|
|||
return IsAligned(reinterpret_cast<uintptr_t>(value), alignment);
|
||||
}
|
||||
|
||||
}
|
||||
template<typename T, typename U> requires std::integral<T> && std::integral<U>
|
||||
constexpr ALWAYS_INLINE T DivideUp(T x, U y) {
|
||||
return (x + (y - 1)) / y;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
int SNPrintf(char *dst, size_t dst_size, const char *fmt, ...);
|
||||
int VSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl);
|
||||
|
||||
int TSNPrintf(char *dst, size_t dst_size, const char *fmt, ...);
|
||||
int TVSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl);
|
||||
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License
|
||||
* 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
|
||||
|
@ -13,8 +13,14 @@
|
|||
* 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
|
||||
|
||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE 0x1D4
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_L_SET 0x300
|
||||
#define CLK_RST_CONTROLLER_RST_DEV_L_CLR 0x304
|
||||
#pragma once
|
||||
#include <vapours/common.hpp>
|
||||
#include <vapours/assert.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
u32 GetMicroSeconds();
|
||||
void WaitMicroSeconds(int us);
|
||||
|
||||
}
|
41
libraries/libvapours/source/dd/dd_cache.cpp
Normal file
41
libraries/libvapours/source/dd/dd_cache.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "impl/dd_select_cache_impl.hpp"
|
||||
|
||||
namespace ams::dd {
|
||||
|
||||
void InvalidateDataCache(void *addr, size_t size) {
|
||||
return impl::InvalidateDataCacheImpl(addr, size);
|
||||
}
|
||||
|
||||
void StoreDataCache(void *addr, size_t size) {
|
||||
return impl::StoreDataCacheImpl(addr, size);
|
||||
}
|
||||
|
||||
void FlushDataCache(void *addr, size_t size) {
|
||||
return impl::FlushDataCacheImpl(addr, size);
|
||||
}
|
||||
|
||||
}
|
101
libraries/libvapours/source/dd/dd_io_mapping.cpp
Normal file
101
libraries/libvapours/source/dd/dd_io_mapping.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::dd {
|
||||
|
||||
uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* TODO: Secure Monitor translation? */
|
||||
AMS_UNUSED(size);
|
||||
return static_cast<uintptr_t>(phys_addr);
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
/* TODO: BPMP translation? */
|
||||
AMS_UNUSED(size);
|
||||
return static_cast<uintptr_t>(phys_addr);
|
||||
#else
|
||||
#error "Unknown architecture for ams::dd::QueryIoMapping (EXOSPHERE)!"
|
||||
#endif
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
/* TODO: Kernel address translation? */
|
||||
AMS_UNUSED(size);
|
||||
return static_cast<uintptr_t>(phys_addr);
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
svc::Address virt_addr = 0;
|
||||
const dd::PhysicalAddress aligned_addr = util::AlignDown(phys_addr, os::MemoryPageSize);
|
||||
const size_t offset = phys_addr - aligned_addr;
|
||||
const size_t aligned_size = size + offset;
|
||||
|
||||
if (hos::GetVersion() >= hos::Version_10_0_0) {
|
||||
svc::Size region_size = 0;
|
||||
R_TRY_CATCH(svc::QueryIoMapping(&virt_addr, ®ion_size, aligned_addr, aligned_size)) {
|
||||
/* Official software handles this by returning 0. */
|
||||
R_CATCH(svc::ResultNotFound) { return 0; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
AMS_ASSERT(region_size >= aligned_size);
|
||||
} else {
|
||||
R_TRY_CATCH(svc::LegacyQueryIoMapping(&virt_addr, aligned_addr, aligned_size)) {
|
||||
/* Official software handles this by returning 0. */
|
||||
R_CATCH(svc::ResultNotFound) { return 0; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return static_cast<uintptr_t>(virt_addr) + offset;
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::QueryIoMapping!"
|
||||
#endif
|
||||
}
|
||||
|
||||
u32 ReadIoRegister(dd::PhysicalAddress phys_addr) {
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
return reg::Read(dd::QueryIoMapping(phys_addr, sizeof(u32)));
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
u32 val;
|
||||
R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(val), phys_addr, 0, 0));
|
||||
return val;
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::ReadIoRegister!"
|
||||
#endif
|
||||
}
|
||||
|
||||
void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value) {
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
reg::Write(dd::QueryIoMapping(phys_addr, sizeof(u32)), value);
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
u32 out_val;
|
||||
R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(out_val), phys_addr, 0xFFFFFFFF, value));
|
||||
AMS_UNUSED(out_val);
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::WriteIoRegister!"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::dd::impl {
|
||||
|
||||
void StoreDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* On aarch64, we can use cache maintenance instructions. */
|
||||
|
||||
/* Get cache line size. */
|
||||
uintptr_t ctr_el0 = 0;
|
||||
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
|
||||
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
|
||||
|
||||
/* Invalidate the cache. */
|
||||
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__("dc cvac, %[cur]" : : [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Add a memory barrier. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#else
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
/* Invoke the relevant svc. */
|
||||
const auto result = svc::StoreProcessDataCache(svc::PseudoHandle::CurrentProcess, reinterpret_cast<uintptr_t>(addr), size);
|
||||
R_ASSERT(result);
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE) && defined(__BPMP__)
|
||||
/* Do nothing. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::impl::StoreDataCacheImpl"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlushDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* On aarch64, we can use cache maintenance instructions. */
|
||||
|
||||
/* Get cache line size. */
|
||||
uintptr_t ctr_el0 = 0;
|
||||
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
|
||||
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
|
||||
|
||||
/* Invalidate the cache. */
|
||||
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__("dc civac, %[cur]" : : [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Add a memory barrier. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#else
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
/* Invoke the relevant svc. */
|
||||
const auto result = svc::FlushProcessDataCache(svc::PseudoHandle::CurrentProcess, reinterpret_cast<uintptr_t>(addr), size);
|
||||
R_ASSERT(result);
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE) && defined(__BPMP__)
|
||||
/* Do nothing. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::impl::FlushDataCacheImpl"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void InvalidateDataCacheImpl(void *addr, size_t size) {
|
||||
/* Just perform a flush, which is clean + invalidate. */
|
||||
return FlushDataCacheImpl(addr, size);
|
||||
}
|
||||
|
||||
}
|
22
libraries/libvapours/source/dd/impl/dd_select_cache_impl.hpp
Normal file
22
libraries/libvapours/source/dd/impl/dd_select_cache_impl.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "dd_cache_impl.os.horizon.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::dd::CacheImpl"
|
||||
#endif
|
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
#define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX() std::scoped_lock lk(this->base_device->device_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
void BaseDevice::GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const {
|
||||
AMS_ABORT_UNLESS(out_c_size_mult != nullptr);
|
||||
AMS_ABORT_UNLESS(out_read_bl_len != nullptr);
|
||||
|
||||
/* Extract C_SIZE_MULT and READ_BL_LEN from the CSD. */
|
||||
*out_c_size_mult = static_cast<u8>((this->csd[2] >> 7) & 0x7);
|
||||
*out_read_bl_len = static_cast<u8>((this->csd[4] >> 8) & 0xF);
|
||||
}
|
||||
|
||||
Result BaseDevice::SetLegacyMemoryCapacity() {
|
||||
/* Get csize from the csd. */
|
||||
const u32 c_size = ((this->csd[3] >> 6) & 0x3FF) | ((this->csd[4] & 0x3) << 10);
|
||||
|
||||
/* Get c_size_mult and read_bl_len. */
|
||||
u8 c_size_mult, read_bl_len;
|
||||
this->GetLegacyCapacityParameters(std::addressof(c_size_mult), std::addressof(read_bl_len));
|
||||
|
||||
/* Validate the parameters. */
|
||||
R_UNLESS((read_bl_len + c_size_mult + 2) >= 9, sdmmc::ResultUnexpectedDeviceCsdValue());
|
||||
|
||||
/* Set memory capacity. */
|
||||
this->memory_capacity = (c_size + 1) << ((read_bl_len + c_size_mult + 2) - 9);
|
||||
this->is_valid_memory_capacity = true;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDevice::CheckDeviceStatus(u32 r1_resp) const {
|
||||
/* Check if there are any errors at all. */
|
||||
R_SUCCEED_IF((r1_resp & DeviceStatus_ErrorMask) == 0);
|
||||
|
||||
/* Check errors individually. */
|
||||
#define AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(__ERROR__) R_UNLESS((r1_resp & DeviceStatus_##__ERROR__) == 0, sdmmc::ResultDeviceStatus##__ERROR__())
|
||||
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(ComCrcError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(DeviceEccFailed);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CcError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(Error);
|
||||
|
||||
if (this->GetDeviceType() == DeviceType_Mmc) {
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(SwitchError);
|
||||
}
|
||||
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressMisaligned);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(BlockLenError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseSeqError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseParam);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpViolation);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(LockUnlockFailed);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CidCsdOverwrite);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseReset);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(IllegalCommand);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressOutOfRange);
|
||||
|
||||
#undef AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
DeviceState BaseDevice::GetDeviceState(u32 r1_resp) const {
|
||||
return static_cast<DeviceState>((r1_resp & DeviceStatus_CurrentStateMask) >> DeviceStatus_CurrentStateShift);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const {
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(command_index, command_arg, CommandResponseType, is_busy);
|
||||
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Get the response. */
|
||||
AMS_ABORT_UNLESS(out_response != nullptr);
|
||||
this->host_controller->GetLastResponse(out_response, sizeof(u32), CommandResponseType);
|
||||
|
||||
/* Mask out the ignored status bits. */
|
||||
if (status_ignore_mask != 0) {
|
||||
*out_response &= ~status_ignore_mask;
|
||||
}
|
||||
|
||||
/* Check the r1 response for errors. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
R_TRY(this->base_device->CheckDeviceStatus(*out_response));
|
||||
|
||||
/* Check the device state. */
|
||||
if (expected_state != DeviceState_Unknown) {
|
||||
R_UNLESS(this->base_device->GetDeviceState(*out_response) == expected_state, sdmmc::ResultUnexpectedDeviceState());
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandGoIdleState() const {
|
||||
/* Issue the command. */
|
||||
Command command(CommandIndex_GoIdleState, 0, ResponseType_R0, false);
|
||||
return this->host_controller->IssueCommand(std::addressof(command));
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandAllSendCid(void *dst, size_t dst_size) const {
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R2;
|
||||
Command command(CommandIndex_AllSendCid, 0, CommandResponseType, false);
|
||||
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Copy the data out. */
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(dst), alignof(u32)));
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
|
||||
this->host_controller->GetLastResponse(static_cast<u32 *>(dst), DeviceCidSize, CommandResponseType);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSelectCard() const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
return this->IssueCommandAndCheckR1(CommandIndex_SelectCard, arg, true, DeviceState_Unknown);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSendCsd(void *dst, size_t dst_size) const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R2;
|
||||
Command command(CommandIndex_SendCsd, arg, CommandResponseType, false);
|
||||
R_TRY(this->host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Copy the data out. */
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(dst), alignof(u32)));
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
|
||||
this->host_controller->GetLastResponse(static_cast<u32 *>(dst), DeviceCsdSize, CommandResponseType);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(this->base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
return this->IssueCommandAndCheckR1(out_device_status, CommandIndex_SendStatus, arg, false, DeviceState_Tran, status_ignore_mask);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize() const {
|
||||
/* Issue the command. */
|
||||
return this->IssueCommandAndCheckR1(CommandIndex_SetBlockLen, SectorSize, false, DeviceState_Tran);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const {
|
||||
/* Get the argument. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
const u32 arg = this->base_device->IsHighCapacity() ? sector_index : sector_index * SectorSize;
|
||||
|
||||
/* Get the command index and transfer direction. */
|
||||
const u32 command_index = is_read ? CommandIndex_ReadMultipleBlock : CommandIndex_WriteMultipleBlock;
|
||||
const auto xfer_direction = is_read ? TransferDirection_ReadFromDevice : TransferDirection_WriteToDevice;
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(command_index, arg, CommandResponseType, false);
|
||||
TransferData xfer_data(buf, SectorSize, num_sectors, xfer_direction, true, true);
|
||||
Result result = this->host_controller->IssueCommand(std::addressof(command), std::addressof(xfer_data), out_num_transferred_blocks);
|
||||
|
||||
/* Handle the failure case. */
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* By default, we'll want to return the result we just got. */
|
||||
Result result_to_return = result;
|
||||
|
||||
/* Stop transmission. */
|
||||
u32 resp = 0;
|
||||
result = this->host_controller->IssueStopTransmissionCommand(std::addressof(resp));
|
||||
if (R_SUCCEEDED(result)) {
|
||||
result = this->base_device->CheckDeviceStatus(resp & (~DeviceStatus_IllegalCommand));
|
||||
if (R_FAILED(result)) {
|
||||
result_to_return = result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Get the device status. */
|
||||
u32 device_status = 0;
|
||||
result = this->IssueCommandSendStatus(std::addressof(device_status), DeviceStatus_IllegalCommand);
|
||||
|
||||
/* If there's a device status error we don't already have, we prefer to return it. */
|
||||
if (!sdmmc::ResultDeviceStatusHasError::Includes(result_to_return) && sdmmc::ResultDeviceStatusHasError::Includes(result)) {
|
||||
result_to_return = result;
|
||||
}
|
||||
|
||||
/* Return the result we chose. */
|
||||
return result_to_return;
|
||||
}
|
||||
|
||||
/* Get the responses. */
|
||||
u32 resp, st_resp;
|
||||
this->host_controller->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
|
||||
this->host_controller->GetLastStopTransmissionResponse(std::addressof(st_resp), sizeof(st_resp));
|
||||
|
||||
/* Check the device status. */
|
||||
R_TRY(this->base_device->CheckDeviceStatus(resp));
|
||||
|
||||
/* Decide on what errors to ignore. */
|
||||
u32 status_ignore_mask = 0;
|
||||
if (is_read) {
|
||||
AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr);
|
||||
if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) {
|
||||
status_ignore_mask = DeviceStatus_AddressOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the device status. */
|
||||
R_TRY(this->base_device->CheckDeviceStatus(st_resp & ~status_ignore_mask));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const {
|
||||
/* Issue the read/write command. */
|
||||
AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr);
|
||||
R_TRY(this->IssueCommandMultipleBlock(out_num_transferred_blocks, sector_index, num_sectors, buf, is_read));
|
||||
|
||||
/* Decide on what errors to ignore. */
|
||||
u32 status_ignore_mask = 0;
|
||||
if (is_read) {
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
if ((*out_num_transferred_blocks + sector_index) == this->base_device->GetMemoryCapacity()) {
|
||||
status_ignore_mask = DeviceStatus_AddressOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get and check the status. */
|
||||
u32 device_status;
|
||||
R_TRY(this->IssueCommandSendStatus(std::addressof(device_status), status_ignore_mask));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read) {
|
||||
/* Verify that we can send the command. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
|
||||
/* If we want to read zero sectors, there's no work for us to do. */
|
||||
R_SUCCEED_IF(num_sectors == 0);
|
||||
|
||||
/* Check that the buffer is big enough for the sectors we're reading. */
|
||||
AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors);
|
||||
|
||||
/* Read sectors repeatedly until we've read all the ones we want. */
|
||||
u32 cur_sector_index = sector_index;
|
||||
u32 remaining_sectors = num_sectors;
|
||||
u8 *cur_buf = static_cast<u8 *>(buf);
|
||||
while (remaining_sectors > 0) {
|
||||
/* Determine how many sectors we can read in this iteration. */
|
||||
u32 cur_sectors = remaining_sectors;
|
||||
if (sector_index_alignment > 0) {
|
||||
AMS_ABORT_UNLESS((cur_sector_index % sector_index_alignment) == 0);
|
||||
|
||||
const u32 max_sectors = this->host_controller->GetMaxTransferNumBlocks();
|
||||
if (remaining_sectors > max_sectors) {
|
||||
cur_sectors = max_sectors - (max_sectors % sector_index_alignment);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to perform the read/write. */
|
||||
u32 num_transferred_blocks = 0;
|
||||
Result result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Log that we failed to read/write. */
|
||||
this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
|
||||
/* Retry the read/write. */
|
||||
num_transferred_blocks = 0;
|
||||
result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Log that we failed to read/write. */
|
||||
this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
|
||||
/* Re-startup the connection, to see if that helps. */
|
||||
R_TRY(this->ReStartup());
|
||||
|
||||
/* Retry the read/write a third time. */
|
||||
num_transferred_blocks = 0;
|
||||
result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Log that we failed after a re-startup. */
|
||||
this->PushErrorLog(true, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Log that we succeeded after a retry. */
|
||||
this->PushErrorLog(true, "%s %X %X:0", is_read ? "R" : "W", cur_sector_index, cur_sectors);
|
||||
|
||||
/* Increment the number of error corrections we've done. */
|
||||
++this->num_read_write_error_corrections;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update our tracking variables. */
|
||||
AMS_ABORT_UNLESS(remaining_sectors >= num_transferred_blocks);
|
||||
remaining_sectors -= num_transferred_blocks;
|
||||
cur_sector_index += num_transferred_blocks;
|
||||
cur_buf += num_transferred_blocks * SectorSize;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
void BaseDeviceAccessor::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Register the address. */
|
||||
return this->host_controller->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Register the address. */
|
||||
return this->host_controller->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
|
||||
}
|
||||
#endif
|
||||
|
||||
Result BaseDeviceAccessor::Activate() {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is awake. */
|
||||
R_UNLESS(this->base_device->IsAwake(), sdmmc::ResultNotAwakened());
|
||||
|
||||
/* If the device is already active, we don't need to do anything. */
|
||||
R_SUCCEED_IF(this->base_device->IsActive());
|
||||
|
||||
/* Activate the base device. */
|
||||
auto activate_guard = SCOPE_GUARD { ++this->num_activation_failures; };
|
||||
R_TRY(this->OnActivate());
|
||||
|
||||
/* We successfully activated the device. */
|
||||
activate_guard.Cancel();
|
||||
this->base_device->SetActive();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::Deactivate() {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Deactivate the base device. */
|
||||
if (this->base_device->IsActive()) {
|
||||
this->host_controller->Shutdown();
|
||||
this->base_device->Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Perform the read/write. */
|
||||
auto rw_guard = SCOPE_GUARD { ++this->num_read_write_failures; };
|
||||
R_TRY(this->OnReadWrite(sector_index, num_sectors, buffer, buffer_size, is_read));
|
||||
|
||||
/* We successfully performed the read/write. */
|
||||
rw_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the current speed mode/bus width. */
|
||||
*out_speed_mode = this->host_controller->GetSpeedMode();
|
||||
*out_bus_width = this->host_controller->GetBusWidth();
|
||||
|
||||
/* Verify that we can get the status. */
|
||||
R_TRY(this->host_controller->GetInternalStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetMemoryCapacity(u32 *out_sectors) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the capacity. */
|
||||
AMS_ABORT_UNLESS(out_sectors != nullptr);
|
||||
*out_sectors = this->base_device->GetMemoryCapacity();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetDeviceStatus(u32 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the status. */
|
||||
R_TRY(this->IssueCommandSendStatus(out, 0));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetOcr(u32 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the ocr. */
|
||||
AMS_ABORT_UNLESS(out != nullptr);
|
||||
*out = this->base_device->GetOcr();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetRca(u16 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the rca. */
|
||||
AMS_ABORT_UNLESS(out != nullptr);
|
||||
*out = this->base_device->GetRca();
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetCid(void *out, size_t size) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the cid. */
|
||||
this->base_device->GetCid(out, size);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetCsd(void *out, size_t size) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(this->base_device->CheckAccessible());
|
||||
|
||||
/* Get the csd. */
|
||||
this->base_device->GetCsd(out, size);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(this->base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Set the output error info. */
|
||||
AMS_ABORT_UNLESS(out_error_info != nullptr);
|
||||
out_error_info->num_activation_failures = this->num_activation_failures;
|
||||
out_error_info->num_activation_error_corrections = this->num_activation_error_corrections;
|
||||
out_error_info->num_read_write_failures = this->num_read_write_failures;
|
||||
out_error_info->num_read_write_error_corrections = this->num_read_write_error_corrections;
|
||||
this->ClearErrorInfo();
|
||||
|
||||
/* Check if we should write logs. */
|
||||
if (out_log_size == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we can write logs. */
|
||||
if (out_log_buffer == nullptr || log_buffer_size == 0) {
|
||||
*out_log_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get and clear our logs. */
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
if (this->error_logger.HasLog()) {
|
||||
this->PushErrorTimeStamp();
|
||||
|
||||
*out_log_size = this->error_logger.GetAndClearLogs(out_log_buffer, log_buffer_size);
|
||||
} else {
|
||||
*out_log_size = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
*out_log_size = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,512 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
#include "sdmmc_i_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
class Logger {
|
||||
private:
|
||||
static constexpr size_t LogLengthMax = 0x20;
|
||||
static constexpr size_t LogCountMax = 0x10;
|
||||
private:
|
||||
char logs[LogCountMax][LogLengthMax];
|
||||
int log_index;
|
||||
private:
|
||||
void Clear() {
|
||||
for (size_t i = 0; i < LogCountMax; ++i) {
|
||||
this->logs[i][0] = '\0';
|
||||
}
|
||||
this->log_index = 0;
|
||||
}
|
||||
|
||||
size_t Pop(char *dst, size_t dst_size) {
|
||||
/* Decrease log index. */
|
||||
if ((--this->log_index) < 0) {
|
||||
this->log_index = LogCountMax - 1;
|
||||
}
|
||||
|
||||
/* Check if we have a log. */
|
||||
if (this->logs[this->log_index][0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy log to output. */
|
||||
const int len = ::ams::util::Strlcpy(dst, this->logs[this->log_index], dst_size);
|
||||
|
||||
/* Clear the log we copied. */
|
||||
this->logs[this->log_index][0] = '\0';
|
||||
|
||||
return static_cast<size_t>(len);
|
||||
}
|
||||
|
||||
public:
|
||||
Logger() {
|
||||
this->Clear();
|
||||
}
|
||||
|
||||
void Push(const char *fmt, std::va_list vl) {
|
||||
/* Format the log into the current buffer. */
|
||||
::ams::util::TVSNPrintf(this->logs[this->log_index], LogLengthMax, fmt, vl);
|
||||
|
||||
/* Update our log index. */
|
||||
if ((++this->log_index) >= static_cast<int>(LogCountMax)) {
|
||||
this->log_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Push(const char *fmt, ...) {
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
this->Push(fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
bool HasLog() const {
|
||||
const int index = this->log_index > 0 ? this->log_index - 1 : static_cast<int>(LogCountMax - 1);
|
||||
return this->logs[index][0] != '\0';
|
||||
}
|
||||
|
||||
size_t GetAndClearLogs(char *dst, size_t dst_size) {
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size > 0);
|
||||
|
||||
/* Pop logs until we run out of them. */
|
||||
size_t total_len = 0;
|
||||
while (true) {
|
||||
/* Pop the current log. */
|
||||
const size_t cur_len = this->Pop(dst + total_len, dst_size - total_len);
|
||||
if (cur_len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if the log exceeded the buffer size. */
|
||||
if (total_len + cur_len + 1 >= dst_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Advance the total length. */
|
||||
total_len += cur_len;
|
||||
|
||||
/* Check if there's space for our separator. */
|
||||
if (total_len + 3 >= dst_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
dst[total_len + 0] = ',';
|
||||
dst[total_len + 1] = ' ';
|
||||
total_len += 2;
|
||||
}
|
||||
|
||||
/* Ensure that the complete log fits in the buffer. */
|
||||
if (total_len >= dst_size) {
|
||||
total_len = dst_size - 1;
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
dst[total_len] = '\0';
|
||||
|
||||
/* Clear any remaining logs. */
|
||||
this->Clear();
|
||||
|
||||
/* Return the length of the logs, including null terminator. */
|
||||
return total_len + 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
enum DeviceType {
|
||||
DeviceType_Mmc = 0,
|
||||
DeviceType_SdCard = 1,
|
||||
DeviceType_GcAsic = 2,
|
||||
};
|
||||
|
||||
enum DeviceState {
|
||||
DeviceState_Idle = 0,
|
||||
DeviceState_Ready = 1,
|
||||
DeviceState_Ident = 2,
|
||||
DeviceState_Stby = 3,
|
||||
DeviceState_Tran = 4,
|
||||
DeviceState_Data = 5,
|
||||
DeviceState_Rcv = 6,
|
||||
DeviceState_Prg = 7,
|
||||
DeviceState_Dis = 8,
|
||||
DeviceState_Rsvd0 = 9,
|
||||
DeviceState_Rsvd1 = 10,
|
||||
DeviceState_Rsvd2 = 11,
|
||||
DeviceState_Rsvd3 = 12,
|
||||
DeviceState_Rsvd4 = 13,
|
||||
DeviceState_Rsvd5 = 14,
|
||||
DeviceState_RsvdIoMode = 15,
|
||||
DeviceState_Unknown = 16,
|
||||
};
|
||||
|
||||
enum DeviceStatus : u32 {
|
||||
DeviceStatus_AkeSeqError = (1u << 3),
|
||||
DeviceStatus_AppCmd = (1u << 5),
|
||||
DeviceStatus_SwitchError = (1u << 7),
|
||||
DeviceStatus_EraseReset = (1u << 13),
|
||||
DeviceStatus_WpEraseSkip = (1u << 15),
|
||||
DeviceStatus_CidCsdOverwrite = (1u << 16),
|
||||
DeviceStatus_Error = (1u << 19),
|
||||
DeviceStatus_CcError = (1u << 20),
|
||||
DeviceStatus_DeviceEccFailed = (1u << 21),
|
||||
DeviceStatus_IllegalCommand = (1u << 22),
|
||||
DeviceStatus_ComCrcError = (1u << 23),
|
||||
DeviceStatus_LockUnlockFailed = (1u << 24),
|
||||
DeviceStatus_WpViolation = (1u << 26),
|
||||
DeviceStatus_EraseParam = (1u << 27),
|
||||
DeviceStatus_EraseSeqError = (1u << 28),
|
||||
DeviceStatus_BlockLenError = (1u << 29),
|
||||
DeviceStatus_AddressMisaligned = (1u << 30),
|
||||
DeviceStatus_AddressOutOfRange = (1u << 31),
|
||||
|
||||
DeviceStatus_CurrentStateShift = 9,
|
||||
DeviceStatus_CurrentStateMask = (0b1111u << DeviceStatus_CurrentStateShift),
|
||||
|
||||
DeviceStatus_ErrorMask = (DeviceStatus_SwitchError |
|
||||
DeviceStatus_EraseReset |
|
||||
DeviceStatus_WpEraseSkip |
|
||||
DeviceStatus_CidCsdOverwrite |
|
||||
DeviceStatus_Error |
|
||||
DeviceStatus_CcError |
|
||||
DeviceStatus_DeviceEccFailed |
|
||||
DeviceStatus_IllegalCommand |
|
||||
DeviceStatus_ComCrcError |
|
||||
DeviceStatus_LockUnlockFailed |
|
||||
DeviceStatus_WpViolation |
|
||||
DeviceStatus_EraseParam |
|
||||
DeviceStatus_EraseSeqError |
|
||||
DeviceStatus_BlockLenError |
|
||||
DeviceStatus_AddressMisaligned |
|
||||
DeviceStatus_AddressOutOfRange),
|
||||
};
|
||||
|
||||
class BaseDevice {
|
||||
private:
|
||||
u32 ocr;
|
||||
u8 cid[DeviceCidSize];
|
||||
u16 csd[DeviceCsdSize / sizeof(u16)];
|
||||
u32 memory_capacity;
|
||||
bool is_high_capacity;
|
||||
bool is_valid_ocr;
|
||||
bool is_valid_cid;
|
||||
bool is_valid_csd;
|
||||
bool is_valid_high_capacity;
|
||||
bool is_valid_memory_capacity;
|
||||
bool is_active;
|
||||
bool is_awake;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
mutable os::Mutex device_mutex;
|
||||
public:
|
||||
BaseDevice() : device_mutex(true)
|
||||
#else
|
||||
BaseDevice()
|
||||
#endif
|
||||
{
|
||||
this->is_awake = true;
|
||||
this->ocr = 0;
|
||||
this->memory_capacity = 0;
|
||||
this->is_high_capacity = false;
|
||||
this->OnDeactivate();
|
||||
}
|
||||
|
||||
void OnDeactivate() {
|
||||
this->is_active = false;
|
||||
this->is_valid_ocr = false;
|
||||
this->is_valid_cid = false;
|
||||
this->is_valid_csd = false;
|
||||
this->is_valid_high_capacity = false;
|
||||
this->is_valid_memory_capacity = false;
|
||||
}
|
||||
|
||||
bool IsAwake() const {
|
||||
return this->is_awake;
|
||||
}
|
||||
|
||||
void Awaken() {
|
||||
this->is_awake = true;
|
||||
}
|
||||
|
||||
void PutToSleep() {
|
||||
this->is_awake = false;
|
||||
}
|
||||
|
||||
bool IsActive() const {
|
||||
return this->is_active;
|
||||
}
|
||||
|
||||
void SetActive() {
|
||||
this->is_active = true;
|
||||
}
|
||||
|
||||
virtual void Deactivate() {
|
||||
this->OnDeactivate();
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual os::EventType *GetRemovedEvent() const = 0;
|
||||
#endif
|
||||
|
||||
virtual DeviceType GetDeviceType() const = 0;
|
||||
virtual u16 GetRca() const = 0;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
void InitializeRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::InitializeEvent(removed_event, false, os::EventClearMode_ManualClear);
|
||||
}
|
||||
}
|
||||
|
||||
void FinalizeRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::FinalizeEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
void SignalRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::SignalEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::ClearEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsRemoved() const {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
return os::TryWaitEvent(removed_event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
Result CheckRemoved() const {
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved());
|
||||
#endif
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result CheckAccessible() const {
|
||||
/* Check awake. */
|
||||
R_UNLESS(this->IsAwake(), sdmmc::ResultNotAwakened());
|
||||
|
||||
/* Check active. */
|
||||
R_UNLESS(this->IsActive(), sdmmc::ResultNotActivated());
|
||||
|
||||
/* Check removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void SetHighCapacity(bool en) {
|
||||
this->is_high_capacity = en;
|
||||
this->is_valid_high_capacity = true;
|
||||
}
|
||||
|
||||
bool IsHighCapacity() const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_high_capacity);
|
||||
return this->is_high_capacity;
|
||||
}
|
||||
|
||||
void SetOcr(u32 o) {
|
||||
this->ocr = o;
|
||||
this->is_valid_ocr = true;
|
||||
}
|
||||
|
||||
u32 GetOcr() const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_ocr);
|
||||
return this->ocr;
|
||||
}
|
||||
|
||||
void SetCid(const void *src, size_t src_size) {
|
||||
AMS_ABORT_UNLESS(src != nullptr);
|
||||
AMS_ABORT_UNLESS(src_size >= DeviceCidSize);
|
||||
std::memcpy(this->cid, src, DeviceCidSize);
|
||||
this->is_valid_cid = true;
|
||||
}
|
||||
|
||||
void GetCid(void *dst, size_t dst_size) const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_cid);
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
|
||||
std::memcpy(dst, this->cid, DeviceCidSize);
|
||||
}
|
||||
|
||||
void SetCsd(const void *src, size_t src_size) {
|
||||
AMS_ABORT_UNLESS(src != nullptr);
|
||||
AMS_ABORT_UNLESS(src_size >= DeviceCsdSize);
|
||||
std::memcpy(this->csd, src, DeviceCsdSize);
|
||||
this->is_valid_csd = true;
|
||||
}
|
||||
|
||||
void GetCsd(void *dst, size_t dst_size) const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_csd);
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
|
||||
std::memcpy(dst, this->csd, DeviceCsdSize);
|
||||
}
|
||||
|
||||
void SetMemoryCapacity(u32 num_sectors) {
|
||||
this->memory_capacity = num_sectors;
|
||||
this->is_valid_memory_capacity = true;
|
||||
}
|
||||
|
||||
u32 GetMemoryCapacity() const {
|
||||
AMS_ABORT_UNLESS(this->is_valid_memory_capacity);
|
||||
return this->memory_capacity;
|
||||
}
|
||||
|
||||
void GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const;
|
||||
Result SetLegacyMemoryCapacity();
|
||||
|
||||
Result CheckDeviceStatus(u32 r1_resp) const;
|
||||
DeviceState GetDeviceState(u32 r1_resp) const;
|
||||
};
|
||||
|
||||
class BaseDeviceAccessor : public IDeviceAccessor {
|
||||
private:
|
||||
IHostController *host_controller;
|
||||
BaseDevice *base_device;
|
||||
u32 num_activation_failures;
|
||||
u32 num_activation_error_corrections;
|
||||
u32 num_read_write_failures;
|
||||
u32 num_read_write_error_corrections;
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
Logger error_logger;
|
||||
#endif
|
||||
private:
|
||||
void ClearErrorInfo() {
|
||||
this->num_activation_failures = 0;
|
||||
this->num_activation_error_corrections = 0;
|
||||
this->num_read_write_failures = 0;
|
||||
this->num_read_write_error_corrections = 0;
|
||||
}
|
||||
protected:
|
||||
explicit BaseDeviceAccessor(IHostController *hc) : host_controller(hc), base_device(nullptr) {
|
||||
this->ClearErrorInfo();
|
||||
}
|
||||
|
||||
IHostController *GetHostController() const {
|
||||
return this->host_controller;
|
||||
}
|
||||
|
||||
void SetDevice(BaseDevice *bd) {
|
||||
this->base_device = bd;
|
||||
}
|
||||
|
||||
Result CheckRemoved() const {
|
||||
return this->base_device->CheckRemoved();
|
||||
}
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const;
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const {
|
||||
u32 dummy;
|
||||
return this->IssueCommandAndCheckR1(std::addressof(dummy), command_index, command_arg, is_busy, expected_state, status_ignore_mask);
|
||||
}
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state) const {
|
||||
return this->IssueCommandAndCheckR1(command_index, command_arg, is_busy, expected_state, 0);
|
||||
}
|
||||
|
||||
Result IssueCommandGoIdleState() const;
|
||||
Result IssueCommandAllSendCid(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandSelectCard() const;
|
||||
Result IssueCommandSendCsd(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const;
|
||||
|
||||
Result IssueCommandSendStatus(u32 status_ignore_mask) const {
|
||||
u32 dummy;
|
||||
return this->IssueCommandSendStatus(std::addressof(dummy), status_ignore_mask);
|
||||
}
|
||||
|
||||
Result IssueCommandSendStatus() const {
|
||||
return this->IssueCommandSendStatus(0);
|
||||
}
|
||||
|
||||
Result IssueCommandSetBlockLenToSectorSize() const;
|
||||
Result IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const;
|
||||
Result ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const;
|
||||
Result ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read);
|
||||
|
||||
void IncrementNumActivationErrorCorrections() {
|
||||
++this->num_activation_error_corrections;
|
||||
}
|
||||
|
||||
void PushErrorTimeStamp() {
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
this->error_logger.Push("%u", static_cast<u32>(os::ConvertToTimeSpan(os::GetSystemTick()).GetSeconds()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushErrorLog(bool with_timestamp, const char *fmt, ...) {
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
this->error_logger.Push(fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
if (with_timestamp) {
|
||||
this->PushErrorTimeStamp();
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
AMS_UNUSED(with_timestamp, fmt);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual Result OnActivate() = 0;
|
||||
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) = 0;
|
||||
virtual Result ReStartup() = 0;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
|
||||
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
|
||||
#endif
|
||||
|
||||
virtual Result Activate() override;
|
||||
virtual void Deactivate() override;
|
||||
|
||||
virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) override;
|
||||
virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) override;
|
||||
|
||||
virtual Result GetMemoryCapacity(u32 *out_sectors) const override;
|
||||
virtual Result GetDeviceStatus(u32 *out) const override;
|
||||
virtual Result GetOcr(u32 *out) const override;
|
||||
virtual Result GetRca(u16 *out) const override;
|
||||
virtual Result GetCid(void *out, size_t size) const override;
|
||||
virtual Result GetCsd(void *out, size_t size) const override;
|
||||
|
||||
virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_clock_reset_controller.hpp"
|
||||
#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp"
|
||||
#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit bool g_is_module_initialized[Module_Count] = {};
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
constinit os::SdkMutex g_module_mutex;
|
||||
|
||||
#define AMS_SDMMC_LOCK_MODULE_MUTEX() std::scoped_lock lk(g_module_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_MODULE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
constinit bool g_is_pcv_control = false;
|
||||
|
||||
#define AMS_SDMMC_IF_IS_PCV_CONTROL() if (g_is_pcv_control)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_IF_IS_PCV_CONTROL() if constexpr (false)
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void Initialize(Module module) {
|
||||
/* Acquire exclusive access to module state. */
|
||||
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||
|
||||
/* Mark the module as initialized. */
|
||||
g_is_module_initialized[module] = true;
|
||||
|
||||
/* Initialize the module. */
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
ClockResetController::pcv::Initialize(module);
|
||||
} else {
|
||||
ClockResetController::reg::Initialize(module);
|
||||
}
|
||||
}
|
||||
|
||||
void Finalize(Module module) {
|
||||
/* Acquire exclusive access to module state. */
|
||||
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||
|
||||
/* Finalize the module. */
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
ClockResetController::pcv::Finalize(module);
|
||||
} else {
|
||||
ClockResetController::reg::Finalize(module);
|
||||
}
|
||||
|
||||
/* Mark the module as finalized. */
|
||||
g_is_module_initialized[module] = false;
|
||||
}
|
||||
|
||||
bool IsAvailable(Module module) {
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return ClockResetController::pcv::IsAvailable(module);
|
||||
} else {
|
||||
return ClockResetController::reg::IsAvailable(module);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
void SwitchToPcvControl() {
|
||||
/* Acquire exclusive access to module state. */
|
||||
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||
|
||||
/* If we're already using pcv control, we don't need to do anything. */
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Finalize all modules. */
|
||||
for (int i = 0; i < Module_Count; ++i) {
|
||||
if (g_is_module_initialized[i]) {
|
||||
ClockResetController::reg::Finalize(static_cast<Module>(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark that we've switched to pcv control. */
|
||||
|
||||
/* Initialize modules using pcv control. */
|
||||
for (int i = 0; i < Module_Count; ++i) {
|
||||
if (g_is_module_initialized[i]) {
|
||||
ClockResetController::pcv::Initialize(static_cast<Module>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return ClockResetController::pcv::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency);
|
||||
} else {
|
||||
return ClockResetController::reg::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency);
|
||||
}
|
||||
}
|
||||
|
||||
void AssertReset(Module module) {
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return ClockResetController::pcv::AssertReset(module);
|
||||
} else {
|
||||
return ClockResetController::reg::AssertReset(module);
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return ClockResetController::pcv::ReleaseReset(module, target_frequency_khz);
|
||||
} else {
|
||||
return ClockResetController::reg::ReleaseReset(module, target_frequency_khz);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController {
|
||||
|
||||
enum Module {
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
Module_Sdmmc1 = 0,
|
||||
Module_Sdmmc2 = 1,
|
||||
Module_Sdmmc3 = 2,
|
||||
Module_Sdmmc4 = 3,
|
||||
#endif
|
||||
|
||||
Module_Count,
|
||||
};
|
||||
|
||||
void Initialize(Module module);
|
||||
void Finalize(Module module);
|
||||
bool IsAvailable(Module module);
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
void SwitchToPcvControl();
|
||||
#endif
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
|
||||
void AssertReset(Module module);
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz);
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController::pcv {
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
|
||||
void Initialize(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
void Finalize(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
bool IsAvailable(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||
AMS_UNUSED(out_actual_frequency, module, target_frequency);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
void AssertReset(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||
AMS_UNUSED(module, target_frequency_khz);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void Initialize(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
void Finalize(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
bool IsAvailable(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||
AMS_UNUSED(out_actual_frequency, module, target_frequency);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
void AssertReset(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||
AMS_UNUSED(module, target_frequency_khz);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "sdmmc_clock_reset_controller.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController::pcv {
|
||||
|
||||
void Initialize(Module module);
|
||||
void Finalize(Module module);
|
||||
bool IsAvailable(Module module);
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
|
||||
void AssertReset(Module module);
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz);
|
||||
|
||||
}
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp"
|
||||
#include "sdmmc_timer.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController::reg {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
constinit os::SdkMutex g_init_mutex;
|
||||
|
||||
#define AMS_SDMMC_LOCK_INIT_MUTEX() std::scoped_lock lk(g_init_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_INIT_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
constinit bool g_is_initialized = false;
|
||||
|
||||
struct ModuleInfo {
|
||||
u32 target_frequency_khz;
|
||||
u32 actual_frequency_khz;
|
||||
};
|
||||
|
||||
constinit ModuleInfo g_module_infos[Module_Count] = {};
|
||||
|
||||
constexpr inline dd::PhysicalAddress ClockResetControllerRegistersPhysicalAddress = UINT64_C(0x60006000);
|
||||
constexpr inline size_t ClockResetControllerRegistersSize = 4_KB;
|
||||
|
||||
constinit uintptr_t g_clkrst_registers_address = 0;
|
||||
|
||||
[[maybe_unused]] void InitializePllc4() {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Check if PLLC4_BASE has the expected value; if it does, we have nothing to do. */
|
||||
constexpr u32 ExpectedPllc4Base = 0x58006804;
|
||||
if (ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE) == ExpectedPllc4Base) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disable PLLC4_ENABLE, if it's currently set. */
|
||||
if (ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE))) {
|
||||
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, DISABLE));
|
||||
}
|
||||
|
||||
/* Operate on the register with PLLC4_ENABLE cleared. */
|
||||
{
|
||||
/* Clear IDDQ, read to be sure it takes, wait 5 us. */
|
||||
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_IDDQ, OFF));
|
||||
ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE);
|
||||
WaitMicroSeconds(5);
|
||||
|
||||
/* Write the expected value sans IDDQ/PLLC4_ENABLE. */
|
||||
constexpr u32 ExpectedPllc4BaseMask = ~ams::reg::EncodeMask(CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_ENABLE),
|
||||
CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_IDDQ));
|
||||
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, ExpectedPllc4Base & ExpectedPllc4BaseMask);
|
||||
}
|
||||
|
||||
/* Write PLLC4_ENABLE, and read to be sure our configuration takes. */
|
||||
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE));
|
||||
ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE);
|
||||
|
||||
/* Wait up to 1s for changes to take. */
|
||||
{
|
||||
ManualTimer timer(1000);
|
||||
while (true) {
|
||||
/* Check if we're done. */
|
||||
if (!ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_LOCK, NOT_LOCK))) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check that we haven't timed out. */
|
||||
AMS_ABORT_UNLESS(timer.Update());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeLegacyTmClk() {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Configure the legacy tm clock as 12MHz. */
|
||||
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_LEGACY_TM_CLK_SRC, PLLP_OUT0),
|
||||
CLK_RST_REG_BITS_VALUE(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 66));
|
||||
|
||||
/* Enable clock to the legacy tm. */
|
||||
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_CLK_ENB_LEGACY_TM, ENABLE));
|
||||
}
|
||||
|
||||
bool IsResetReleased(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Get the reset bit from RST_DEVICES_* */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC1_RST, DISABLE));
|
||||
case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC2_RST, DISABLE));
|
||||
case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_REG_BITS_ENUM(RST_DEVICES_U_SWR_SDMMC3_RST, DISABLE));
|
||||
case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC4_RST, DISABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void SetReset(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Set reset in RST_DEV_*_SET */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void ClearReset(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Set reset in RST_DEV_*_CLR */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsClockEnabled(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Get the enable bit from CLK_OUT_ENB_* */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void SetClockEnable(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Set clock enable bit in CLK_ENB_*_SET */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void ClearClockEnable(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Set clock enable bit in CLK_ENB_*_CLR */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void SetClockSourceSdmmc(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) {
|
||||
/* Check that we can write to output. */
|
||||
AMS_ABORT_UNLESS(out_actual_frequency_khz != nullptr);
|
||||
|
||||
/* Determine frequency/divisor. */
|
||||
u32 clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLP_OUT0));
|
||||
u8 n;
|
||||
switch (target_frequency_khz) {
|
||||
case 25'000:
|
||||
*out_actual_frequency_khz = 24'728;
|
||||
n = 31;
|
||||
break;
|
||||
case 26'000:
|
||||
*out_actual_frequency_khz = 25'500;
|
||||
n = 30;
|
||||
break;
|
||||
case 40'800:
|
||||
*out_actual_frequency_khz = 40'800;
|
||||
n = 18;
|
||||
break;
|
||||
case 50'000:
|
||||
*out_actual_frequency_khz = 48'000;
|
||||
n = 15;
|
||||
break;
|
||||
case 52'000:
|
||||
*out_actual_frequency_khz = 51'000;
|
||||
n = 14;
|
||||
break;
|
||||
case 100'000:
|
||||
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||
*out_actual_frequency_khz = 99'840;
|
||||
n = 2;
|
||||
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2));
|
||||
#else
|
||||
*out_actual_frequency_khz = 90'667;
|
||||
n = 7;
|
||||
#endif
|
||||
break;
|
||||
case 200'000:
|
||||
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||
*out_actual_frequency_khz = 199'680;
|
||||
n = 0;
|
||||
if (module == Module_Sdmmc2 || module == Module_Sdmmc4) {
|
||||
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, PLLC4_OUT2_LJ));
|
||||
} else {
|
||||
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2));
|
||||
}
|
||||
#else
|
||||
*out_actual_frequency_khz = 163'200;
|
||||
n = 3;
|
||||
#endif
|
||||
break;
|
||||
case 208'000:
|
||||
*out_actual_frequency_khz = 204'000;
|
||||
n = 2;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Set frequencies in module info. */
|
||||
g_module_infos[module].target_frequency_khz = target_frequency_khz;
|
||||
g_module_infos[module].actual_frequency_khz = *out_actual_frequency_khz;
|
||||
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Update the clock source. */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1, clk_m | static_cast<u32>(n)); break;
|
||||
case Module_Sdmmc2: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2, clk_m | static_cast<u32>(n)); break;
|
||||
case Module_Sdmmc3: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3, clk_m | static_cast<u32>(n)); break;
|
||||
case Module_Sdmmc4: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4, clk_m | static_cast<u32>(n)); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureControl(Module module) {
|
||||
/* Read from RST_DEVICES_* to be sure previous configuration takes. */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||
case Module_Sdmmc2: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||
case Module_Sdmmc3: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U); break;
|
||||
case Module_Sdmmc4: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize(Module module) {
|
||||
/* Initialization isn't module specific. */
|
||||
AMS_UNUSED(module);
|
||||
|
||||
/* Acquire exclusive access to the initialization lock. */
|
||||
AMS_SDMMC_LOCK_INIT_MUTEX();
|
||||
|
||||
/* If we've already initialized, we don't need to do anything. */
|
||||
if (g_is_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear module infos. */
|
||||
std::memset(g_module_infos, 0, sizeof(g_module_infos));
|
||||
|
||||
/* Get the registers address. */
|
||||
g_clkrst_registers_address = dd::QueryIoMapping(ClockResetControllerRegistersPhysicalAddress, ClockResetControllerRegistersSize);
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Perform register initialization. */
|
||||
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||
InitializePllc4();
|
||||
#endif
|
||||
InitializeLegacyTmClk();
|
||||
|
||||
/* Mark that we've initialized. */
|
||||
g_is_initialized = true;
|
||||
}
|
||||
|
||||
void Finalize(Module module) {
|
||||
/* Nothing is needed for finalization. */
|
||||
AMS_UNUSED(module);
|
||||
}
|
||||
|
||||
bool IsAvailable(Module module) {
|
||||
return IsResetReleased(module) && IsClockEnabled(module);
|
||||
}
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) {
|
||||
/* If we're not changing the clock frequency, we don't need to do anything. */
|
||||
if (target_frequency_khz == g_module_infos[module].target_frequency_khz) {
|
||||
*out_actual_frequency_khz = g_module_infos[module].actual_frequency_khz;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Temporarily disable clock. */
|
||||
const bool clock_enabled = IsClockEnabled(module);
|
||||
if (clock_enabled) {
|
||||
ClearClockEnable(module);
|
||||
}
|
||||
|
||||
/* Set the clock source. */
|
||||
SetClockSourceSdmmc(out_actual_frequency_khz, module, target_frequency_khz);
|
||||
|
||||
/* Re-enable clock, if we should. */
|
||||
if (clock_enabled) {
|
||||
SetClockEnable(module);
|
||||
}
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
EnsureControl(module);
|
||||
}
|
||||
|
||||
void AssertReset(Module module) {
|
||||
/* Set reset and disable clock. */
|
||||
SetReset(module);
|
||||
ClearClockEnable(module);
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
EnsureControl(module);
|
||||
}
|
||||
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||
/* Disable clock if it's enabled. */
|
||||
if (IsClockEnabled(module)) {
|
||||
ClearClockEnable(module);
|
||||
}
|
||||
|
||||
/* Set reset. */
|
||||
SetReset(module);
|
||||
|
||||
/* Set the clock source. */
|
||||
u32 actual_source_frequency_khz;
|
||||
SetClockSourceSdmmc(std::addressof(actual_source_frequency_khz), module, target_frequency_khz);
|
||||
|
||||
/* Enable clock. */
|
||||
SetClockEnable(module);
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
EnsureControl(module);
|
||||
|
||||
/* Wait 100 clocks. */
|
||||
WaitClocks(100, actual_source_frequency_khz);
|
||||
|
||||
/* Clear reset. */
|
||||
ClearReset(module);
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
EnsureControl(module);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "sdmmc_clock_reset_controller.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController::reg {
|
||||
|
||||
void Initialize(Module module);
|
||||
void Finalize(Module module);
|
||||
bool IsAvailable(Module module);
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
|
||||
void AssertReset(Module module);
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz);
|
||||
|
||||
}
|
270
libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp
Normal file
270
libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
#include "sdmmc_device_detector.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
bool DeviceDetector::IsCurrentInserted() {
|
||||
return gpio::GetValue(std::addressof(this->gpio_pad_session)) == this->inserted_gpio_value;
|
||||
}
|
||||
|
||||
void DeviceDetector::HandleDeviceStatus(bool prev_inserted, bool cur_inserted) {
|
||||
if (!prev_inserted && !cur_inserted) {
|
||||
/* Not inserted -> Not inserted, nothing to do. */
|
||||
} else if (!prev_inserted && cur_inserted) {
|
||||
/* Card was inserted. */
|
||||
if (this->callback_info.inserted_callback != nullptr) {
|
||||
this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg);
|
||||
}
|
||||
} else if (prev_inserted && !cur_inserted) {
|
||||
/* Card was removed. */
|
||||
if (this->callback_info.removed_callback != nullptr) {
|
||||
this->callback_info.removed_callback(this->callback_info.removed_callback_arg);
|
||||
}
|
||||
} else /* if (prev_inserted && cur_inserted) */ {
|
||||
/* Card was removed, and then inserted. */
|
||||
if (this->callback_info.removed_callback != nullptr) {
|
||||
this->callback_info.removed_callback(this->callback_info.removed_callback_arg);
|
||||
}
|
||||
|
||||
if (this->callback_info.inserted_callback != nullptr) {
|
||||
this->callback_info.inserted_callback(this->callback_info.inserted_callback_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceDetector::DetectorThread() {
|
||||
/* Initialize the gpio session. */
|
||||
sm::DoWithSession([] { gpio::Initialize(); });
|
||||
gpio::OpenSession(std::addressof(this->gpio_pad_session), this->gpio_device_code);
|
||||
gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Input);
|
||||
gpio::SetDebounceTime(std::addressof(this->gpio_pad_session), this->gpio_debounce_ms);
|
||||
gpio::SetDebounceEnabled(std::addressof(this->gpio_pad_session), true);
|
||||
gpio::SetInterruptMode(std::addressof(this->gpio_pad_session), gpio::InterruptMode_AnyEdge);
|
||||
|
||||
/* Get the gpio session's interrupt event. */
|
||||
os::SystemEventType gpio_event;
|
||||
R_ABORT_UNLESS(gpio::BindInterrupt(std::addressof(gpio_event), std::addressof(this->gpio_pad_session)));
|
||||
|
||||
/* Initialize and link waitable holders. */
|
||||
os::WaitableManagerType wait_manager;
|
||||
os::WaitableHolderType detector_thread_end_holder;
|
||||
os::WaitableHolderType request_sleep_wake_event_holder;
|
||||
os::WaitableHolderType gpio_event_holder;
|
||||
os::InitializeWaitableManager(std::addressof(wait_manager));
|
||||
os::InitializeWaitableHolder(std::addressof(detector_thread_end_holder), std::addressof(this->detector_thread_end_event));
|
||||
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(detector_thread_end_holder));
|
||||
os::InitializeWaitableHolder(std::addressof(request_sleep_wake_event_holder), std::addressof(this->request_sleep_wake_event));
|
||||
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(request_sleep_wake_event_holder));
|
||||
os::InitializeWaitableHolder(std::addressof(gpio_event_holder), std::addressof(gpio_event));
|
||||
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder));
|
||||
|
||||
/* Wait before detecting the initial state of the card. */
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(this->gpio_debounce_ms));
|
||||
bool cur_inserted = this->IsCurrentInserted();
|
||||
this->is_prev_inserted = cur_inserted;
|
||||
|
||||
/* Set state as awake. */
|
||||
this->state = State_Awake;
|
||||
os::SignalEvent(std::addressof(this->ready_device_status_event));
|
||||
|
||||
/* Enable interrupts to be informed of device status. */
|
||||
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true);
|
||||
|
||||
/* Wait, servicing our events. */
|
||||
while (true) {
|
||||
/* Get the signaled holder. */
|
||||
os::WaitableHolderType *signaled_holder = os::WaitAny(std::addressof(wait_manager));
|
||||
|
||||
/* Process the holder. */
|
||||
bool insert_change = false;
|
||||
if (signaled_holder == std::addressof(detector_thread_end_holder)) {
|
||||
/* We should kill ourselves. */
|
||||
os::ClearEvent(std::addressof(this->detector_thread_end_event));
|
||||
this->state = State_Finalized;
|
||||
break;
|
||||
} else if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) {
|
||||
/* A request for us to sleep/wake has come in, so we'll acknowledge it. */
|
||||
os::ClearEvent(std::addressof(this->request_sleep_wake_event));
|
||||
this->state = State_Sleep;
|
||||
os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event));
|
||||
|
||||
/* Temporarily unlink our interrupt event. */
|
||||
os::UnlinkWaitableHolder(std::addressof(gpio_event_holder));
|
||||
|
||||
/* Wait to be signaled. */
|
||||
signaled_holder = os::WaitAny(std::addressof(wait_manager));
|
||||
|
||||
/* Link our interrupt event back in. */
|
||||
os::LinkWaitableHolder(std::addressof(wait_manager), std::addressof(gpio_event_holder));
|
||||
|
||||
/* We're awake again. Either because we should exit, or because we were asked to wake up. */
|
||||
os::ClearEvent(std::addressof(this->request_sleep_wake_event));
|
||||
this->state = State_Awake;
|
||||
os::SignalEvent(std::addressof(this->acknowledge_sleep_awake_event));
|
||||
|
||||
/* If we were asked to exit, do so. */
|
||||
if (signaled_holder == std::addressof(detector_thread_end_holder)) {
|
||||
/* We should kill ourselves. */
|
||||
os::ClearEvent(std::addressof(this->detector_thread_end_event));
|
||||
this->state = State_Finalized;
|
||||
break;
|
||||
} else /* if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) */ {
|
||||
if ((this->force_detection) ||
|
||||
(({ bool active; R_SUCCEEDED(gpio::IsWakeEventActive(std::addressof(active), this->gpio_device_code)) && active; })) ||
|
||||
(os::TryWaitSystemEvent(std::addressof(gpio_event))) ||
|
||||
(this->is_prev_inserted != this->IsCurrentInserted()))
|
||||
{
|
||||
insert_change = true;
|
||||
}
|
||||
}
|
||||
} else /* if (signaled_holder == std::addressof(gpio_event_holder)) */ {
|
||||
/* An event was detected. */
|
||||
insert_change = true;
|
||||
}
|
||||
|
||||
/* Handle an insert change, if one occurred. */
|
||||
if (insert_change) {
|
||||
/* Call the relevant callback, if we have one. */
|
||||
if (this->device_detection_event_callback != nullptr) {
|
||||
this->device_detection_event_callback(this->device_detection_event_callback_arg);
|
||||
}
|
||||
|
||||
/* Clear the interrupt event. */
|
||||
os::ClearSystemEvent(std::addressof(gpio_event));
|
||||
gpio::ClearInterruptStatus(std::addressof(this->gpio_pad_session));
|
||||
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), true);
|
||||
|
||||
/* Update insertion status. */
|
||||
cur_inserted = this->IsCurrentInserted();
|
||||
this->HandleDeviceStatus(this->is_prev_inserted, cur_inserted);
|
||||
this->is_prev_inserted = cur_inserted;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable interrupts to our gpio event. */
|
||||
gpio::SetInterruptEnable(std::addressof(this->gpio_pad_session), false);
|
||||
|
||||
/* Finalize and unlink waitable holders. */
|
||||
os::UnlinkWaitableHolder(std::addressof(gpio_event_holder));
|
||||
os::FinalizeWaitableHolder(std::addressof(gpio_event_holder));
|
||||
os::UnlinkWaitableHolder(std::addressof(request_sleep_wake_event_holder));
|
||||
os::FinalizeWaitableHolder(std::addressof(request_sleep_wake_event_holder));
|
||||
os::UnlinkWaitableHolder(std::addressof(detector_thread_end_holder));
|
||||
os::FinalizeWaitableHolder(std::addressof(detector_thread_end_holder));
|
||||
os::FinalizeWaitableManager(std::addressof(wait_manager));
|
||||
|
||||
/* Finalize the gpio session. */
|
||||
gpio::UnbindInterrupt(std::addressof(this->gpio_pad_session));
|
||||
gpio::CloseSession(std::addressof(this->gpio_pad_session));
|
||||
gpio::Finalize();
|
||||
}
|
||||
|
||||
void DeviceDetector::Initialize(CallbackInfo *ci) {
|
||||
/* Transition our state from finalized to initializing. */
|
||||
AMS_ABORT_UNLESS(this->state == State_Finalized);
|
||||
this->state = State_Initializing;
|
||||
|
||||
/* Set our callback infos. */
|
||||
this->callback_info = *ci;
|
||||
|
||||
/* Initialize our events. */
|
||||
os::InitializeEvent(std::addressof(this->ready_device_status_event), false, os::EventClearMode_ManualClear);
|
||||
os::InitializeEvent(std::addressof(this->request_sleep_wake_event), false, os::EventClearMode_ManualClear);
|
||||
os::InitializeEvent(std::addressof(this->acknowledge_sleep_awake_event), false, os::EventClearMode_ManualClear);
|
||||
os::InitializeEvent(std::addressof(this->detector_thread_end_event), false, os::EventClearMode_ManualClear);
|
||||
|
||||
/* Create and start the detector thread. */
|
||||
os::CreateThread(std::addressof(this->detector_thread), DetectorThreadEntry, this, this->detector_thread_stack, sizeof(this->detector_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(sdmmc, DeviceDetector));
|
||||
os::SetThreadNamePointer(std::addressof(this->detector_thread), AMS_GET_SYSTEM_THREAD_NAME(sdmmc, DeviceDetector));
|
||||
os::StartThread(std::addressof(this->detector_thread));
|
||||
}
|
||||
|
||||
void DeviceDetector::Finalize() {
|
||||
/* Ensure we're not already finalized. */
|
||||
AMS_ABORT_UNLESS(this->state != State_Finalized);
|
||||
|
||||
/* Signal event to end the detector thread. */
|
||||
os::SignalEvent(std::addressof(this->detector_thread_end_event));
|
||||
os::WaitThread(std::addressof(this->detector_thread));
|
||||
|
||||
/* Finalize thread and events. */
|
||||
os::DestroyThread(std::addressof(this->detector_thread));
|
||||
os::FinalizeEvent(std::addressof(this->ready_device_status_event));
|
||||
os::FinalizeEvent(std::addressof(this->request_sleep_wake_event));
|
||||
os::FinalizeEvent(std::addressof(this->acknowledge_sleep_awake_event));
|
||||
os::FinalizeEvent(std::addressof(this->detector_thread_end_event));
|
||||
}
|
||||
|
||||
void DeviceDetector::PutToSleep() {
|
||||
/* Signal request, wait for acknowledgement. */
|
||||
os::SignalEvent(std::addressof(this->request_sleep_wake_event));
|
||||
os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event));
|
||||
os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event));
|
||||
}
|
||||
|
||||
void DeviceDetector::Awaken(bool force_det) {
|
||||
/* Signal request, wait for acknowledgement. */
|
||||
this->force_detection = force_det;
|
||||
os::SignalEvent(std::addressof(this->request_sleep_wake_event));
|
||||
os::WaitEvent(std::addressof(this->acknowledge_sleep_awake_event));
|
||||
os::ClearEvent(std::addressof(this->acknowledge_sleep_awake_event));
|
||||
}
|
||||
|
||||
bool DeviceDetector::IsInserted() {
|
||||
bool inserted = false;
|
||||
|
||||
switch (this->state) {
|
||||
case State_Initializing:
|
||||
/* Wait for us to know whether the device is inserted. */
|
||||
os::WaitEvent(std::addressof(this->ready_device_status_event));
|
||||
[[fallthrough]];
|
||||
case State_Awake:
|
||||
/* Get whether the device is currently inserted. */
|
||||
inserted = this->IsCurrentInserted();
|
||||
break;
|
||||
case State_Sleep:
|
||||
case State_Finalized:
|
||||
/* Get whether the device was inserted when we last knew. */
|
||||
inserted = this->is_prev_inserted;
|
||||
break;
|
||||
}
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
void DeviceDetector::RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) {
|
||||
this->device_detection_event_callback_arg = arg;
|
||||
this->device_detection_event_callback = cb;
|
||||
}
|
||||
|
||||
void DeviceDetector::UnregisterDetectionEventCallback() {
|
||||
this->device_detection_event_callback = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
using InsertedCallback = void (*)(void *);
|
||||
using RemovedCallback = void (*)(void *);
|
||||
|
||||
struct CallbackInfo {
|
||||
InsertedCallback inserted_callback;
|
||||
void *inserted_callback_arg;
|
||||
RemovedCallback removed_callback;
|
||||
void *removed_callback_arg;
|
||||
};
|
||||
|
||||
class DeviceDetector {
|
||||
private:
|
||||
enum State {
|
||||
State_Initializing = 0,
|
||||
State_Awake = 1,
|
||||
State_Sleep = 2,
|
||||
State_Finalized = 3,
|
||||
};
|
||||
private:
|
||||
alignas(os::ThreadStackAlignment) u8 detector_thread_stack[8_KB];
|
||||
State state;
|
||||
bool is_prev_inserted;
|
||||
bool force_detection;
|
||||
os::ThreadType detector_thread;
|
||||
os::EventType detector_thread_end_event;
|
||||
os::EventType request_sleep_wake_event;
|
||||
os::EventType acknowledge_sleep_awake_event;
|
||||
os::EventType ready_device_status_event;
|
||||
|
||||
DeviceCode gpio_device_code;
|
||||
gpio::GpioValue inserted_gpio_value;
|
||||
u32 gpio_debounce_ms;
|
||||
gpio::GpioPadSession gpio_pad_session;
|
||||
|
||||
CallbackInfo callback_info;
|
||||
|
||||
DeviceDetectionEventCallback device_detection_event_callback;
|
||||
void *device_detection_event_callback_arg;
|
||||
private:
|
||||
static void DetectorThreadEntry(void *arg) {
|
||||
reinterpret_cast<DeviceDetector *>(arg)->DetectorThread();
|
||||
}
|
||||
|
||||
void DetectorThread();
|
||||
bool IsCurrentInserted();
|
||||
void HandleDeviceStatus(bool prev_inserted, bool cur_inserted);
|
||||
public:
|
||||
explicit DeviceDetector(DeviceCode dc, gpio::GpioValue igv, u32 gd)
|
||||
: gpio_device_code(dc), inserted_gpio_value(igv), gpio_debounce_ms(gd)
|
||||
{
|
||||
this->state = State_Finalized;
|
||||
this->is_prev_inserted = false;
|
||||
this->force_detection = false;
|
||||
this->callback_info = {};
|
||||
this->device_detection_event_callback = nullptr;
|
||||
this->device_detection_event_callback_arg = nullptr;
|
||||
}
|
||||
|
||||
void Initialize(CallbackInfo *ci);
|
||||
void Finalize();
|
||||
|
||||
void PutToSleep();
|
||||
void Awaken(bool force_det);
|
||||
|
||||
u32 GetDebounceMilliSeconds() const {
|
||||
return this->gpio_debounce_ms;
|
||||
}
|
||||
|
||||
bool IsInserted();
|
||||
|
||||
void RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg);
|
||||
void UnregisterDetectionEventCallback();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_gc_asic_device_accessor.hpp"
|
||||
#include "sdmmc_timer.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
#define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX() std::scoped_lock lk(this->gc_asic_device.device_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
|
||||
#define AMS_SDMMC_CHECK_GC_ASIC_REMOVED() R_UNLESS(!this->gc_asic_device.IsRemoved(), sdmmc::ResultDeviceRemoved())
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_CHECK_GC_ASIC_REMOVED()
|
||||
|
||||
#endif
|
||||
|
||||
Result GcAsicDeviceAccessor::IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const {
|
||||
/* Validate the operation buffer. */
|
||||
AMS_ABORT_UNLESS(op_buf != nullptr);
|
||||
AMS_ABORT_UNLESS(op_buf_size >= GcAsicOperationSize);
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(CommandIndex_GcAsicWriteOperation, 0, CommandResponseType, false);
|
||||
TransferData xfer_data(const_cast<void *>(op_buf), GcAsicOperationSize, 1, TransferDirection_WriteToDevice);
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
Result result = hc->IssueCommand(std::addressof(command), std::addressof(xfer_data));
|
||||
if (R_FAILED(result)) {
|
||||
/* We failed to write operation. Check if we were removed. */
|
||||
AMS_SDMMC_CHECK_GC_ASIC_REMOVED();
|
||||
|
||||
/* Determine what result we should return. */
|
||||
Result return_result = result;
|
||||
{
|
||||
/* Issue a stop transmission command. */
|
||||
u32 resp = 0;
|
||||
result = hc->IssueStopTransmissionCommand(std::addressof(resp));
|
||||
if (R_SUCCEEDED(result)) {
|
||||
/* If we successfully stopped transmission but have an error status, we prefer to return that. */
|
||||
result = this->gc_asic_device.CheckDeviceStatus(resp);
|
||||
if (R_FAILED(result)) {
|
||||
return_result = result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check again if we were removed. */
|
||||
AMS_SDMMC_CHECK_GC_ASIC_REMOVED();
|
||||
|
||||
/* Request device status. */
|
||||
u32 device_status;
|
||||
result = BaseDeviceAccessor::IssueCommandSendStatus(std::addressof(device_status), 0);
|
||||
|
||||
/* If we got a device status error here and we didn't previously, we prefer to return that. */
|
||||
if (!sdmmc::ResultDeviceStatusHasError::Includes(return_result) && sdmmc::ResultDeviceStatusHasError::Includes(result)) {
|
||||
return_result = result;
|
||||
}
|
||||
}
|
||||
return return_result;
|
||||
}
|
||||
|
||||
/* Get the response. */
|
||||
u32 resp;
|
||||
hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
|
||||
R_TRY(this->gc_asic_device.CheckDeviceStatus(resp));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::IssueCommandFinishOperation() const {
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicFinishOperation, 0, true, DeviceState_Tran));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::IssueCommandSleep() {
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicSleep, 0, true, DeviceState_Tran));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::IssueCommandUpdateKey() const {
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicUpdateKey, 0, true, DeviceState_Tran));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::StartupGcAsicDevice() {
|
||||
/* Start up the host controller. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
R_TRY(hc->Startup(BusPower_1_8V, BusWidth_8Bit, SpeedMode_GcAsicSpeed, false));
|
||||
|
||||
/* Wait 10 clocks for configuration to take. */
|
||||
WaitClocks(10, hc->GetDeviceClockFrequencyKHz());
|
||||
|
||||
/* Perform tuning with command index 21. */
|
||||
AMS_ABORT_UNLESS(hc->IsSupportedTuning());
|
||||
R_TRY(hc->Tuning(SpeedMode_GcAsicSpeed, 21));
|
||||
|
||||
/* Set the device as low capacity/no memory. */
|
||||
this->gc_asic_device.SetHighCapacity(false);
|
||||
this->gc_asic_device.SetMemoryCapacity(0);
|
||||
|
||||
/* Enable power saving. */
|
||||
hc->SetPowerSaving(true);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::OnActivate() {
|
||||
/* If we fail to start up the device, ensure the host controller is shut down. */
|
||||
auto power_guard = SCOPE_GUARD { BaseDeviceAccessor::GetHostController()->Shutdown(); };
|
||||
|
||||
/* Try to start up the device. */
|
||||
R_TRY(this->StartupGcAsicDevice());
|
||||
|
||||
/* We started up, so we don't need to power down. */
|
||||
power_guard.Cancel();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) {
|
||||
/* Check that we're not performing zero-byte rw. */
|
||||
AMS_ABORT_UNLESS(num_sectors > 0);
|
||||
|
||||
/* Check that the buffer is big enough for the rw. */
|
||||
AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors);
|
||||
|
||||
/* Perform the read/write. */
|
||||
u32 num_transferred_blocks;
|
||||
R_TRY(BaseDeviceAccessor::ReadWriteSingle(std::addressof(num_transferred_blocks), sector_index, num_sectors, buf, is_read));
|
||||
|
||||
/* Require that we read/wrote as many sectors as we expected. */
|
||||
AMS_ABORT_UNLESS(num_transferred_blocks == num_sectors);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void GcAsicDeviceAccessor::Initialize() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* If we've already initialized, we don't need to do anything. */
|
||||
if (this->is_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the base device to our gc asic device. */
|
||||
BaseDeviceAccessor::SetDevice(std::addressof(this->gc_asic_device));
|
||||
|
||||
/* Initialize. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
{
|
||||
this->gc_asic_device.InitializeRemovedEvent();
|
||||
hc->PreSetRemovedEvent(this->gc_asic_device.GetRemovedEvent());
|
||||
}
|
||||
#endif
|
||||
hc->Initialize();
|
||||
|
||||
/* Mark ourselves as initialized. */
|
||||
this->is_initialized = true;
|
||||
}
|
||||
|
||||
void GcAsicDeviceAccessor::Finalize() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* If we've already finalized, we don't need to do anything. */
|
||||
if (!this->is_initialized) {
|
||||
return;
|
||||
}
|
||||
this->is_initialized = false;
|
||||
|
||||
/* Deactivate the device. */
|
||||
BaseDeviceAccessor::Deactivate();
|
||||
|
||||
/* Finalize the host controller. */
|
||||
BaseDeviceAccessor::GetHostController()->Finalize();
|
||||
|
||||
/* Finalize the removed event. */
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
{
|
||||
this->gc_asic_device.FinalizeRemovedEvent();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const {
|
||||
/* Check that we can write to output. */
|
||||
AMS_ABORT_UNLESS(out_speed_mode != nullptr);
|
||||
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(this->gc_asic_device.CheckAccessible());
|
||||
|
||||
*out_speed_mode = SpeedMode_GcAsicSpeed;
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void GcAsicDeviceAccessor::PutGcAsicToSleep() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* If the device isn't awake, we don't need to do anything. */
|
||||
if (!this->gc_asic_device.IsAwake()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If necessary, put the host controller to sleep. */
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved())
|
||||
#else
|
||||
if (this->gc_asic_device.IsActive())
|
||||
#endif
|
||||
{
|
||||
BaseDeviceAccessor::GetHostController()->PutToSleep();
|
||||
}
|
||||
|
||||
/* Put the gc asic device to sleep. */
|
||||
this->gc_asic_device.PutToSleep();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::AwakenGcAsic() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* If the device is awake, we don't need to do anything. */
|
||||
R_SUCCEED_IF(this->gc_asic_device.IsAwake());
|
||||
|
||||
/* Wake the device. */
|
||||
this->gc_asic_device.Awaken();
|
||||
|
||||
/* Wake the host controller, if we need to.*/
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
if (this->gc_asic_device.IsActive() && !this->gc_asic_device.IsRemoved())
|
||||
#else
|
||||
if (this->gc_asic_device.IsActive())
|
||||
#endif
|
||||
{
|
||||
R_TRY(BaseDeviceAccessor::GetHostController()->Awaken());
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::WriteGcAsicOperation(const void *op_buf, size_t op_buf_size) {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(this->gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandWriteOperation(op_buf, op_buf_size));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::FinishGcAsicOperation() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(this->gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandFinishOperation());
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::AbortGcAsicOperation() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(this->gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue stop transmission command. */
|
||||
u32 resp = 0;
|
||||
R_TRY(BaseDeviceAccessor::GetHostController()->IssueStopTransmissionCommand(std::addressof(resp)));
|
||||
R_TRY(this->gc_asic_device.CheckDeviceStatus(resp));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::SleepGcAsic() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(this->gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandSleep());
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::UpdateGcAsicKey() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(this->gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandUpdateKey());
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
class GcAsicDevice : public BaseDevice {
|
||||
private:
|
||||
static constexpr u16 Rca = 0;
|
||||
private:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
mutable os::EventType removed_event;
|
||||
#endif
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual os::EventType *GetRemovedEvent() const override {
|
||||
return std::addressof(this->removed_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual DeviceType GetDeviceType() const override {
|
||||
return DeviceType_GcAsic;
|
||||
}
|
||||
|
||||
virtual u16 GetRca() const override {
|
||||
return Rca;
|
||||
}
|
||||
};
|
||||
|
||||
class GcAsicDeviceAccessor : public BaseDeviceAccessor {
|
||||
private:
|
||||
GcAsicDevice gc_asic_device;
|
||||
bool is_initialized;
|
||||
private:
|
||||
Result IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const;
|
||||
Result IssueCommandFinishOperation() const;
|
||||
Result IssueCommandSleep();
|
||||
Result IssueCommandUpdateKey() const;
|
||||
Result StartupGcAsicDevice();
|
||||
protected:
|
||||
virtual Result OnActivate() override;
|
||||
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
|
||||
|
||||
virtual Result ReStartup() override {
|
||||
AMS_ABORT("Can't ReStartup GcAsic\n");
|
||||
}
|
||||
public:
|
||||
virtual void Initialize() override;
|
||||
virtual void Finalize() override;
|
||||
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
|
||||
public:
|
||||
explicit GcAsicDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc), is_initialized(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void PutGcAsicToSleep();
|
||||
Result AwakenGcAsic();
|
||||
Result WriteGcAsicOperation(const void *op_buf, size_t op_buf_size);
|
||||
Result FinishGcAsicOperation();
|
||||
Result AbortGcAsicOperation();
|
||||
Result SleepGcAsic();
|
||||
Result UpdateGcAsicKey();
|
||||
|
||||
void SignalGcRemovedEvent() {
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
this->gc_asic_device.SignalRemovedEvent();
|
||||
#else
|
||||
AMS_ABORT("SignalGcRemovedEvent called without event support\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClearGcRemovedEvent() {
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
this->gc_asic_device.ClearRemovedEvent();
|
||||
#else
|
||||
AMS_ABORT("ClearGcRemovedEvent called without event support\n");
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
class IDeviceAccessor {
|
||||
public:
|
||||
virtual void Initialize() = 0;
|
||||
virtual void Finalize() = 0;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
|
||||
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
|
||||
#endif
|
||||
|
||||
virtual Result Activate() = 0;
|
||||
virtual void Deactivate() = 0;
|
||||
|
||||
virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) = 0;
|
||||
virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) = 0;
|
||||
|
||||
virtual Result GetSpeedMode(SpeedMode *out) const = 0;
|
||||
virtual Result GetMemoryCapacity(u32 *out_sectors) const = 0;
|
||||
virtual Result GetDeviceStatus(u32 *out) const = 0;
|
||||
virtual Result GetOcr(u32 *out) const = 0;
|
||||
virtual Result GetRca(u16 *out) const = 0;
|
||||
virtual Result GetCid(void *out, size_t size) const = 0;
|
||||
virtual Result GetCsd(void *out, size_t size) const = 0;
|
||||
|
||||
virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) = 0;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
#include <stratosphere/os.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
enum ResponseType {
|
||||
ResponseType_R0 = 0,
|
||||
ResponseType_R1 = 1,
|
||||
ResponseType_R2 = 2,
|
||||
ResponseType_R3 = 3,
|
||||
ResponseType_R6 = 4,
|
||||
ResponseType_R7 = 5,
|
||||
};
|
||||
|
||||
enum TransferDirection {
|
||||
TransferDirection_ReadFromDevice = 0,
|
||||
TransferDirection_WriteToDevice = 1,
|
||||
};
|
||||
|
||||
enum CommandIndex {
|
||||
/* Generic commands. */
|
||||
CommandIndex_GoIdleState = 0,
|
||||
CommandIndex_SendOpCond = 1,
|
||||
CommandIndex_AllSendCid = 2,
|
||||
CommandIndex_SendRelativeAddr = 3,
|
||||
CommandIndex_SetRelativeAddr = 3,
|
||||
CommandIndex_SetDsr = 4,
|
||||
|
||||
CommandIndex_Switch = 6,
|
||||
CommandIndex_SelectCard = 7,
|
||||
CommandIndex_DeselectCard = 7,
|
||||
CommandIndex_SendIfCond = 8,
|
||||
CommandIndex_SendExtCsd = 8,
|
||||
CommandIndex_SendCsd = 9,
|
||||
CommandIndex_SendCid = 10,
|
||||
CommandIndex_VoltageSwitch = 11,
|
||||
CommandIndex_StopTransmission = 12,
|
||||
CommandIndex_SendStatus = 13,
|
||||
CommandIndex_SendTaskStatus = 13,
|
||||
|
||||
CommandIndex_GoInactiveState = 15,
|
||||
CommandIndex_SetBlockLen = 16,
|
||||
CommandIndex_ReadSingleBlock = 17,
|
||||
CommandIndex_ReadMultipleBlock = 18,
|
||||
CommandIndex_SendTuningBlock = 19,
|
||||
CommandIndex_SpeedClassControl = 20,
|
||||
|
||||
CommandIndex_AddressExtension = 22,
|
||||
CommandIndex_SetBlockCount = 23,
|
||||
CommandIndex_WriteBlock = 24,
|
||||
CommandIndex_WriteMultipleBlock = 25,
|
||||
|
||||
CommandIndex_ProgramCsd = 27,
|
||||
CommandIndex_SetWriteProt = 28,
|
||||
CommandIndex_ClearWriteProt = 29,
|
||||
CommandIndex_SendWriteProt = 30,
|
||||
|
||||
CommandIndex_EraseWriteBlockStart = 32,
|
||||
CommandIndex_EraseWriteBlockEnd = 33,
|
||||
|
||||
CommandIndex_EraseGroupStart = 35,
|
||||
CommandIndex_EraseGroupEnd = 36,
|
||||
|
||||
CommandIndex_Erase = 38,
|
||||
|
||||
CommandIndex_LockUnlock = 42,
|
||||
|
||||
CommandIndex_AppCmd = 55,
|
||||
CommandIndex_GenCmd = 56,
|
||||
|
||||
/* Nintendo specific vendor commands for lotus3. */
|
||||
CommandIndex_GcAsicWriteOperation = 60,
|
||||
CommandIndex_GcAsicFinishOperation = 61,
|
||||
CommandIndex_GcAsicSleep = 62,
|
||||
CommandIndex_GcAsicUpdateKey = 63,
|
||||
};
|
||||
|
||||
struct Command {
|
||||
u32 command_index;
|
||||
u32 command_argument;
|
||||
ResponseType response_type;
|
||||
bool is_busy;
|
||||
|
||||
constexpr Command(u32 ci, u32 ca, ResponseType r, bool b) : command_index(ci), command_argument(ca), response_type(r), is_busy(b) { /* ... */ }
|
||||
};
|
||||
|
||||
struct TransferData {
|
||||
void *buffer;
|
||||
size_t block_size;
|
||||
u32 num_blocks;
|
||||
TransferDirection transfer_direction;
|
||||
bool is_multi_block_transfer;
|
||||
bool is_stop_transmission_command_enabled;
|
||||
|
||||
constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd, bool mb, bool st)
|
||||
: buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(mb), is_stop_transmission_command_enabled(st)
|
||||
{
|
||||
if (this->num_blocks > 1) {
|
||||
AMS_ABORT_UNLESS(this->is_multi_block_transfer);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd)
|
||||
: buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(false), is_stop_transmission_command_enabled(false)
|
||||
{
|
||||
AMS_ABORT_UNLESS(this->num_blocks == 1);
|
||||
}
|
||||
};
|
||||
|
||||
class IHostController {
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual void PreSetRemovedEvent(ams::os::EventType *event) = 0;
|
||||
#endif
|
||||
|
||||
virtual void Initialize() = 0;
|
||||
virtual void Finalize() = 0;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
|
||||
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
|
||||
#endif
|
||||
|
||||
virtual void SetWorkBuffer(void *wb, size_t wb_size) = 0;
|
||||
|
||||
virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) = 0;
|
||||
virtual void Shutdown();
|
||||
virtual void PutToSleep();
|
||||
virtual Result Awaken();
|
||||
|
||||
virtual Result SwitchToSdr12();
|
||||
|
||||
virtual bool IsSupportedBusPower(BusPower bus_power) const = 0;
|
||||
virtual BusPower GetBusPower() const = 0;
|
||||
|
||||
virtual bool IsSupportedBusWidth(BusWidth bus_width) const = 0;
|
||||
virtual void SetBusWidth(BusWidth bus_width) = 0;
|
||||
virtual BusWidth GetBusWidth() const = 0;
|
||||
|
||||
virtual Result SetSpeedMode(SpeedMode speed_mode) = 0;
|
||||
virtual SpeedMode GetSpeedMode() const = 0;
|
||||
|
||||
virtual u32 GetDeviceClockFrequencyKHz() const = 0;
|
||||
|
||||
virtual void SetPowerSaving(bool en) = 0;
|
||||
virtual bool IsPowerSavingEnable() const = 0;
|
||||
|
||||
virtual void EnableDeviceClock() = 0;
|
||||
virtual void DisableDeviceClock() = 0;
|
||||
virtual bool IsDeviceClockEnable() const = 0;
|
||||
|
||||
virtual u32 GetMaxTransferNumBlocks() const = 0;
|
||||
|
||||
virtual void ChangeCheckTransferInterval(u32 ms) = 0;
|
||||
virtual void SetDefaultCheckTransferInterval() = 0;
|
||||
|
||||
virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) = 0;
|
||||
virtual Result IssueStopTransmissionCommand(u32 *out_response) = 0;
|
||||
|
||||
ALWAYS_INLINE Result IssueCommand(const Command *command, TransferData *xfer_data) {
|
||||
return this->IssueCommand(command, xfer_data, nullptr);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result IssueCommand(const Command *command) {
|
||||
return this->IssueCommand(command, nullptr, nullptr);
|
||||
}
|
||||
|
||||
virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const = 0;
|
||||
virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const = 0;
|
||||
|
||||
virtual bool IsSupportedTuning() const = 0;
|
||||
virtual Result Tuning(SpeedMode speed_mode, u32 command_index) = 0;
|
||||
virtual void SaveTuningStatusForHs400() = 0;
|
||||
virtual Result GetInternalStatus() const = 0;
|
||||
};
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
Result SetSdCardVoltageEnabled(bool en);
|
||||
Result SetSdCardVoltageValue(u32 micro_volts);
|
||||
|
||||
namespace pinmux_impl {
|
||||
|
||||
enum PinAssignment {
|
||||
PinAssignment_Sdmmc1OutputHigh = 2,
|
||||
PinAssignment_Sdmmc1ResetState = 3,
|
||||
PinAssignment_Sdmmc1SchmtEnable = 4,
|
||||
PinAssignment_Sdmmc1SchmtDisable = 5,
|
||||
};
|
||||
|
||||
void SetPinAssignment(PinAssignment assignment);
|
||||
|
||||
}
|
||||
|
||||
namespace gpio_impl {
|
||||
|
||||
enum GpioValue {
|
||||
GpioValue_Low = 0,
|
||||
GpioValue_High = 1
|
||||
};
|
||||
|
||||
enum Direction {
|
||||
Direction_Input = 0,
|
||||
Direction_Output = 1,
|
||||
};
|
||||
|
||||
enum GpioPadName {
|
||||
GpioPadName_PowSdEn = 2,
|
||||
};
|
||||
|
||||
void OpenSession(GpioPadName pad);
|
||||
void CloseSession(GpioPadName pad);
|
||||
|
||||
void SetDirection(GpioPadName pad, Direction direction);
|
||||
void SetValue(GpioPadName pad, GpioValue value);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,728 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_mmc_device_accessor.hpp"
|
||||
#include "sdmmc_timer.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
#define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX() std::scoped_lock lk(this->mmc_device.device_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 OcrCardPowerUpStatus = (1 << 31);
|
||||
|
||||
constexpr inline u32 OcrAccessMode_Mask = (3 << 29);
|
||||
constexpr inline u32 OcrAccessMode_SectorMode = (2 << 29);
|
||||
|
||||
constexpr inline u8 ManufacturerId_Toshiba = 0x11;
|
||||
|
||||
enum DeviceType : u8 {
|
||||
DeviceType_HighSpeed26MHz = (1u << 0),
|
||||
DeviceType_HighSpeed52MHz = (1u << 1),
|
||||
DeviceType_HighSpeedDdr52MHz1_8VOr3_0V = (1u << 2),
|
||||
DeviceType_HighSpeedDdr52MHz1_2V = (1u << 3),
|
||||
DeviceType_Hs200Sdr200MHz1_8V = (1u << 4),
|
||||
DeviceType_Hs200Sdr200MHz1_2V = (1u << 5),
|
||||
DeviceType_Hs400Sdr200MHz1_8V = (1u << 6),
|
||||
DeviceType_Hs400Sdr200MHz1_2V = (1u << 7),
|
||||
};
|
||||
|
||||
constexpr bool IsToshibaMmc(const u8 *cid) {
|
||||
/* Check whether the CID's manufacturer id field is Toshiba. */
|
||||
AMS_ABORT_UNLESS(cid != nullptr);
|
||||
return cid[14] == ManufacturerId_Toshiba;
|
||||
}
|
||||
|
||||
constexpr bool IsLessThanSpecification4(const u8 *csd) {
|
||||
const u8 spec_vers = ((csd[14] >> 2) & 0xF);
|
||||
return spec_vers < 4;
|
||||
}
|
||||
|
||||
constexpr bool IsBkopAutoEnable(const u8 *ext_csd) {
|
||||
/* Check the AUTO_EN bit of BKOPS_EN. */
|
||||
return (ext_csd[163] & (1u << 1)) != 0;
|
||||
}
|
||||
|
||||
constexpr u8 GetDeviceType(const u8 *ext_csd) {
|
||||
/* Get the DEVICE_TYPE register. */
|
||||
AMS_ABORT_UNLESS(ext_csd != nullptr);
|
||||
return ext_csd[196];
|
||||
}
|
||||
|
||||
constexpr bool IsSupportedHs400(u8 device_type) {
|
||||
return (device_type & DeviceType_Hs400Sdr200MHz1_8V) != 0;
|
||||
}
|
||||
|
||||
constexpr bool IsSupportedHs200(u8 device_type) {
|
||||
return (device_type & DeviceType_Hs200Sdr200MHz1_8V) != 0;
|
||||
}
|
||||
|
||||
constexpr bool IsSupportedHighSpeed(u8 device_type) {
|
||||
return (device_type & DeviceType_HighSpeed52MHz) != 0;
|
||||
}
|
||||
|
||||
constexpr u32 GetMemoryCapacityFromExtCsd(const u32 *ext_csd) {
|
||||
/* Get the SEC_COUNT register. */
|
||||
AMS_ABORT_UNLESS(ext_csd != nullptr);
|
||||
return ext_csd[212 / sizeof(u32)];
|
||||
}
|
||||
|
||||
constexpr u32 GetBootPartitionMemoryCapacityFromExtCsd(const u8 *ext_csd) {
|
||||
/* Get the BOOT_SIZE_MULT register. */
|
||||
AMS_ABORT_UNLESS(ext_csd != nullptr);
|
||||
return ext_csd[226] * (128_KB / SectorSize);
|
||||
}
|
||||
|
||||
constexpr Result GetCurrentSpeedModeFromExtCsd(SpeedMode *out, const u8 *ext_csd) {
|
||||
/* Get the HS_TIMING register. */
|
||||
AMS_ABORT_UNLESS(out != nullptr);
|
||||
AMS_ABORT_UNLESS(ext_csd != nullptr);
|
||||
|
||||
switch (ext_csd[185] & 0xF) {
|
||||
case 0: *out = SpeedMode_MmcLegacySpeed; break;
|
||||
case 1: *out = SpeedMode_MmcHighSpeed; break;
|
||||
case 2: *out = SpeedMode_MmcHs200; break;
|
||||
case 3: *out = SpeedMode_MmcHs400; break;
|
||||
default: return sdmmc::ResultUnexpectedMmcExtendedCsdValue();
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MmcDevice::SetOcrAndHighCapacity(u32 ocr) {
|
||||
/* Set ocr. */
|
||||
BaseDevice::SetOcr(ocr);
|
||||
|
||||
/* Set high capacity. */
|
||||
BaseDevice::SetHighCapacity((ocr & OcrAccessMode_Mask) == OcrAccessMode_SectorMode);
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const {
|
||||
/* Get the command argument. */
|
||||
u32 arg = OcrAccessMode_SectorMode;
|
||||
switch (bus_power) {
|
||||
case BusPower_1_8V: arg |= 0x000080; break;
|
||||
case BusPower_3_3V: arg |= 0x03F800; break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R3;
|
||||
Command command(CommandIndex_SendOpCond, arg, CommandResponseType, false);
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
R_TRY(hc->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Get the response. */
|
||||
hc->GetLastResponse(out_ocr, sizeof(*out_ocr), CommandResponseType);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandSetRelativeAddr() const {
|
||||
/* Get rca. */
|
||||
const u32 rca = this->mmc_device.GetRca();
|
||||
AMS_ABORT_UNLESS(rca > 0);
|
||||
|
||||
/* Issue comamnd. */
|
||||
const u32 arg = rca << 16;
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_SetRelativeAddr, arg, false, DeviceState_Unknown));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandSwitch(CommandSwitch cs) const {
|
||||
/* Get the command argument. */
|
||||
const u32 arg = GetCommandSwitchArgument(cs);
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Switch, arg, true, DeviceState_Unknown));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandSendExtCsd(void *dst, size_t dst_size) const {
|
||||
/* Validate the output buffer. */
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size >= MmcExtendedCsdSize);
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(CommandIndex_SendExtCsd, 0, CommandResponseType, false);
|
||||
TransferData xfer_data(dst, MmcExtendedCsdSize, 1, TransferDirection_ReadFromDevice);
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data)));
|
||||
|
||||
/* Get the response. */
|
||||
u32 resp;
|
||||
hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
|
||||
R_TRY(this->mmc_device.CheckDeviceStatus(resp));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandEraseGroupStart(u32 sector_index) const {
|
||||
/* Get the command argument. */
|
||||
const u32 arg = this->mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize;
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupStart, arg, false, DeviceState_Unknown));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandEraseGroupEnd(u32 sector_index) const {
|
||||
/* Get the command argument. */
|
||||
const u32 arg = this->mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize;
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupEnd, arg, false, DeviceState_Tran));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandErase() const {
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Erase, 0, false, DeviceState_Tran));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::CancelToshibaMmcModel() {
|
||||
/* Special erase sequence done by Nintendo on Toshiba MMCs. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsProductionStateAwarenessEnable));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_ClearBitsAutoModeEnable));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessNormal));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ChangeToReadyState(BusPower bus_power) {
|
||||
/* Be prepared to wait up to 1.5 seconds to change state. */
|
||||
ManualTimer timer(1500);
|
||||
while (true) {
|
||||
/* Get the ocr, and check if we're done. */
|
||||
u32 ocr;
|
||||
R_TRY(this->IssueCommandSendOpCond(std::addressof(ocr), bus_power));
|
||||
if ((ocr & OcrCardPowerUpStatus) != 0) {
|
||||
this->mmc_device.SetOcrAndHighCapacity(ocr);
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Check if we've timed out. */
|
||||
R_UNLESS(timer.Update(), sdmmc::ResultMmcInitializationSoftwareTimeout());
|
||||
|
||||
/* Try again in 1ms. */
|
||||
WaitMicroSeconds(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ExtendBusWidth(BusWidth max_bw) {
|
||||
/* If the maximum bus width is 1bit, we can't extend. */
|
||||
R_SUCCEED_IF(max_bw == BusWidth_1Bit);
|
||||
|
||||
/* Determine what bus width to switch to. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
BusWidth target_bw = BusWidth_1Bit;
|
||||
CommandSwitch cs;
|
||||
if (max_bw == BusWidth_8Bit && hc->IsSupportedBusWidth(BusWidth_8Bit)) {
|
||||
target_bw = BusWidth_8Bit;
|
||||
cs = CommandSwitch_WriteBusWidth8Bit;
|
||||
} else if ((max_bw == BusWidth_8Bit || max_bw == BusWidth_4Bit) && hc->IsSupportedBusWidth(BusWidth_4Bit)) {
|
||||
target_bw = BusWidth_4Bit;
|
||||
cs = CommandSwitch_WriteBusWidth4Bit;
|
||||
} else {
|
||||
/* Target bus width is 1bit. */
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Set the bus width. */
|
||||
R_TRY(this->IssueCommandSwitch(cs));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
hc->SetBusWidth(target_bw);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::EnableBkopsAuto() {
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsBkopsEnAutoEn));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ChangeToHighSpeed(bool check_before) {
|
||||
/* Issue high speed command. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHighSpeed));
|
||||
|
||||
/* If we should check status before setting mode, do so. */
|
||||
if (check_before) {
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
}
|
||||
|
||||
/* Set the host controller to high speed. */
|
||||
R_TRY(BaseDeviceAccessor::GetHostController()->SetSpeedMode(SpeedMode_MmcHighSpeed));
|
||||
|
||||
/* If we should check status after setting mode, do so. */
|
||||
if (!check_before) {
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ChangeToHs200() {
|
||||
/* Issue Hs200 command. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs200));
|
||||
|
||||
/* Set the host controller to Hs200. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs200));
|
||||
|
||||
/* Perform tuning using command index 21. */
|
||||
R_TRY(hc->Tuning(SpeedMode_MmcHs200, 21));
|
||||
|
||||
/* Check status. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ChangeToHs400() {
|
||||
/* Change first to Hs200. */
|
||||
R_TRY(this->ChangeToHs200());
|
||||
|
||||
/* Save tuning status. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
hc->SaveTuningStatusForHs400();
|
||||
|
||||
/* Change to high speed. */
|
||||
R_TRY(this->ChangeToHighSpeed(false));
|
||||
|
||||
/* Issue Hs400 command. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteBusWidth8BitDdr));
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs400));
|
||||
|
||||
/* Set the host controller to Hs400. */
|
||||
R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs400));
|
||||
|
||||
/* Check status. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ExtendBusSpeed(u8 device_type, SpeedMode max_sm) {
|
||||
/* We want to switch to the highest speed we can. */
|
||||
/* Check Hs400/Hs200 first. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
if (hc->IsSupportedTuning() && hc->GetBusPower() == BusPower_1_8V) {
|
||||
if (hc->GetBusWidth() == BusWidth_8Bit && IsSupportedHs400(device_type) && max_sm == SpeedMode_MmcHs400) {
|
||||
return this->ChangeToHs400();
|
||||
} else if ((hc->GetBusWidth() == BusWidth_8Bit || hc->GetBusWidth() == BusWidth_4Bit) && IsSupportedHs200(device_type) && (max_sm == SpeedMode_MmcHs400 || max_sm == SpeedMode_MmcHs200)) {
|
||||
return this->ChangeToHs200();
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we can switch to high speed. */
|
||||
if (IsSupportedHighSpeed(device_type)) {
|
||||
return this->ChangeToHighSpeed(true);
|
||||
}
|
||||
|
||||
/* We can't, so stay at normal speeds. */
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size) {
|
||||
/* Start up at an appropriate bus power. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
const BusPower bp = hc->IsSupportedBusPower(BusPower_1_8V) ? BusPower_1_8V : BusPower_3_3V;
|
||||
R_TRY(hc->Startup(bp, BusWidth_1Bit, SpeedMode_MmcIdentification, false));
|
||||
|
||||
/* Wait 1ms for configuration to take. */
|
||||
WaitMicroSeconds(1000);
|
||||
|
||||
/* Wait an additional 74 clocks for configuration to take. */
|
||||
WaitClocks(74, hc->GetDeviceClockFrequencyKHz());
|
||||
|
||||
/* Go to idle state. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandGoIdleState());
|
||||
this->current_partition = MmcPartition_UserData;
|
||||
|
||||
/* Go to ready state. */
|
||||
R_TRY(this->ChangeToReadyState(bp));
|
||||
|
||||
/* Get the CID. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAllSendCid(wb, wb_size));
|
||||
this->mmc_device.SetCid(wb, wb_size);
|
||||
const bool is_toshiba = IsToshibaMmc(static_cast<const u8 *>(wb));
|
||||
|
||||
/* Issue set relative addr. */
|
||||
R_TRY(this->IssueCommandSetRelativeAddr());
|
||||
|
||||
/* Get the CSD. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendCsd(wb, wb_size));
|
||||
this->mmc_device.SetCsd(wb, wb_size);
|
||||
const bool spec_under_4 = IsLessThanSpecification4(static_cast<const u8 *>(wb));
|
||||
|
||||
/* Set the speed mode to legacy. */
|
||||
R_TRY(hc->SetSpeedMode(SpeedMode_MmcLegacySpeed));
|
||||
|
||||
/* Issue select card command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSelectCard());
|
||||
|
||||
/* Set block length to sector size. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize());
|
||||
|
||||
/* If the device SPEC_VERS is less than 4, extended csd/switch aren't supported. */
|
||||
if (spec_under_4) {
|
||||
R_TRY(this->mmc_device.SetLegacyMemoryCapacity());
|
||||
|
||||
this->mmc_device.SetActive();
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Extend the bus width to the largest that we can. */
|
||||
R_TRY(this->ExtendBusWidth(max_bw));
|
||||
|
||||
/* Get the extended csd. */
|
||||
R_TRY(this->IssueCommandSendExtCsd(wb, wb_size));
|
||||
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(wb), alignof(u32)));
|
||||
this->mmc_device.SetMemoryCapacity(GetMemoryCapacityFromExtCsd(static_cast<const u32 *>(wb)));
|
||||
|
||||
/* If the mmc is manufactured by toshiba, try to enable bkops auto. */
|
||||
if (is_toshiba && !IsBkopAutoEnable(static_cast<const u8 *>(wb))) {
|
||||
/* NOTE: Nintendo does not check the result of this. */
|
||||
this->EnableBkopsAuto();
|
||||
}
|
||||
|
||||
/* Extend the bus speed to as fast as we can. */
|
||||
const u8 device_type = GetDeviceType(static_cast<const u8 *>(wb));
|
||||
R_TRY(this->ExtendBusSpeed(device_type, max_sm));
|
||||
|
||||
/* Enable power saving. */
|
||||
hc->SetPowerSaving(true);
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::OnActivate() {
|
||||
/* Define the possible startup parameters. */
|
||||
constexpr const struct {
|
||||
BusWidth bus_width;
|
||||
SpeedMode speed_mode;
|
||||
} StartupParameters[] = {
|
||||
#if defined(AMS_SDMMC_ENABLE_MMC_HS400)
|
||||
{ BusWidth_8Bit, SpeedMode_MmcHs400 },
|
||||
#else
|
||||
{ BusWidth_8Bit, SpeedMode_MmcHighSpeed },
|
||||
#endif
|
||||
{ BusWidth_8Bit, SpeedMode_MmcHighSpeed },
|
||||
{ BusWidth_1Bit, SpeedMode_MmcHighSpeed },
|
||||
};
|
||||
|
||||
/* Try to start up with each set of parameters. */
|
||||
Result result;
|
||||
for (int i = 0; i < static_cast<int>(util::size(StartupParameters)); ++i) {
|
||||
/* Alias the parameters. */
|
||||
const auto ¶ms = StartupParameters[i];
|
||||
|
||||
/* Set our max bus width/speed mode. */
|
||||
this->max_bus_width = params.bus_width;
|
||||
this->max_speed_mode = params.speed_mode;
|
||||
|
||||
/* Try to start up the device. */
|
||||
result = this->StartupMmcDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size);
|
||||
if (R_SUCCEEDED(result)) {
|
||||
/* If we previously failed to start up the device, log the error correction. */
|
||||
if (i != 0) {
|
||||
BaseDeviceAccessor::PushErrorLog(true, "S %d %d:0", this->max_bus_width, this->max_speed_mode);
|
||||
BaseDeviceAccessor::IncrementNumActivationErrorCorrections();
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
/* Log that our startup failed. */
|
||||
BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue());
|
||||
|
||||
/* Shut down the host controller before we try to start up again. */
|
||||
BaseDeviceAccessor::GetHostController()->Shutdown();
|
||||
}
|
||||
|
||||
/* We failed to start up with all sets of parameters. */
|
||||
BaseDeviceAccessor::PushErrorTimeStamp();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) {
|
||||
/* Get the sector index alignment. */
|
||||
u32 sector_index_alignment = 0;
|
||||
if (!is_read) {
|
||||
constexpr u32 MmcWriteSectorAlignment = 16_KB / SectorSize;
|
||||
sector_index_alignment = MmcWriteSectorAlignment;
|
||||
AMS_ABORT_UNLESS(util::IsAligned(sector_index, MmcWriteSectorAlignment));
|
||||
}
|
||||
|
||||
/* Do the read/write. */
|
||||
return BaseDeviceAccessor::ReadWriteMultiple(sector_index, num_sectors, sector_index_alignment, buf, buf_size, is_read);
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ReStartup() {
|
||||
/* Shut down the host controller. */
|
||||
BaseDeviceAccessor::GetHostController()->Shutdown();
|
||||
|
||||
/* Perform start up. */
|
||||
Result result = this->StartupMmcDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size);
|
||||
if (R_FAILED(result)) {
|
||||
BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue());
|
||||
return result;
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void MmcDeviceAccessor::Initialize() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* If we've already initialized, we don't need to do anything. */
|
||||
if (this->is_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the base device to our mmc device. */
|
||||
BaseDeviceAccessor::SetDevice(std::addressof(this->mmc_device));
|
||||
|
||||
/* Initialize. */
|
||||
BaseDeviceAccessor::GetHostController()->Initialize();
|
||||
this->is_initialized = true;
|
||||
}
|
||||
|
||||
void MmcDeviceAccessor::Finalize() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* If we've already finalized, we don't need to do anything. */
|
||||
if (!this->is_initialized) {
|
||||
return;
|
||||
}
|
||||
this->is_initialized = false;
|
||||
|
||||
/* Deactivate the device. */
|
||||
BaseDeviceAccessor::Deactivate();
|
||||
|
||||
/* Finalize the host controller. */
|
||||
BaseDeviceAccessor::GetHostController()->Finalize();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const {
|
||||
/* Check that we can write to output. */
|
||||
AMS_ABORT_UNLESS(out_speed_mode != nullptr);
|
||||
|
||||
/* Get the current speed mode from the ext csd. */
|
||||
R_TRY(GetMmcExtendedCsd(this->work_buffer, this->work_buffer_size));
|
||||
R_TRY(GetCurrentSpeedModeFromExtCsd(out_speed_mode, static_cast<const u8 *>(this->work_buffer)));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
void MmcDeviceAccessor::PutMmcToSleep() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* If the device isn't awake, we don't need to do anything. */
|
||||
if (!this->mmc_device.IsAwake()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Put the device to sleep. */
|
||||
this->mmc_device.PutToSleep();
|
||||
|
||||
/* If necessary, put the host controller to sleep. */
|
||||
if (this->mmc_device.IsActive()) {
|
||||
BaseDeviceAccessor::GetHostController()->PutToSleep();
|
||||
}
|
||||
}
|
||||
|
||||
void MmcDeviceAccessor::AwakenMmc() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* If the device is awake, we don't need to do anything. */
|
||||
if (this->mmc_device.IsAwake()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wake the host controller, if we need to.*/
|
||||
if (this->mmc_device.IsActive()) {
|
||||
const Result result = BaseDeviceAccessor::GetHostController()->Awaken();
|
||||
if (R_FAILED(result)) {
|
||||
BaseDeviceAccessor::PushErrorLog(true, "A:%X", result.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
/* Wake the device. */
|
||||
this->mmc_device.Awaken();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::SelectMmcPartition(MmcPartition part) {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we can access the device. */
|
||||
R_TRY(this->mmc_device.CheckAccessible());
|
||||
|
||||
/* Determine the appropriate SWITCH subcommand. */
|
||||
CommandSwitch cs;
|
||||
switch (part) {
|
||||
case MmcPartition_UserData: cs = CommandSwitch_WritePartitionAccessDefault; break;
|
||||
case MmcPartition_BootPartition1: cs = CommandSwitch_WritePartitionAccessRwBootPartition1; break;
|
||||
case MmcPartition_BootPartition2: cs = CommandSwitch_WritePartitionAccessRwBootPartition2; break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Change partition. */
|
||||
this->current_partition = MmcPartition_Unknown;
|
||||
{
|
||||
R_TRY(this->IssueCommandSwitch(cs));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
}
|
||||
this->current_partition = part;
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::EraseMmc() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we can access the device. */
|
||||
R_TRY(this->mmc_device.CheckAccessible());
|
||||
|
||||
/* Get the partition capacity. */
|
||||
u32 part_capacity;
|
||||
switch (this->current_partition) {
|
||||
case MmcPartition_UserData:
|
||||
part_capacity = this->mmc_device.GetMemoryCapacity();
|
||||
break;
|
||||
case MmcPartition_BootPartition1:
|
||||
case MmcPartition_BootPartition2:
|
||||
R_TRY(this->GetMmcBootPartitionCapacity(std::addressof(part_capacity)));
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Begin the erase. */
|
||||
R_TRY(this->IssueCommandEraseGroupStart(0));
|
||||
R_TRY(this->IssueCommandEraseGroupEnd(part_capacity - 1));
|
||||
|
||||
/* Issue the erase command, allowing 30 seconds for it to complete. */
|
||||
ManualTimer timer(30000);
|
||||
Result result = this->IssueCommandErase();
|
||||
R_TRY_CATCH(result) {
|
||||
R_CATCH(sdmmc::ResultDataTimeoutError) { /* Data timeout error is acceptable. */ }
|
||||
R_CATCH(sdmmc::ResultCommandCompleteSoftwareTimeout) { /* Command complete software timeout error is acceptable. */ }
|
||||
R_CATCH(sdmmc::ResultBusySoftwareTimeout) { /* Busy software timeout error is acceptable. */ }
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Wait for the erase to finish. */
|
||||
while (true) {
|
||||
/* Check if we're done. */
|
||||
result = BaseDeviceAccessor::IssueCommandSendStatus();
|
||||
if (R_SUCCEEDED(result)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Otherwise, check if we should reject the error. */
|
||||
if (!sdmmc::ResultUnexpectedDeviceState::Includes(result)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Check if timeout has been exceeded. */
|
||||
R_UNLESS(timer.Update(), sdmmc::ResultMmcEraseSoftwareTimeout());
|
||||
}
|
||||
|
||||
/* If the partition is user data, check if we need to perform toshiba-specific erase. */
|
||||
if (this->current_partition == MmcPartition_UserData) {
|
||||
u8 cid[DeviceCidSize];
|
||||
this->mmc_device.GetCid(cid, sizeof(cid));
|
||||
if (IsToshibaMmc(cid)) {
|
||||
/* NOTE: Nintendo does not check the result of this operation. */
|
||||
this->CancelToshibaMmcModel();
|
||||
}
|
||||
}
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::GetMmcBootPartitionCapacity(u32 *out_num_sectors) const {
|
||||
/* Get the capacity from the extended csd. */
|
||||
AMS_ABORT_UNLESS(out_num_sectors != nullptr);
|
||||
R_TRY(this->GetMmcExtendedCsd(this->work_buffer, this->work_buffer_size));
|
||||
|
||||
*out_num_sectors = GetBootPartitionMemoryCapacityFromExtCsd(static_cast<const u8 *>(this->work_buffer));
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::GetMmcExtendedCsd(void *dst, size_t dst_size) const {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we can access the device. */
|
||||
R_TRY(this->mmc_device.CheckAccessible());
|
||||
|
||||
/* Get the csd. */
|
||||
u8 csd[DeviceCsdSize];
|
||||
this->mmc_device.GetCsd(csd, sizeof(csd));
|
||||
|
||||
/* Check that the card supports ext csd. */
|
||||
R_UNLESS(!IsLessThanSpecification4(csd), sdmmc::ResultMmcNotSupportExtendedCsd());
|
||||
|
||||
/* Get the ext csd. */
|
||||
R_TRY(this->IssueCommandSendExtCsd(dst, dst_size));
|
||||
|
||||
return ResultSuccess();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <vapours.hpp>
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
class MmcDevice : public BaseDevice {
|
||||
private:
|
||||
static constexpr u16 Rca = 2;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual os::EventType *GetRemovedEvent() const override {
|
||||
/* Mmc can't be removed. */
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual DeviceType GetDeviceType() const override {
|
||||
return DeviceType_Mmc;
|
||||
}
|
||||
|
||||
virtual u16 GetRca() const override {
|
||||
return Rca;
|
||||
}
|
||||
|
||||
void SetOcrAndHighCapacity(u32 ocr);
|
||||
};
|
||||
|
||||
class MmcDeviceAccessor : public BaseDeviceAccessor {
|
||||
private:
|
||||
MmcDevice mmc_device;
|
||||
void *work_buffer;
|
||||
size_t work_buffer_size;
|
||||
BusWidth max_bus_width;
|
||||
SpeedMode max_speed_mode;
|
||||
MmcPartition current_partition;
|
||||
bool is_initialized;
|
||||
private:
|
||||
enum CommandSwitch {
|
||||
CommandSwitch_SetBitsProductionStateAwarenessEnable = 0,
|
||||
CommandSwitch_ClearBitsAutoModeEnable = 1,
|
||||
CommandSwitch_WriteProductionStateAwarenessNormal = 2,
|
||||
CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites = 3,
|
||||
CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites = 4,
|
||||
CommandSwitch_SetBitsBkopsEnAutoEn = 5,
|
||||
CommandSwitch_WriteBusWidth1Bit = 6,
|
||||
CommandSwitch_WriteBusWidth4Bit = 7,
|
||||
CommandSwitch_WriteBusWidth8Bit = 8,
|
||||
CommandSwitch_WriteBusWidth8BitDdr = 9,
|
||||
CommandSwitch_WriteHsTimingLegacySpeed = 10,
|
||||
CommandSwitch_WriteHsTimingHighSpeed = 11,
|
||||
CommandSwitch_WriteHsTimingHs200 = 12,
|
||||
CommandSwitch_WriteHsTimingHs400 = 13,
|
||||
CommandSwitch_WritePartitionAccessDefault = 14,
|
||||
CommandSwitch_WritePartitionAccessRwBootPartition1 = 15,
|
||||
CommandSwitch_WritePartitionAccessRwBootPartition2 = 16,
|
||||
};
|
||||
|
||||
static constexpr ALWAYS_INLINE u32 GetCommandSwitchArgument(CommandSwitch cs) {
|
||||
switch (cs) {
|
||||
case CommandSwitch_SetBitsProductionStateAwarenessEnable: return 0x01111000;
|
||||
case CommandSwitch_ClearBitsAutoModeEnable: return 0x02112000;
|
||||
case CommandSwitch_WriteProductionStateAwarenessNormal: return 0x03850000;
|
||||
case CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites: return 0x03850100;
|
||||
case CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites: return 0x03850200;
|
||||
case CommandSwitch_SetBitsBkopsEnAutoEn: return 0x01A30200;
|
||||
case CommandSwitch_WriteBusWidth1Bit: return 0x03B70000;
|
||||
case CommandSwitch_WriteBusWidth4Bit: return 0x03B70100;
|
||||
case CommandSwitch_WriteBusWidth8Bit: return 0x03B70200;
|
||||
case CommandSwitch_WriteBusWidth8BitDdr: return 0x03B70600;
|
||||
case CommandSwitch_WriteHsTimingLegacySpeed: return 0x03B90000;
|
||||
case CommandSwitch_WriteHsTimingHighSpeed: return 0x03B90100;
|
||||
case CommandSwitch_WriteHsTimingHs200: return 0x03B90200;
|
||||
case CommandSwitch_WriteHsTimingHs400: return 0x03B90300;
|
||||
case CommandSwitch_WritePartitionAccessDefault: return 0x03B30000;
|
||||
case CommandSwitch_WritePartitionAccessRwBootPartition1: return 0x03B30100;
|
||||
case CommandSwitch_WritePartitionAccessRwBootPartition2: return 0x03B30200;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
private:
|
||||
Result IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const;
|
||||
Result IssueCommandSetRelativeAddr() const;
|
||||
Result IssueCommandSwitch(CommandSwitch cs) const;
|
||||
Result IssueCommandSendExtCsd(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandEraseGroupStart(u32 sector_index) const;
|
||||
Result IssueCommandEraseGroupEnd(u32 sector_index) const;
|
||||
Result IssueCommandErase() const;
|
||||
Result CancelToshibaMmcModel();
|
||||
Result ChangeToReadyState(BusPower bus_power);
|
||||
Result ExtendBusWidth(BusWidth max_bus_width);
|
||||
Result EnableBkopsAuto();
|
||||
Result ChangeToHighSpeed(bool check_before);
|
||||
Result ChangeToHs200();
|
||||
Result ChangeToHs400();
|
||||
Result ExtendBusSpeed(u8 device_type, SpeedMode max_sm);
|
||||
Result StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size);
|
||||
protected:
|
||||
virtual Result OnActivate() override;
|
||||
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
|
||||
virtual Result ReStartup() override;
|
||||
public:
|
||||
virtual void Initialize() override;
|
||||
virtual void Finalize() override;
|
||||
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
|
||||
public:
|
||||
explicit MmcDeviceAccessor(IHostController *hc)
|
||||
: BaseDeviceAccessor(hc), work_buffer(nullptr), work_buffer_size(0),
|
||||
max_bus_width(BusWidth_8Bit), max_speed_mode(SpeedMode_MmcHs400), current_partition(MmcPartition_Unknown),
|
||||
is_initialized(false)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void SetMmcWorkBuffer(void *wb, size_t wb_size) {
|
||||
this->work_buffer = wb;
|
||||
this->work_buffer_size = wb_size;
|
||||
}
|
||||
|
||||
void PutMmcToSleep();
|
||||
void AwakenMmc();
|
||||
Result SelectMmcPartition(MmcPartition part);
|
||||
Result EraseMmc();
|
||||
Result GetMmcBootPartitionCapacity(u32 *out_num_sectors) const;
|
||||
Result GetMmcExtendedCsd(void *dst, size_t dst_size) const;
|
||||
};
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue