diff --git a/.gitignore b/.gitignore index c8c423006..78f859ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -91,3 +91,7 @@ sept/sept-secondary/KEYS.py **/out **/build +**/build_nintendo_nx_arm64 +**/build_nintendo_nx_arm +**/build_nintendo_nx_x64 +**/build_nintendo_nx_x86 diff --git a/Makefile b/Makefile index 759ee1d77..5533e6bbc 100644 --- a/Makefile +++ b/Makefile @@ -71,8 +71,8 @@ dist-no-debug: all mkdir -p atmosphere-$(AMSVER)/atmosphere/config cp fusee/fusee-primary/fusee-primary.bin atmosphere-$(AMSVER)/atmosphere/reboot_payload.bin cp fusee/fusee-mtc/fusee-mtc.bin atmosphere-$(AMSVER)/atmosphere/fusee-mtc.bin - cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin - cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin + cp fusee/fusee-secondary/fusee-secondary-experimental.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin + cp fusee/fusee-secondary/fusee-secondary-experimental.bin atmosphere-$(AMSVER)/sept/payload.bin cp sept/sept-primary/sept-primary.bin atmosphere-$(AMSVER)/sept/sept-primary.bin cp sept/sept-secondary/sept-secondary.bin atmosphere-$(AMSVER)/sept/sept-secondary.bin cp sept/sept-secondary/sept-secondary_00.enc atmosphere-$(AMSVER)/sept/sept-secondary_00.enc @@ -101,9 +101,13 @@ dist-no-debug: all touch atmosphere-$(AMSVER)/atmosphere/contents/0100000000000037/flags/boot2.flag cp troposphere/reboot_to_payload/reboot_to_payload.nro atmosphere-$(AMSVER)/switch/reboot_to_payload.nro cp troposphere/daybreak/daybreak.nro atmosphere-$(AMSVER)/switch/daybreak.nro + cd atmosphere-$(AMSVER); zip -r ../atmosphere-EXPERIMENTAL-$(AMSVER).zip ./*; cd ../; + cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/atmosphere/fusee-secondary.bin + cp fusee/fusee-secondary/fusee-secondary.bin atmosphere-$(AMSVER)/sept/payload.bin cd atmosphere-$(AMSVER); zip -r ../atmosphere-$(AMSVER).zip ./*; cd ../; rm -r atmosphere-$(AMSVER) mkdir out + mv atmosphere-EXPERIMENTAL-$(AMSVER).zip out/atmosphere-EXPERIMENTAL-$(AMSVER).zip mv atmosphere-$(AMSVER).zip out/atmosphere-$(AMSVER).zip cp fusee/fusee-primary/fusee-primary.bin out/fusee-primary.bin @@ -122,7 +126,7 @@ dist: dist-no-debug mkdir atmosphere-$(AMSVER)-debug cp fusee/fusee-primary/fusee-primary.elf atmosphere-$(AMSVER)-debug/fusee-primary.elf cp fusee/fusee-mtc/fusee-mtc.elf atmosphere-$(AMSVER)-debug/fusee-mtc.elf - cp fusee/fusee-secondary/fusee-secondary.elf atmosphere-$(AMSVER)-debug/fusee-secondary.elf + cp fusee/fusee-secondary/fusee-secondary-experimental.elf atmosphere-$(AMSVER)-debug/fusee-secondary.elf cp sept/sept-primary/sept-primary.elf atmosphere-$(AMSVER)-debug/sept-primary.elf cp sept/sept-secondary/sept-secondary.elf atmosphere-$(AMSVER)-debug/sept-secondary.elf cp sept/sept-secondary/key_derivation/key_derivation.elf atmosphere-$(AMSVER)-debug/sept-secondary-key-derivation.elf diff --git a/config_templates/BCT.ini b/config_templates/BCT.ini index 9e23c843b..74dd6f648 100644 --- a/config_templates/BCT.ini +++ b/config_templates/BCT.ini @@ -9,4 +9,4 @@ stage2_entrypoint = 0xF0000000 ; To force-enable nogc, add nogc = 1 ; To force-disable nogc, add nogc = 0 -; To opt in to using Atmosphere's NCM reimplementation, add enable_ncm = 1 +; To opt out of using Atmosphere's NCM reimplementation, add disable_ncm = 1 diff --git a/config_templates/kip_patches/default_nogc/160D3E104EAD6176991B8AE7AFA2B78BC5879FC07BE88A4FC9E16812971BC161.ips b/config_templates/kip_patches/default_nogc/160D3E104EAD6176991B8AE7AFA2B78BC5879FC07BE88A4FC9E16812971BC161.ips new file mode 100644 index 000000000..2d018e38f Binary files /dev/null and b/config_templates/kip_patches/default_nogc/160D3E104EAD6176991B8AE7AFA2B78BC5879FC07BE88A4FC9E16812971BC161.ips differ diff --git a/config_templates/kip_patches/default_nogc/A952B657ADF9C2BA1434BA9B8B86F3317D20659A8AC8510D8ECF4CE1BD7593E2.ips b/config_templates/kip_patches/default_nogc/A952B657ADF9C2BA1434BA9B8B86F3317D20659A8AC8510D8ECF4CE1BD7593E2.ips new file mode 100644 index 000000000..2d018e38f Binary files /dev/null and b/config_templates/kip_patches/default_nogc/A952B657ADF9C2BA1434BA9B8B86F3317D20659A8AC8510D8ECF4CE1BD7593E2.ips differ diff --git a/docs/building.md b/docs/building.md index a96173c9a..b5c8f7bd4 100644 --- a/docs/building.md +++ b/docs/building.md @@ -4,7 +4,7 @@ Building Atmosphère is a very straightforward process that relies almost exclus ## Dependencies + [devkitA64](https://devkitpro.org) + [devkitARM](https://devkitpro.org) -+ [Python 2 or 3](https://www.python.org) (optional) ++ [Python 2](https://www.python.org) (Python 3 may work as well, but this is not guaranteed) + [PyCryptodome](https://pypi.org/project/pycryptodome) (optional) ## Instructions diff --git a/docs/changelog.md b/docs/changelog.md index 8f436a89f..eb3d2e6af 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,4 +1,54 @@ # Changelog +## 0.15.0 ++ fusee-primary's panic display was updated to automatically identify and give suggestions to resolve many of the most common errors users encounter. ++ Having been tested as well as I can alone, `mesosphere` (atmosphère's reimplementation of the Nintendo Switch kernel) is now available for users interested in trying it. + + Beginning in this release and until it is stable and well-tested, atmosphère will distribute two zips. + + Users who wish to opt-in to mesosphere should download and extract the "cool kids" zip ("atmosphere-EXPERIMENTAL-"). + + Users who do not wish to use mesosphere should continue using the normal zip ("atmosphere-"). + + Users may detect whether mesosphere is active in system settings. + + When mesosphere is active, the system version string will display "M.15.0" rather than "0.15.0", and so on for future releases. + + Crash reports and the like will contain information on whether or not the user is using mesosphere, as well. + + There are "probably" no material user-facing benefits to using mesosphere at this time. + + Developers may be interested in the fact that mesosphere provides many newer SVC APIs even when on lower firmware versions. + + The primary benefit to using mesosphere is that any issues you may encounter and report to me will be fixed. + + All users who choose to opt in to using mesosphere have my deepest gratitude. + + **Note:** If using hekate instead of fusee-primary, you will have to wait for the next hekate release for mesosphere to function, as hekate's support has not yet been included in an official release build. + + This will be updated in the release notes when hekate provides a new release. + + As mentioned in previous release notes, when mesosphere is stable and well-tested, it will be enabled by default and atmosphère's version will transition to 1.0.0. ++ Having been tested sufficiently over the last half-year, Atmosphere's NCM implementation is now opt-out, rather than opt in. + + In the unlikely event that any issues are encountered, please report them to @SciresM. + + Users interested in opting out of using our implementation should set `stratosphere!disable_ncm = 1` in BCT.ini. + + The NCM implementation will stop being opt-out in a future update, probably around the same time that mesosphere becomes opt-out instead of opt-in. ++ Several bugs were fixed, including: + + Loader now sets HBL's thread priority to a higher value when loading it in applet mode. + + This fixes an extremely-slow launch ("hang") when using applet-HBL with certain games that do not suspend while inactive (e.g. Super Mario Sunshine). + + set.mitm now caches user language configuration much more heavily. + + This severely reduces lag in certain games which misuse the "nn::oe::GetDesiredLanguage()" API. + + A bug was fixed that could cause erpt to fatal when loading an official save file that had error report attachments in it. ++ General system stability improvements to enhance the user's experience. +## 0.14.4 ++ Several bugs were fixed involving the official jit sysmodule added in 10.0.0. + + A Process handle leak was fixed when JitPlugin NRRs were registered with the `ro` sysmodule. + + This prevented processes using jit from being able to exit, causing a full system freeze. + + The `sm` atmosphere extension to not unregister services when the server's connection is closed was special-case disabled for `jit:u`. + + This extension is normally desirable in order to allow more concurrent processes to exist (as only 0x40 sm connections may ever be concurrently open), but official jit sysmodule relies on the behavior. + + This would cause crashes on attempts to launch a program using jit services more than once per reboot. ++ General system stability improvements to enhance the user's experience. +## 0.14.3 ++ Support was added for 10.2.0. ++ General system stability improvements to enhance the user's experience. +## 0.14.2 ++ A bug was fixed that could cause a deadlock when installing mitm services. + + Fixing this required a breaking change to the client behavior when installing a mitm service, and so custom sysmodules which use mitm will need to be re-compiled to function properly. ++ A bug was fixed that caused atmosphere sysmodules to respond incorrectly when receiving invalid messages. ++ A bug was fixed that caused fatal auto-reboot timing to work improperly. ++ Support was added to fusee for loading binaries for `mesosphere`, atmosphère's reimplementation of the Nintendo Switch kernel. + + 0.14.2 does not include mesosphere, but those who are especially interested can build and test mesosphere themselves. + + In the future, to enable a sufficient testing period Atmosphère releases will distribute two zips for some time. + + One zip will use mesosphere, and the other will not. + + This will allow users who are interested to opt-in to mesosphere usage before it has been tested to be stable. + + When mesosphere is stable and well-tested, it will be enabled by default and Atmosphère's version will transition to 1.0.0. ++ General system stability improvements to enhance the user's experience. ## 0.14.1 + An issue was fixed in 0.14.0 that would cause a black screen on boot when the INI1's size was not aligned to 8 bytes. + General system stability improvements to enhance the user's experience. diff --git a/docs/features/configurations.md b/docs/features/configurations.md index 858ec443c..f7f4cd111 100644 --- a/docs/features/configurations.md +++ b/docs/features/configurations.md @@ -9,7 +9,7 @@ This file is located under the `/atmosphere/config/` folder on your SD card and Atmosphère provides its own default splashscreen which is displayed at boot time. However, this can be replaced at will. The boot splashscreen must be a BMP file, it must be 720x1280 (1280x720 rotated 90 degrees left/counterclockwise/anti-clockwise) resolution, and be in 32-bit ARGB format. You can use image editing software such as GIMP or Photoshop to export the image in this format. - + Add the following lines to BCT.ini and change the value of `custom_splash` to the actual path and filename of your boot splashscreen: ``` [stage2] @@ -29,11 +29,11 @@ nogc = X 0 = force-disable nogc, so Atmosphère will always enable the Game Card reader. ``` -### NCM opt-in -Atmosphère provides a reimplementation of the [ncm](../components/modules/ncm.md) system module, but currently this is not enabled by default. If you wish to enable this reimplementation add the following line to the `stratosphere` section: +### NCM opt-out +Atmosphère provides a reimplementation of the [ncm](../components/modules/ncm.md) system module. If you wish to disable this reimplementation add the following line to the `stratosphere` section: ``` [stratosphere] -enable_ncm = 1 +disable_ncm = 1 ``` ### Logging diff --git a/emummc/.gitrepo b/emummc/.gitrepo index 643c3ea7a..427b1d33e 100644 --- a/emummc/.gitrepo +++ b/emummc/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/m4xw/emuMMC branch = exo2 - commit = 06ab9b895c4264ecc14d3bf9be1260e2096f6037 - parent = dccd41f6d25498c191a157123a27724203d3bc37 + commit = 6a814ebbe72cf5245b863b9edea9cd3801437a12 + parent = f551ca4461a79908c37307db10cd8cacf8b98f17 method = rebase cmdver = 0.4.1 diff --git a/emummc/source/FS/FS_offsets.c b/emummc/source/FS/FS_offsets.c index d7da45c4a..821d31061 100644 --- a/emummc/source/FS/FS_offsets.c +++ b/emummc/source/FS/FS_offsets.c @@ -47,6 +47,8 @@ #include "offsets/910_exfat.h" #include "offsets/1000.h" #include "offsets/1000_exfat.h" +#include "offsets/1020.h" +#include "offsets/1020_exfat.h" #include "../utils/fatal.h" #define GET_OFFSET_STRUCT_NAME(vers) g_offsets##vers @@ -104,6 +106,8 @@ DEFINE_OFFSET_STRUCT(_910); DEFINE_OFFSET_STRUCT(_910_EXFAT); DEFINE_OFFSET_STRUCT(_1000); DEFINE_OFFSET_STRUCT(_1000_EXFAT); +DEFINE_OFFSET_STRUCT(_1020); +DEFINE_OFFSET_STRUCT(_1020_EXFAT); const fs_offsets_t *get_fs_offsets(enum FS_VER version) { switch (version) { @@ -169,6 +173,10 @@ const fs_offsets_t *get_fs_offsets(enum FS_VER version) { return &(GET_OFFSET_STRUCT_NAME(_1000)); case FS_VER_10_0_0_EXFAT: return &(GET_OFFSET_STRUCT_NAME(_1000_EXFAT)); + case FS_VER_10_2_0: + return &(GET_OFFSET_STRUCT_NAME(_1020)); + case FS_VER_10_2_0_EXFAT: + return &(GET_OFFSET_STRUCT_NAME(_1020_EXFAT)); default: fatal_abort(Fatal_UnknownVersion); } diff --git a/emummc/source/FS/FS_versions.h b/emummc/source/FS/FS_versions.h index 2a12f7a00..3d7826879 100644 --- a/emummc/source/FS/FS_versions.h +++ b/emummc/source/FS/FS_versions.h @@ -68,6 +68,9 @@ enum FS_VER FS_VER_10_0_0, FS_VER_10_0_0_EXFAT, + FS_VER_10_2_0, + FS_VER_10_2_0_EXFAT, + FS_VER_MAX, }; diff --git a/emummc/source/FS/offsets/1020.h b/emummc/source/FS/offsets/1020.h new file mode 100644 index 000000000..2d43be5cd --- /dev/null +++ b/emummc/source/FS/offsets/1020.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS_1020_H__ +#define __FS_1020_H__ + +// Accessor vtable getters +#define FS_OFFSET_1020_SDMMC_ACCESSOR_GC 0x14E0F0 +#define FS_OFFSET_1020_SDMMC_ACCESSOR_SD 0x14C200 +#define FS_OFFSET_1020_SDMMC_ACCESSOR_NAND 0x147080 + +// Hooks +#define FS_OFFSET_1020_SDMMC_WRAPPER_READ 0x1427E0 +#define FS_OFFSET_1020_SDMMC_WRAPPER_WRITE 0x1428C0 +#define FS_OFFSET_1020_RTLD 0x634 +#define FS_OFFSET_1020_RTLD_DESTINATION 0x9C + +#define FS_OFFSET_1020_CLKRST_SET_MIN_V_CLK_RATE 0x141A00 + +// Misc funcs +#define FS_OFFSET_1020_LOCK_MUTEX 0x28910 +#define FS_OFFSET_1020_UNLOCK_MUTEX 0x28960 + +#define FS_OFFSET_1020_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x142740 + +// Misc Data +#define FS_OFFSET_1020_SD_MUTEX 0xE273E8 +#define FS_OFFSET_1020_NAND_MUTEX 0xE22DA0 +#define FS_OFFSET_1020_ACTIVE_PARTITION 0xE22DE0 +#define FS_OFFSET_1020_SDMMC_DAS_HANDLE 0xE0AB90 + +// NOPs +#define FS_OFFSET_1020_SD_DAS_INIT 0x15214C + +// Nintendo Paths +#define FS_OFFSET_1020_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0006BBA4, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x00078520, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007ED0C, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x0009115C, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ +} + +#endif // __FS_1020_H__ diff --git a/emummc/source/FS/offsets/1020_exfat.h b/emummc/source/FS/offsets/1020_exfat.h new file mode 100644 index 000000000..0ed6a8fcf --- /dev/null +++ b/emummc/source/FS/offsets/1020_exfat.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019 m4xw + * Copyright (c) 2019 Atmosphere-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef __FS_1020_EXFAT_H__ +#define __FS_1020_EXFAT_H__ + +// Accessor vtable getters +#define FS_OFFSET_1020_EXFAT_SDMMC_ACCESSOR_GC 0x14E0F0 +#define FS_OFFSET_1020_EXFAT_SDMMC_ACCESSOR_SD 0x14C200 +#define FS_OFFSET_1020_EXFAT_SDMMC_ACCESSOR_NAND 0x147080 + +// Hooks +#define FS_OFFSET_1020_EXFAT_SDMMC_WRAPPER_READ 0x1427E0 +#define FS_OFFSET_1020_EXFAT_SDMMC_WRAPPER_WRITE 0x1428C0 +#define FS_OFFSET_1020_EXFAT_RTLD 0x634 +#define FS_OFFSET_1020_EXFAT_RTLD_DESTINATION 0x9C + +#define FS_OFFSET_1020_EXFAT_CLKRST_SET_MIN_V_CLK_RATE 0x141A00 + +// Misc funcs +#define FS_OFFSET_1020_EXFAT_LOCK_MUTEX 0x28910 +#define FS_OFFSET_1020_EXFAT_UNLOCK_MUTEX 0x28960 + +#define FS_OFFSET_1020_EXFAT_SDMMC_WRAPPER_CONTROLLER_CLOSE 0x142740 + +// Misc Data +#define FS_OFFSET_1020_EXFAT_SD_MUTEX 0xE353E8 +#define FS_OFFSET_1020_EXFAT_NAND_MUTEX 0xE30DA0 +#define FS_OFFSET_1020_EXFAT_ACTIVE_PARTITION 0xE30DE0 +#define FS_OFFSET_1020_EXFAT_SDMMC_DAS_HANDLE 0xE18B90 + +// NOPs +#define FS_OFFSET_1020_EXFAT_SD_DAS_INIT 0x15214C + +// Nintendo Paths +#define FS_OFFSET_1020_EXFAT_NINTENDO_PATHS \ +{ \ + {.opcode_reg = 3, .adrp_offset = 0x0006BBA4, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x00078520, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 3, .adrp_offset = 0x0007ED0C, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 4, .adrp_offset = 0x0009115C, .add_rel_offset = 0x00000004}, \ + {.opcode_reg = 0, .adrp_offset = 0, .add_rel_offset = 0}, \ +} + +#endif // __FS_1020_EXFAT_H__ diff --git a/exosphere/Makefile b/exosphere/Makefile index bf39b341b..b89cd3d24 100644 --- a/exosphere/Makefile +++ b/exosphere/Makefile @@ -1,47 +1,52 @@ -TARGETS := exosphere.bin warmboot.bin program.lz4 -CLEAN_TARGETS := exosphere-clean program-clean boot_code-clean warmboot-clean +ATMOSPHERE_BUILD_CONFIGS := +all: release -SUBFOLDERS := $(MODULES) +define ATMOSPHERE_ADD_TARGET -all: exosphere.bin warmboot.bin +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) -clean: $(CLEAN_TARGETS) - @rm -f exosphere.bin +$(strip $1): exosphere$(strip $2).bin warmboot/warmboot$(strip $2).bin + @cp warmboot/warmboot$(strip $2).bin warmboot$(strip $2).bin -exosphere.bin: program.lz4 boot_code.lz4 - $(MAKE) -C loader_stub - @cp loader_stub/loader_stub.bin exosphere.bin - @printf LENY >> exosphere.bin - @echo "Built exosphere.bin..." +exosphere$(strip $2).bin: loader_stub/loader_stub$(strip $2).bin + @cp loader_stub/loader_stub$(strip $2).bin exosphere$(strip $2).bin + @printf LENY >> exosphere$(strip $2).bin + @echo "Built exosphere$(strip $2).bin..." -program.lz4: build_program - @cp program/program.lz4 program.lz4 - @cp program/boot_code.lz4 boot_code.lz4 +warmboot/warmboot$(strip $2).bin: + @$$(MAKE) -C warmboot $(strip $1) -build_program: - $(MAKE) -C program +loader_stub/loader_stub$(strip $2).bin: + @$$(MAKE) -C loader_stub $(strip $1) -warmboot.bin: build_warmboot - @cp warmboot/warmboot.bin warmboot.bin +clean-$(strip $1): clean-program-$(strip $1) clean-loader_stub-$(strip $1) clean-warmboot-$(strip $1) + @rm -rf exosphere$(strip $2).bin warmboot$(strip $2).bin -build_warmboot: - $(MAKE) -C warmboot +clean-program-$(strip $1): + @$$(MAKE) -C program clean-$(strip $1) -boot_code.lz4: program.lz4 +clean-loader_stub-$(strip $1): + @$$(MAKE) -C loader_stub clean-$(strip $1) -exosphere-clean: - $(MAKE) -C loader_stub clean - @rm -f exosphere.bin +clean-warmboot-$(strip $1): + @$$(MAKE) -C warmboot clean-$(strip $1) -program-clean: - $(MAKE) -C program clean - @rm -f program.lz4 +endef -warmboot-clean: - $(MAKE) -C warmboot clean - @rm -f warmboot.bin +$(eval $(call ATMOSPHERE_ADD_TARGET, release, )) +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug)) +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit)) -boot_code-clean: - @rm -f boot_code.lz4 +clean: clean-program clean-loader_stub clean-warmboot + @rm -rf exosphere*.bin warmboot*.bin -.PHONY: all clean build_program $(CLEAN_TARGETS) +clean-program: + @$(MAKE) -C program clean + +clean-loader_stub: + @$(MAKE) -C loader_stub clean + +clean-warmboot: + @$(MAKE) -C warmboot clean + +.PHONY: all clean clean-program clean-loader_stub clean-warmboot $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config) clean-program-$(config) clean-loader_stub-$(config) clean-warmboot-$(config)) diff --git a/exosphere/loader_stub/Makefile b/exosphere/loader_stub/Makefile index a5725e57e..a350be583 100644 --- a/exosphere/loader_stub/Makefile +++ b/exosphere/loader_stub/Makefile @@ -7,42 +7,23 @@ export ATMOSPHERE_CPU := arm-cortex-a57 #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) 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))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(TOPDIR)/../program - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) - -BINFILES := program.lz4 boot_code.lz4 +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 @@ -58,70 +39,116 @@ else 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 OFILES := $(OFILES_BIN) $(OFILES_SRC) program.lz4.o boot_code.lz4.o 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)) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) -.PHONY: $(BUILD) clean all check_program +export TOPDIR := $(CURRENT_DIRECTORY) + +OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR)) #--------------------------------------------------------------------------------- -all: $(BUILD) check_program -$(BUILD): check_program +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): check_libexo_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \ + ATMOSPHERE_BUILD_TARGET_IDENTIFIER=$(strip $1)\ + ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX=$(strip $2)\ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + LIBEXOSPHERE_NAME=exosphere$(strip $2) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +check_libexo_$(strip $1): + @$$(MAKE) --no-print-directory -C $$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere $$(ATMOSPHERE_ARCH_NAME)-$(strip $1) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf $$(OUTPUT_BASE)$(strip $2).lz4 boot_code$(strip $2).lz4 + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \ +)) + +$(ATMOSPHERE_BUILD_DIR)/%: @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -check_program: - @$(MAKE) -C ../program all #--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) #--------------------------------------------------------------------------------- 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 +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBEXOSPHERE_NAME).a + + +program.lz4.o: program_lz4.h + +program.lz4.o program_lz4.h: $(TOPDIR)/../program/program$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4 + @echo $(notdir $<) + @rm -rf tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + @mkdir -p tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + @cp $(TOPDIR)/../program/program$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4 tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/program.lz4 + @bin2s -a 8 -H program_lz4.h tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/program.lz4 | $(AS) -o program.lz4.o + @rm -rf tmp_program_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + +$(TOPDIR)/../program/program$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4: + @$(MAKE) __RECURSIVE__=0 --no-print-directory -C $(TOPDIR)/../program $(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + +boot_code.lz4.o: boot_code_lz4.h + +boot_code_lz4.h: $(TOPDIR)/../program/boot_code$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4 + @echo $(notdir $<) + @rm -rf tmp_boot_code_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + @mkdir -p tmp_boot_code_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + @cp $(TOPDIR)/../program/boot_code$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4 tmp_boot_code_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/boot_code.lz4 + @bin2s -a 8 -H boot_code_lz4.h tmp_boot_code_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/boot_code.lz4 | $(AS) -o boot_code.lz4.o + @rm -rf tmp_boot_code_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + +$(TOPDIR)/../program/boot_code$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4: $(TOPDIR)/../program/program$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).lz4 %.elf: @echo linking $(notdir $@) $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ @$(NM) -CSn $@ > $(notdir $*.lst) -$(OFILES_SRC) : $(HFILES_BIN) +$(OFILES_SRC) : program_lz4.h boot_code_lz4.h -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o %_bin.h: %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) - -%.lz4.o %_lz4.h: %.lz4 -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) -include $(DEPENDS) diff --git a/exosphere/loader_stub/source/secmon_loader_error.cpp b/exosphere/loader_stub/source/secmon_loader_error.cpp index 2f67b507f..4fc58c6f7 100644 --- a/exosphere/loader_stub/source/secmon_loader_error.cpp +++ b/exosphere/loader_stub/source/secmon_loader_error.cpp @@ -19,10 +19,12 @@ namespace ams::diag { NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) { + AMS_UNUSED(file, line, func, expr, value, format); ams::secmon::loader::ErrorReboot(); } NORETURN void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) { + AMS_UNUSED(file, line, func, expr, value); ams::secmon::loader::ErrorReboot(); } diff --git a/exosphere/program/Makefile b/exosphere/program/Makefile index 23b18af76..750b6690f 100644 --- a/exosphere/program/Makefile +++ b/exosphere/program/Makefile @@ -7,43 +7,23 @@ export ATMOSPHERE_CPU := arm-cortex-a57 #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) 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))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ - $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ - $(TOPDIR)/sc7fw \ - $(TOPDIR)/rebootstub - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) - -BINFILES := sc7fw.bin rebootstub.bin +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 @@ -59,77 +39,122 @@ else 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 OFILES := $(OFILES_BIN) $(OFILES_SRC) rebootstub.bin.o sc7fw.bin.o 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)) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) -.PHONY: $(BUILD) clean all +export TOPDIR := $(CURRENT_DIRECTORY) + +OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR)) #--------------------------------------------------------------------------------- -all: $(BUILD) check_libexo -$(BUILD): check_libexo check_firmwares +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): check_libexo_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \ + ATMOSPHERE_BUILD_TARGET_IDENTIFIER=$(strip $1)\ + ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX=$(strip $2)\ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + LIBEXOSPHERE_NAME=exosphere$(strip $2) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +check_libexo_$(strip $1): + @$$(MAKE) --no-print-directory -C $$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere $$(ATMOSPHERE_ARCH_NAME)-$(strip $1) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf $$(OUTPUT_BASE)$(strip $2).lz4 boot_code$(strip $2).lz4 + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \ +)) + +$(ATMOSPHERE_BUILD_DIR)/%: @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -check_libexo: - @$(MAKE) --no-print-directory -C ../../libraries/libexosphere arm64 - -check_firmwares: - @$(MAKE) -C $(TOPDIR)/sc7fw all - @$(MAKE) -C $(TOPDIR)/rebootstub all #--------------------------------------------------------------------------------- -clean: - @echo clean ... - @$(MAKE) -C $(TOPDIR)/sc7fw clean - @$(MAKE) -C $(TOPDIR)/rebootstub clean - @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf *.lz4 +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) #--------------------------------------------------------------------------------- else -.PHONY: all DEPENDS := $(OFILES:.o=.d) #--------------------------------------------------------------------------------- # main targets #--------------------------------------------------------------------------------- -all : $(OUTPUT).lz4 $(OUTPUT).lz4 : $(OUTPUT).bin - @python ../split_program.py $(OUTPUT).bin $(dir $(OUTPUT)) + @python $(TOPDIR)/split_program.py $(OUTPUT).bin "$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX)" $(dir $(OUTPUT)) @echo built ... $(notdir $@) $(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 +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBEXOSPHERE_NAME).a secmon_crt0_cpp.o secmon_make_page_table.o : CFLAGS += -fno-builtin +rebootstub.bin.o: rebootstub_bin.h + +rebootstub_bin.h: $(TOPDIR)/rebootstub/rebootstub$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).bin + @echo $(notdir $<) + @rm -rf tmp_rebootstub_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + @mkdir -p tmp_rebootstub_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + @cp $(TOPDIR)/rebootstub/rebootstub$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).bin tmp_rebootstub_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/rebootstub.bin + @bin2s -a 8 -H rebootstub_bin.h tmp_rebootstub_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/rebootstub.bin | $(AS) -o rebootstub.bin.o + @rm -rf tmp_rebootstub_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + +$(TOPDIR)/rebootstub/rebootstub$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).bin: + @$(MAKE) __RECURSIVE__=0 --no-print-directory -C $(TOPDIR)/rebootstub $(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + +sc7fw.bin.o: sc7fw_bin.h + +sc7fw.bin.o sc7fw_bin.h: $(TOPDIR)/sc7fw/sc7fw$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).bin + @echo $(notdir $<) + @rm -rf tmp_sc7fw_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + @mkdir -p tmp_sc7fw_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + @cp $(TOPDIR)/sc7fw/sc7fw$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).bin tmp_sc7fw_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/sc7fw.bin + @bin2s -a 8 -H sc7fw_bin.h tmp_sc7fw_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER)/sc7fw.bin | $(AS) -o sc7fw.bin.o + @rm -rf tmp_sc7fw_$(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + +$(TOPDIR)/sc7fw/sc7fw$(ATMOSPHERE_BUILD_TARGET_BINARY_SUFFIX).bin: + @$(MAKE) __RECURSIVE__=0 --no-print-directory -C $(TOPDIR)/sc7fw $(ATMOSPHERE_BUILD_TARGET_IDENTIFIER) + %.elf: @echo linking $(notdir $@) $(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ @$(NM) -CSn $@ > $(notdir $*.lst) -$(OFILES_SRC) : $(HFILES_BIN) +$(OFILES_SRC) : rebootstub_bin.h sc7fw_bin.h -#--------------------------------------------------------------------------------- -# you need a rule like this for each extension you use as binary data -#--------------------------------------------------------------------------------- -%.bin.o %_bin.h: %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - @$(bin2o) -include $(DEPENDS) diff --git a/exosphere/program/rebootstub/Makefile b/exosphere/program/rebootstub/Makefile index 31439e834..aed4fa813 100644 --- a/exosphere/program/rebootstub/Makefile +++ b/exosphere/program/rebootstub/Makefile @@ -7,39 +7,23 @@ export ATMOSPHERE_CPU := arm7tdmi #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) 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))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +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 @@ -64,41 +48,73 @@ 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)) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) -.PHONY: $(BUILD) clean all +export TOPDIR := $(CURRENT_DIRECTORY) + +OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR)) #--------------------------------------------------------------------------------- -all: $(BUILD) check_libexo -$(BUILD): check_libexo +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): check_libexo_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + LIBEXOSPHERE_NAME=exosphere$(strip $2) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +check_libexo_$(strip $1): + @$$(MAKE) --no-print-directory -C $$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere $$(ATMOSPHERE_ARCH_NAME)-$(strip $1) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \ +)) + +$(ATMOSPHERE_BUILD_DIR)/%: @[ -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 +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) #--------------------------------------------------------------------------------- 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 +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBEXOSPHERE_NAME).a %.elf: @echo linking $(notdir $@) diff --git a/exosphere/program/rebootstub/source/rebootstub_power_off.cpp b/exosphere/program/rebootstub/source/rebootstub_power_off.cpp index 97d0e210f..985e4cc97 100644 --- a/exosphere/program/rebootstub/source/rebootstub_power_off.cpp +++ b/exosphere/program/rebootstub/source/rebootstub_power_off.cpp @@ -58,4 +58,6 @@ namespace ams::diag { __builtin_unreachable(); } + #include + } diff --git a/exosphere/program/sc7fw/Makefile b/exosphere/program/sc7fw/Makefile index 31439e834..aed4fa813 100644 --- a/exosphere/program/sc7fw/Makefile +++ b/exosphere/program/sc7fw/Makefile @@ -7,39 +7,23 @@ export ATMOSPHERE_CPU := arm7tdmi #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) 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))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +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 @@ -64,41 +48,73 @@ 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)) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) -.PHONY: $(BUILD) clean all +export TOPDIR := $(CURRENT_DIRECTORY) + +OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR)) #--------------------------------------------------------------------------------- -all: $(BUILD) check_libexo -$(BUILD): check_libexo +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): check_libexo_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + LIBEXOSPHERE_NAME=exosphere$(strip $2) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +check_libexo_$(strip $1): + @$$(MAKE) --no-print-directory -C $$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere $$(ATMOSPHERE_ARCH_NAME)-$(strip $1) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \ +)) + +$(ATMOSPHERE_BUILD_DIR)/%: @[ -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 +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) #--------------------------------------------------------------------------------- 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 +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBEXOSPHERE_NAME).a %.elf: @echo linking $(notdir $@) diff --git a/exosphere/program/sc7fw/source/sc7fw_main.cpp b/exosphere/program/sc7fw/source/sc7fw_main.cpp index 0a082586a..d06ddd3c1 100644 --- a/exosphere/program/sc7fw/source/sc7fw_main.cpp +++ b/exosphere/program/sc7fw/source/sc7fw_main.cpp @@ -90,7 +90,7 @@ namespace ams::sc7fw { reg::ReadWrite(PMC + APBDEV_PMC_DPD_ENABLE, PMC_REG_BITS_ENUM(DPD_ENABLE_ON, ENABLE)); /* Wait forever until we're asleep. */ - while (true) { /* ... */ } + AMS_INFINITE_LOOP(); } } @@ -102,7 +102,9 @@ namespace ams::sc7fw { NORETURN void ExceptionHandler() { /* Write enable to MAIN_RESET. */ reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE)); - while (true) { /* ... */ } + + /* Wait forever until we're reset. */ + AMS_INFINITE_LOOP(); } } @@ -113,4 +115,6 @@ namespace ams::diag { sc7fw::ExceptionHandler(); } + #include + } diff --git a/exosphere/program/source/boot/secmon_boot_setup.cpp b/exosphere/program/source/boot/secmon_boot_setup.cpp index 9452d70fd..d7cde0b25 100644 --- a/exosphere/program/source/boot/secmon_boot_setup.cpp +++ b/exosphere/program/source/boot/secmon_boot_setup.cpp @@ -85,6 +85,8 @@ namespace ams::secmon::boot { if constexpr (false) { /* TODO: Consider implementing this as a reference. */ } + + AMS_UNUSED(is_prod); } /* NOTE: These are just latest-master-kek encrypted with BEK. */ @@ -406,6 +408,7 @@ namespace ams::secmon::boot { constexpr void UnmapDramImpl(u64 *l1, u64 *l2, u64 *l3) { /* Unmap the L1 entry corresponding to to the Dram entries. */ + AMS_UNUSED(l2, l3); InvalidateL1Entries(l1, MemoryRegionDram.GetAddress(), MemoryRegionDram.GetSize()); } diff --git a/exosphere/program/source/secmon_error.cpp b/exosphere/program/source/secmon_error.cpp index 4c7d7752c..d025d5675 100644 --- a/exosphere/program/source/secmon_error.cpp +++ b/exosphere/program/source/secmon_error.cpp @@ -71,6 +71,8 @@ namespace ams::diag { secmon::ErrorReboot(); } + #include + } namespace ams::secmon { diff --git a/exosphere/program/source/secmon_map.cpp b/exosphere/program/source/secmon_map.cpp index 96ccf84ed..984b6782c 100644 --- a/exosphere/program/source/secmon_map.cpp +++ b/exosphere/program/source/secmon_map.cpp @@ -41,6 +41,7 @@ namespace ams::secmon { constexpr void UnmapBootCodeImpl(u64 *l1, u64 *l2, u64 *l3, uintptr_t boot_code, size_t boot_code_size) { /* Unmap the L3 entries corresponding to the boot code. */ + AMS_UNUSED(l1, l2); InvalidateL3Entries(l3, boot_code, boot_code_size); } @@ -95,10 +96,6 @@ namespace ams::secmon { util::ClearMemory(reinterpret_cast(address + size / 2), size / 2); } - bool IsPhysicalMemoryAddress(uintptr_t address) { - return (address - MemoryRegionDram.GetAddress()) < GetPhysicalMemorySize(); - } - } void ClearBootCodeHigh() { @@ -129,6 +126,10 @@ namespace ams::secmon { } } + bool IsPhysicalMemoryAddress(uintptr_t address) { + return (address - MemoryRegionDram.GetAddress()) < GetPhysicalMemorySize(); + } + void UnmapTzram() { /* Get the tables. */ u64 * const l1 = MemoryRegionVirtualTzramL1PageTable.GetPointer(); @@ -195,6 +196,11 @@ namespace ams::secmon { return 0; } + /* Validate that the page isn't a secure monitor debug page. */ + if (MemoryRegionPhysicalIramSecureMonitorDebug.Contains(address, 1)) { + return 0; + } + /* Validate that the page is aligned. */ if (!util::IsAligned(address, 4_KB)) { return 0; diff --git a/exosphere/program/source/secmon_map.hpp b/exosphere/program/source/secmon_map.hpp index a6bdbc405..7cc0cbc56 100644 --- a/exosphere/program/source/secmon_map.hpp +++ b/exosphere/program/source/secmon_map.hpp @@ -18,6 +18,7 @@ namespace ams::secmon { + bool IsPhysicalMemoryAddress(uintptr_t address); size_t GetPhysicalMemorySize(); void UnmapTzram(); diff --git a/exosphere/program/source/secmon_setup.cpp b/exosphere/program/source/secmon_setup.cpp index b5580c1b2..5e33002b8 100644 --- a/exosphere/program/source/secmon_setup.cpp +++ b/exosphere/program/source/secmon_setup.cpp @@ -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. */ diff --git a/exosphere/program/source/smc/secmon_mc_access_table_data.inc b/exosphere/program/source/smc/secmon_mc_access_table_data.inc index 5b97b8f3f..9d75169d7 100644 --- a/exosphere/program/source/smc/secmon_mc_access_table_data.inc +++ b/exosphere/program/source/smc/secmon_mc_access_table_data.inc @@ -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 */ diff --git a/exosphere/program/source/smc/secmon_smc_handler.cpp b/exosphere/program/source/smc/secmon_smc_handler.cpp index 3c790e3a7..38d950ec8 100644 --- a/exosphere/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere/program/source/smc/secmon_smc_handler.cpp @@ -141,6 +141,9 @@ namespace ams::secmon::smc { { 0xC3000006, Restriction_Normal, SmcShowError }, { 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion }, { 0xC3000008, Restriction_Normal, SmcReadWriteRegister }, + + /* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */ + { 0xC3000409, Restriction_Normal, SmcSetConfig }, }; constinit HandlerInfo g_ams_handlers[] = { diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index d06b8a509..857eeccc4 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -15,6 +15,7 @@ */ #include #include "../secmon_error.hpp" +#include "../secmon_map.hpp" #include "../secmon_misc.hpp" #include "../secmon_page_mapper.hpp" #include "../secmon_user_power_management.hpp" @@ -157,6 +158,8 @@ namespace ams::secmon::smc { return value.value; } + constinit u64 g_payload_address = 0; + SmcResult GetConfig(SmcArguments &args, bool kern) { switch (static_cast(args.r[1])) { case ConfigItem::DisableProgramVerification: @@ -267,6 +270,14 @@ namespace ams::secmon::smc { /* NOTE: This may return values other than 1 in the future. */ args.r[1] = (GetEmummcConfiguration().IsEmummcActive() ? 1 : 0); break; + case ConfigItem::ExospherePayloadAddress: + /* Gets the physical address of the reboot payload buffer, if one exists. */ + if (g_payload_address != 0) { + args.r[1] = g_payload_address; + } else { + return SmcResult::NotInitialized; + } + break; default: return SmcResult::InvalidArgument; } @@ -309,6 +320,17 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } break; + case ConfigItem::ExospherePayloadAddress: + if (g_payload_address == 0) { + if (secmon::IsPhysicalMemoryAddress(args.r[2])) { + g_payload_address = args.r[2]; + } else { + return SmcResult::InvalidArgument; + } + } else { + return SmcResult::Busy; + } + break; default: return SmcResult::InvalidArgument; } diff --git a/exosphere/program/source/smc/secmon_smc_info.hpp b/exosphere/program/source/smc/secmon_smc_info.hpp index 55846631e..7f33cbdba 100644 --- a/exosphere/program/source/smc/secmon_smc_info.hpp +++ b/exosphere/program/source/smc/secmon_smc_info.hpp @@ -48,6 +48,7 @@ namespace ams::secmon::smc { ExosphereBlankProdInfo = 65005, ExosphereAllowCalWrites = 65006, ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, }; SmcResult SmcGetConfigUser(SmcArguments &args); diff --git a/exosphere/program/source/smc/secmon_smc_memory_access.cpp b/exosphere/program/source/smc/secmon_smc_memory_access.cpp index dba249ccf..2f2c1ca2c 100644 --- a/exosphere/program/source/smc/secmon_smc_memory_access.cpp +++ b/exosphere/program/source/smc/secmon_smc_memory_access.cpp @@ -69,6 +69,7 @@ namespace ams::secmon::smc { SmcResult SmcWriteAddress(SmcArguments &args) { /* NOTE: This smc was deprecated in Atmosphère 0.13.0. */ + AMS_UNUSED(args); return SmcResult::NotImplemented; } diff --git a/exosphere/program/source/smc/secmon_smc_power_management.cpp b/exosphere/program/source/smc/secmon_smc_power_management.cpp index 1e8a90389..f83b76b0b 100644 --- a/exosphere/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere/program/source/smc/secmon_smc_power_management.cpp @@ -492,6 +492,8 @@ namespace ams::secmon::smc { } SmcResult SmcPowerOffCpu(SmcArguments &args) { + AMS_UNUSED(args); + /* Get the current core id. */ const auto core_id = hw::GetCurrentCoreId(); diff --git a/exosphere/program/source/smc/secmon_smc_register_access.cpp b/exosphere/program/source/smc/secmon_smc_register_access.cpp index cdf834353..b07b6d13d 100644 --- a/exosphere/program/source/smc/secmon_smc_register_access.cpp +++ b/exosphere/program/source/smc/secmon_smc_register_access.cpp @@ -155,10 +155,27 @@ namespace ams::secmon::smc { /* Find the access table. */ const AccessTableEntry * const entry = GetAccessTableEntry(address); - /* If we have a table, perform the write. */ + /* Translate our entry into an address to access. */ + uintptr_t virtual_address = 0; if (entry != nullptr) { /* Get the address to read or write. */ - const uintptr_t virtual_address = entry->virtual_address + (address - entry->address); + virtual_address = entry->virtual_address + (address - entry->address); + } else { + /* For no clearly discernable reason, SmcReadWriteRegister returns success despite not doing the read/write */ + /* when accessing the SMMU controls for the BPMP and for APB-DMA. */ + /* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */ + /* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */ + constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress(); + SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument); + + /* For backwards compatibility, we'll allow access to these devices on 1.0.0. */ + if (GetTargetFirmware() < TargetFirmware_2_0_0) { + virtual_address = MemoryRegionVirtualDeviceMemoryController.GetAddress() + (address - MC); + } + } + + /* Perform the read or write, if we should. */ + if (virtual_address != 0) { u32 out = 0; if (mask != ~static_cast(0)) { @@ -169,13 +186,6 @@ namespace ams::secmon::smc { } args.r[1] = out; - } else { - /* For no clearly discernable reason, SmcReadWriteRegister returns success despite not doing the read/write */ - /* when accessing the SMMU controls for the BPMP and for APB-DMA. */ - /* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */ - /* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */ - constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress(); - SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument); } return SmcResult::Success; diff --git a/exosphere/program/source/smc/secmon_smc_rsa.cpp b/exosphere/program/source/smc/secmon_smc_rsa.cpp index 01104a503..460dccf3c 100644 --- a/exosphere/program/source/smc/secmon_smc_rsa.cpp +++ b/exosphere/program/source/smc/secmon_smc_rsa.cpp @@ -75,6 +75,7 @@ namespace ams::secmon::smc { u8 msg[se::RsaSize]; public: void Set(const void *m, size_t m_size) { + AMS_UNUSED(m_size); std::memcpy(this->msg, m, sizeof(this->msg)); } diff --git a/exosphere/program/split_program.py b/exosphere/program/split_program.py index e8806c16b..158aa6c69 100644 --- a/exosphere/program/split_program.py +++ b/exosphere/program/split_program.py @@ -19,17 +19,17 @@ def split_binary(data): boot_code = data[BOOT_CODE_START - START:BOOT_CODE_END - BOOT_CODE_START] program = data[PROGRAM_START - START:] - return [('boot_code.lz4', lz4_compress(boot_code)), ('program.lz4', lz4_compress(program))] + return [('boot_code%s.lz4', lz4_compress(boot_code)), ('program%s.lz4', lz4_compress(program))] def main(argc, argv): - if argc != 3: - print('Usage: %s in outdir' % argv[0]) + if argc != 4: + print('Usage: %s in suffix outdir' % argv[0]) return 1 with open(argv[1], 'rb') as f: data = f.read() assert len(data) >= 0x40 for (fn, fdata) in split_binary(data): - with open('%s/%s' % (argv[2], fn), 'wb') as f: + with open('%s/%s' % (argv[3], fn % argv[2]), 'wb') as f: f.write(fdata) return 0 diff --git a/exosphere/sdmmc_test/Makefile b/exosphere/sdmmc_test/Makefile new file mode 100644 index 000000000..4bfed891e --- /dev/null +++ b/exosphere/sdmmc_test/Makefile @@ -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 +#--------------------------------------------------------------------------------------- diff --git a/exosphere/sdmmc_test/sdmmc_test.ld b/exosphere/sdmmc_test/sdmmc_test.ld new file mode 100644 index 000000000..181d33330 --- /dev/null +++ b/exosphere/sdmmc_test/sdmmc_test.ld @@ -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) } +} \ No newline at end of file diff --git a/exosphere/sdmmc_test/sdmmc_test.specs b/exosphere/sdmmc_test/sdmmc_test.specs new file mode 100644 index 000000000..72d846e06 --- /dev/null +++ b/exosphere/sdmmc_test/sdmmc_test.specs @@ -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 diff --git a/exosphere/sdmmc_test/source/sdmmc_test_main.cpp b/exosphere/sdmmc_test/source/sdmmc_test_main.cpp new file mode 100644 index 000000000..c7da95715 --- /dev/null +++ b/exosphere/sdmmc_test/source/sdmmc_test_main.cpp @@ -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 . + */ +#include + +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(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(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(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(speed_mode); + DEBUG[3] = static_cast(bus_width); + + /* Perform a reboot. */ + PmcMainReboot(); + } + + NORETURN void ExceptionHandler() { + PmcMainReboot(); + } + +} + +namespace ams::diag { + + void AbortImpl() { + sdmmc_test::ExceptionHandler(); + } + +} diff --git a/exosphere/sdmmc_test/source/sdmmc_test_start.s b/exosphere/sdmmc_test/source/sdmmc_test_start.s new file mode 100644 index 000000000..69c1204f7 --- /dev/null +++ b/exosphere/sdmmc_test/source/sdmmc_test_start.s @@ -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 . + */ + +.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 \ No newline at end of file diff --git a/exosphere/warmboot/Makefile b/exosphere/warmboot/Makefile index 2288e2717..fe070ac3e 100644 --- a/exosphere/warmboot/Makefile +++ b/exosphere/warmboot/Makefile @@ -7,39 +7,23 @@ export ATMOSPHERE_CPU := arm7tdmi #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) 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))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +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 @@ -64,41 +48,73 @@ 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)) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) -.PHONY: $(BUILD) clean all +export TOPDIR := $(CURRENT_DIRECTORY) + +OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR)) #--------------------------------------------------------------------------------- -all: $(BUILD) check_libexo -$(BUILD): check_libexo +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): check_libexo_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + LIBEXOSPHERE_NAME=exosphere$(strip $2) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +check_libexo_$(strip $1): + @$$(MAKE) --no-print-directory -C $$(ATMOSPHERE_LIBRARIES_DIR)/libexosphere $$(ATMOSPHERE_ARCH_NAME)-$(strip $1) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \ +)) + +$(ATMOSPHERE_BUILD_DIR)/%: @[ -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 +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) #--------------------------------------------------------------------------------- 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 +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libexosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBEXOSPHERE_NAME).a %.elf: @echo linking $(notdir $@) diff --git a/exosphere/warmboot/source/warmboot_main.cpp b/exosphere/warmboot/source/warmboot_main.cpp index 5042fa8ad..8da3af85f 100644 --- a/exosphere/warmboot/source/warmboot_main.cpp +++ b/exosphere/warmboot/source/warmboot_main.cpp @@ -90,7 +90,9 @@ namespace ams::warmboot { NORETURN void ExceptionHandler() { /* Write enable to MAIN_RESET. */ reg::Write(PMC + APBDEV_PMC_CNTRL, PMC_REG_BITS_ENUM(CNTRL_MAIN_RESET, ENABLE)); - while (true) { /* ... */ } + + /* Wait forever until we're reset. */ + AMS_INFINITE_LOOP(); } } diff --git a/exosphere/warmboot/source/warmboot_start.s b/exosphere/warmboot/source/warmboot_start.s index 18070e04a..dc6a56c2e 100644 --- a/exosphere/warmboot/source/warmboot_start.s +++ b/exosphere/warmboot/source/warmboot_start.s @@ -30,7 +30,7 @@ _ZN3ams8warmboot5StartEv: /* Invoke main. */ ldr r0, =_metadata - bl _ZN3ams8warmboot4MainEPKNS0_8MetadataE + b _ZN3ams8warmboot4MainEPKNS0_8MetadataE /* Infinite loop. */ 1: b 1b \ No newline at end of file diff --git a/fusee/fusee-primary/src/panic.c b/fusee/fusee-primary/src/panic.c index bd0520efb..e4cd19e06 100644 --- a/fusee/fusee-primary/src/panic.c +++ b/fusee/fusee-primary/src/panic.c @@ -24,6 +24,9 @@ #include "lib/log.h" #include "display/video_fb.h" +#define PROGRAM_ID_AMS_MITM 0x010041544D530000ull +#define PROGRAM_ID_BOOT 0x0100000000000005ull + static uint32_t g_panic_code = 0; static const char *get_error_desc_str(uint32_t error_desc) { @@ -42,6 +45,8 @@ static const char *get_error_desc_str(uint32_t error_desc) { return "SError"; case 0x301: return "Bad SVC"; + case 0xF00: + return "Kernel Panic"; case 0xFFD: return "Stack overflow"; case 0xFFE: @@ -51,6 +56,61 @@ static const char *get_error_desc_str(uint32_t error_desc) { } } +static void _try_suggest_fix(const atmosphere_fatal_error_ctx *ctx) { + /* Try to recognize certain errors automatically, and suggest fixes for them. */ + const char *suggestion = NULL; + + if (ctx->error_desc == 0xFFE) { + if (ctx->program_id == PROGRAM_ID_AMS_MITM) { + /* When a user has archive bits set improperly, attempting to create an automatic backup will fail */ + /* to create the file path with error 0x202 (fs::ResultPathNotFound()) */ + if (ctx->gprs[0] == 0x202) { + /* When the archive bit error is occurring, it manifests as failure to create automatic backup. */ + /* Thus, we can search the stack for the automatic backups path. */ + const char * const automatic_backups_prefix = "automatic_backups/X" /* ..... */; + const int prefix_len = strlen(automatic_backups_prefix); + + for (size_t i = 0; i + prefix_len < ctx->stack_dump_size; ++i) { + if (memcmp(&ctx->stack_dump[i], automatic_backups_prefix, prefix_len) == 0) { + suggestion = "The atmosphere directory may improperly have archive\n" + "bits set. Please try running an archive bit fixer tool\n" + "(for example, the one in Hekate).\n"; + break; + } + } + } else if (ctx->gprs[0] == 0x249A02) { /* fs::ResultResultExFatUnavailable() */ + /* When a user installs non-exFAT firm but has an exFAT formatted SD card, this error will */ + /* be returned on attempt to access the SD card. */ + suggestion = "Your console has non-exFAT firmware installed, but your SD card\n" + "is formatted as exFAT. Format your SD card as FAT32, or manually\n" + "flash exFAT firmware to package2.\n"; + } + } else if (ctx->program_id == PROGRAM_ID_BOOT) { + /* 9.x -> 10.x updated the API for SvcQueryIoMapping. */ + /* This can cause the kernel to reject incorrect-ABI calls by boot when a partial update is applied */ + /* (older kernel in package2, for some reason). */ + for (size_t i = 0; i < 8; ++i) { + if (ctx->gprs[i] == 0xF201) { + suggestion = "A partial update may have been improperly performed.\n" + "To fix, try manually flashing latest package2 to MMC.\n" + "\n" + "For help doing this, seek support in the ReSwitched or\n" + "Nintendo Homebrew discord servers.\n"; + break; + } + } + } + } else if (ctx->error_desc == 0xF00) { /* Kernel Panic */ + suggestion = "Please contact SciresM#0524 on Discord, or create an issue\n" + "on the Atmosphere GitHub issue tracker. Thank you very much\n" + "for helping to test mesosphere.\n"; + } + + if (suggestion != NULL) { + print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\n%s", suggestion); + } +} + static void _check_and_display_atmosphere_fatal_error(void) { /* Check for valid magic. */ if (ATMOSPHERE_FATAL_ERROR_CONTEXT->magic != ATMOSPHERE_REBOOT_TO_FATAL_MAGIC && @@ -84,7 +144,7 @@ static void _check_and_display_atmosphere_fatal_error(void) { ATMOSPHERE_FATAL_ERROR_CONTEXT->magic = 0xCCCCCCCC; print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "A fatal error occurred when running Atmosph\xe8re.\n"); - print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "Title ID: %016llx\n", ctx.title_id); + print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "Program ID: %016llx\n", ctx.program_id); print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "Error Desc: %s (0x%x)\n", get_error_desc_str(ctx.error_desc), ctx.error_desc); /* Save context to the SD card. */ @@ -99,6 +159,9 @@ static void _check_and_display_atmosphere_fatal_error(void) { } } + /* Try to print a fix suggestion via automatic error detection. */ + _try_suggest_fix(&ctx); + /* Display error. */ print(SCREEN_LOG_LEVEL_ERROR | SCREEN_LOG_LEVEL_NO_PREFIX, "\nPress POWER to reboot\n"); } diff --git a/fusee/fusee-primary/src/panic.h b/fusee/fusee-primary/src/panic.h index 86bf0d73f..a7db236d4 100644 --- a/fusee/fusee-primary/src/panic.h +++ b/fusee/fusee-primary/src/panic.h @@ -36,7 +36,7 @@ typedef struct { uint32_t magic; uint32_t error_desc; - uint64_t title_id; + uint64_t program_id; union { uint64_t gprs[32]; struct { diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index 51a408be4..62520cc2c 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -89,7 +89,7 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ $(AMS)/exosphere $(AMS)/exosphere/warmboot $(AMS)/exosphere/program/rebootstub \ $(AMS)/thermosphere $(AMS)/fusee/fusee-primary $(AMS)/sept/sept-primary \ - $(AMS)/sept/sept-secondary $(AMS)/emummc $(AMS)/mesosphere/kernel_ldr $(KIPDIRS) + $(AMS)/sept/sept-secondary $(AMS)/emummc $(AMS)/mesosphere $(AMS)/mesosphere/kernel_ldr $(KIPDIRS) export DEPSDIR := $(CURDIR)/$(BUILD) @@ -100,7 +100,7 @@ KIPFILES := loader.kip ncm.kip pm.kip sm.kip ams_mitm.kip spl.kip boot.kip BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \ exosphere.bin warmboot.bin rebootstub.bin thermosphere.bin splash_screen.bmp \ sept-primary.bin sept-secondary_00.enc sept-secondary_01.enc emummc.kip \ - sept-secondary_dev_00.enc sept-secondary_dev_01.enc kernel_ldr.bin $(KIPFILES) + sept-secondary_dev_00.enc sept-secondary_dev_01.enc mesosphere.bin kernel_ldr.bin $(KIPFILES) #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C @@ -186,11 +186,15 @@ DEPENDS := $(OFILES:.o=.d) #--------------------------------------------------------------------------------- all : $(OUTPUT).bin -$(OUTPUT).bin : $(OUTPUT).elf +$(OUTPUT).bin : $(OUTPUT)-experimental.bin + @python $(TOPDIR)/fusee_make_standard.py $(OUTPUT)-experimental.bin $(OUTPUT).bin + @echo built ... $(notdir $@) + +$(OUTPUT)-experimental.bin : $(OUTPUT)-experimental.elf $(OBJCOPY) -S -O binary $< $@ @echo built ... $(notdir $@) -$(OUTPUT).elf : $(OFILES) +$(OUTPUT)-experimental.elf : $(OFILES) %.elf: $(OFILES) @echo linking $(notdir $@) diff --git a/fusee/fusee-secondary/fusee_make_standard.py b/fusee/fusee-secondary/fusee_make_standard.py new file mode 100644 index 000000000..f921d76bb --- /dev/null +++ b/fusee/fusee-secondary/fusee_make_standard.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +import sys, os +from struct import pack as pk, unpack as up + +def make_standard(exp): + std = exp[:] + _, metadata_offset, is_exp = up('. */ - + #ifndef FUSEE_CAR_H #define FUSEE_CAR_H @@ -37,23 +37,27 @@ /* Clock and reset devices. */ typedef enum { - CARDEVICE_UARTA = ((0 << 5) | 0x6), - CARDEVICE_UARTB = ((0 << 5) | 0x7), - CARDEVICE_UARTC = ((1 << 5) | 0x17), - CARDEVICE_I2C1 = ((0 << 5) | 0xC), - CARDEVICE_I2C5 = ((1 << 5) | 0xF), - CARDEVICE_TZRAM = ((3 << 5) | 0x1E), - CARDEVICE_SE = ((3 << 5) | 0x1F), - CARDEVICE_HOST1X = ((0 << 5) | 0x1C), - CARDEVICE_TSEC = ((2 << 5) | 0x13), - CARDEVICE_SOR_SAFE = ((6 << 5) | 0x1E), - CARDEVICE_SOR0 = ((5 << 5) | 0x16), - CARDEVICE_SOR1 = ((5 << 5) | 0x17), - CARDEVICE_KFUSE = ((1 << 5) | 0x8), - CARDEVICE_CL_DVFS = ((4 << 5) | 0x1B), + CARDEVICE_BPMP = ((0 << 5) | 0x1), + CARDEVICE_UARTA = ((0 << 5) | 0x6), + CARDEVICE_UARTB = ((0 << 5) | 0x7), + CARDEVICE_I2C1 = ((0 << 5) | 0xC), + CARDEVICE_USBD = ((0 << 5) | 0x16), + CARDEVICE_HOST1X = ((0 << 5) | 0x1C), + CARDEVICE_AHBDMA = ((1 << 5) | 0x1), + CARDEVICE_APBDMA = ((1 << 5) | 0x2), + CARDEVICE_KFUSE = ((1 << 5) | 0x8), + CARDEVICE_I2C5 = ((1 << 5) | 0xF), + CARDEVICE_UARTC = ((1 << 5) | 0x17), + CARDEVICE_USB2 = ((1 << 5) | 0x1A), CARDEVICE_CORESIGHT = ((2 << 5) | 0x9), - CARDEVICE_ACTMON = ((3 << 5) | 0x17), - CARDEVICE_BPMP = ((0 << 5) | 0x1) + CARDEVICE_TSEC = ((2 << 5) | 0x13), + CARDEVICE_ACTMON = ((3 << 5) | 0x17), + CARDEVICE_TZRAM = ((3 << 5) | 0x1E), + CARDEVICE_SE = ((3 << 5) | 0x1F), + CARDEVICE_CL_DVFS = ((4 << 5) | 0x1B), + CARDEVICE_SOR0 = ((5 << 5) | 0x16), + CARDEVICE_SOR1 = ((5 << 5) | 0x17), + CARDEVICE_SOR_SAFE = ((6 << 5) | 0x1E), } CarDevice; /* Clock/Reset Controller (CLK_RST_CONTROLLER_) regs */ @@ -97,31 +101,31 @@ typedef struct { uint32_t pllc_out; uint32_t pllc_misc0; uint32_t pllc_misc1; - + /* PLLM 0x90-0x9c */ uint32_t pllm_base; uint32_t pllm_out; uint32_t pllm_misc1; uint32_t pllm_misc2; - + /* PLLP 0xa0-0xac */ uint32_t pllp_base; uint32_t pllp_outa; uint32_t pllp_outb; uint32_t pllp_misc; - + /* PLLA 0xb0-0xbc */ uint32_t plla_base; uint32_t plla_out; uint32_t plla_misc0; uint32_t plla_misc1; - + /* PLLU 0xc0-0xcc */ uint32_t pllu_base; uint32_t pllu_out; uint32_t pllu_misc1; uint32_t pllu_misc2; - + /* PLLD 0xd0-0xdc */ uint32_t plld_base; uint32_t plld_out; @@ -131,13 +135,13 @@ typedef struct { /* PLLX 0xe0-0xe4 */ uint32_t pllx_base; uint32_t pllx_misc; - + /* PLLE 0xe8-0xf4 */ uint32_t plle_base; uint32_t plle_misc; uint32_t plle_ss_cntl1; uint32_t plle_ss_cntl2; - + uint32_t lvl2_clk_gate_ovra; /* _LVL2_CLK_GATE_OVRA_0, 0xf8 */ uint32_t lvl2_clk_gate_ovrb; /* _LVL2_CLK_GATE_OVRB_0, 0xfc */ @@ -188,7 +192,7 @@ typedef struct { uint32_t _0x1e0[5]; uint32_t clk_source_tsec; /* _CLK_SOURCE_TSEC_0, 0x1f4 */ uint32_t _0x1f8; - + uint32_t clk_spare2; /* _CLK_SPARE2_0, 0x1fc */ uint32_t _0x200[32]; @@ -257,7 +261,7 @@ typedef struct { uint32_t lvl2_clk_gate_ovrc; /* _LVL2_CLK_GATE_OVRC, 0x3a0 */ uint32_t lvl2_clk_gate_ovrd; /* _LVL2_CLK_GATE_OVRD, 0x3a4 */ uint32_t _0x3a8[2]; - + uint32_t _0x3b0; uint32_t clk_source_mselect; /* _CLK_SOURCE_MSELECT_0, 0x3b4 */ uint32_t clk_source_tsensor; /* _CLK_SOURCE_TSENSOR_0, 0x3b8 */ @@ -371,13 +375,13 @@ typedef struct { uint32_t spare_reg0; /* _SPARE_REG0_0, 0x55c */ uint32_t audio_sync_clk_dmic1; /* _AUDIO_SYNC_CLK_DMIC1_0, 0x560 */ uint32_t audio_sync_clk_dmic2; /* _AUDIO_SYNC_CLK_DMIC2_0, 0x564 */ - + uint32_t _0x568[2]; uint32_t plld2_ss_cfg; /* _PLLD2_SS_CFG, 0x570 */ uint32_t plld2_ss_ctrl1; /* _PLLD2_SS_CTRL1_0, 0x574 */ uint32_t plld2_ss_ctrl2; /* _PLLD2_SS_CTRL2_0, 0x578 */ uint32_t _0x57c[5]; - + uint32_t plldp_base; /* _PLLDP_BASE, 0x590*/ uint32_t plldp_misc; /* _PLLDP_MISC, 0x594 */ uint32_t plldp_ss_cfg; /* _PLLDP_SS_CFG, 0x598 */ @@ -399,7 +403,7 @@ typedef struct { uint32_t pllx_misc4; /* _PLLX_MISC_4_0, 0x5f0 */ uint32_t pllx_misc5; /* _PLLX_MISC_5_0, 0x5f4 */ uint32_t _0x5f8[2]; - + uint32_t clk_source_xusb_core_host; /* _CLK_SOURCE_XUSB_CORE_HOST_0, 0x600 */ uint32_t clk_source_xusb_falcon; /* _CLK_SOURCE_XUSB_FALCON_0, 0x604 */ uint32_t clk_source_xusb_fs; /* _CLK_SOURCE_XUSB_FS_0, 0x608 */ @@ -428,7 +432,7 @@ typedef struct { uint32_t clk_source_uart_fst_mipi_cal; /* _CLK_SOURCE_UART_FST_MIPI_CAL_0, 0x66c */ uint32_t _0x670[2]; uint32_t clk_source_vic; /* _CLK_SOURCE_VIC_0, 0x678 */ - + uint32_t pllp_outc; /* _PLLP_OUTC_0, 0x67c */ uint32_t pllp_misc1; /* _PLLP_MISC1_0, 0x680 */ uint32_t _0x684[2]; @@ -439,14 +443,14 @@ typedef struct { uint32_t clk_source_nvdec; /* _CLK_SOURCE_NVDEC_0, 0x698 */ uint32_t clk_source_nvjpg; /* _CLK_SOURCE_NVJPG_0, 0x69c */ uint32_t clk_source_nvenc; /* _CLK_SOURCE_NVENC_0, 0x6a0 */ - + uint32_t plla1_base; /* _PLLA1_BASE_0, 0x6a4 */ uint32_t plla1_misc0; /* _PLLA1_MISC_0_0, 0x6a8 */ uint32_t plla1_misc1; /* _PLLA1_MISC_1_0, 0x6ac */ uint32_t plla1_misc2; /* _PLLA1_MISC_2_0, 0x6b0 */ uint32_t plla1_misc3; /* _PLLA1_MISC_3_0, 0x6b4 */ uint32_t audio_sync_clk_dmic3; /* _AUDIO_SYNC_CLK_DMIC3_0, 0x6b8 */ - + uint32_t clk_source_dmic3; /* _CLK_SOURCE_DMIC3_0, 0x6bc */ uint32_t clk_source_ape; /* _CLK_SOURCE_APE_0, 0x6c0 */ uint32_t clk_source_qspi; /* _CLK_SOURCE_QSPI_0, 0x6c4 */ @@ -455,11 +459,11 @@ typedef struct { uint32_t clk_source_pex_sata_usb_rx_byp; /* _CLK_SOURCE_PEX_SATA_USB_RX_BYP_0, 0x6d0 */ uint32_t clk_source_maud; /* _CLK_SOURCE_MAUD_0, 0x6d4 */ uint32_t clk_source_tsecb; /* _CLK_SOURCE_TSECB_0, 0x6d8 */ - + uint32_t clk_cpug_misc1; /* _CLK_CPUG_MISC1_0, 0x6dc */ uint32_t aclk_burst_policy; /* _ACLK_BURST_POLICY_0, 0x6e0 */ uint32_t super_aclk_divider; /* _SUPER_ACLK_DIVIDER_0, 0x6e4 */ - + uint32_t nvenc_super_clk_divider; /* _NVENC_SUPER_CLK_DIVIDER_0, 0x6e8 */ uint32_t vi_super_clk_divider; /* _VI_SUPER_CLK_DIVIDER_0, 0x6ec */ uint32_t vic_super_clk_divider; /* _VIC_SUPER_CLK_DIVIDER_0, 0x6f0 */ @@ -470,7 +474,7 @@ typedef struct { uint32_t se_super_clk_divider; /* _SE_SUPER_CLK_DIVIDER_0, 0x704 */ uint32_t tsec_super_clk_divider; /* _TSEC_SUPER_CLK_DIVIDER_0, 0x708 */ uint32_t tsecb_super_clk_divider; /* _TSECB_SUPER_CLK_DIVIDER_0, 0x70c */ - + uint32_t clk_source_uartape; /* _CLK_SOURCE_UARTAPE_0, 0x710 */ uint32_t clk_cpug_misc2; /* _CLK_CPUG_MISC2_0, 0x714 */ uint32_t clk_source_dbgapb; /* _CLK_SOURCE_DBGAPB_0, 0x718 */ @@ -484,7 +488,7 @@ typedef struct { uint32_t sdmmc4_pllc4_out0_shaper_ctrl; /* _SDMMC4_PLLC4_OUT0_SHAPER_CTRL_0, 0x738 */ uint32_t sdmmc4_pllc4_out1_shaper_ctrl; /* _SDMMC4_PLLC4_OUT1_SHAPER_CTRL_0, 0x73c */ uint32_t sdmmc4_pllc4_out2_shaper_ctrl; /* _SDMMC4_PLLC4_OUT2_SHAPER_CTRL_0, 0x740 */ - uint32_t sdmmc4_div_clk_shaper_ctrl; /* _SDMMC4_DIV_CLK_SHAPER_CTRL_0, 0x744 */ + uint32_t sdmmc4_div_clk_shaper_ctrl; /* _SDMMC4_DIV_CLK_SHAPER_CTRL_0, 0x744 */ } tegra_car_t; static inline volatile tegra_car_t *car_get_regs(void) { diff --git a/fusee/fusee-secondary/src/emummc_cfg.h b/fusee/fusee-secondary/src/emummc_cfg.h index 7f30ecc21..23d611439 100644 --- a/fusee/fusee-secondary/src/emummc_cfg.h +++ b/fusee/fusee-secondary/src/emummc_cfg.h @@ -85,6 +85,9 @@ typedef enum { FS_VER_10_0_0, FS_VER_10_0_0_EXFAT, + FS_VER_10_2_0, + FS_VER_10_2_0_EXFAT, + FS_VER_MAX, } emummc_fs_ver_t; diff --git a/fusee/fusee-secondary/src/ips.c b/fusee/fusee-secondary/src/ips.c index a07438a65..18c0cac6a 100644 --- a/fusee/fusee-secondary/src/ips.c +++ b/fusee/fusee-secondary/src/ips.c @@ -420,6 +420,9 @@ static const uint8_t g_fs_hashes[FS_VER_MAX][0x8] = { "\x3E\xEB\xD9\xB7\xBC\xD1\xB5\xE0", /* FS_VER_10_0_0 */ "\x81\x7E\xA2\xB0\xB7\x02\xC1\xF3", /* FS_VER_10_0_0_EXFAT */ + + "\xA9\x52\xB6\x57\xAD\xF9\xC2\xBA", /* FS_VER_10_2_0 */ + "\x16\x0D\x3E\x10\x4E\xAD\x61\x76", /* FS_VER_10_2_0_EXFAT */ }; kip1_header_t *apply_kip_ips_patches(kip1_header_t *kip, size_t kip_size, emummc_fs_ver_t *out_fs_ver) { diff --git a/fusee/fusee-secondary/src/kernel_patches.c b/fusee/fusee-secondary/src/kernel_patches.c index fc2451052..f61bf7e95 100644 --- a/fusee/fusee-secondary/src/kernel_patches.c +++ b/fusee/fusee-secondary/src/kernel_patches.c @@ -963,11 +963,6 @@ void package2_patch_kernel(void *_kernel, size_t *kernel_size, bool is_sd_kernel } if (kernel_info == NULL && is_sd_kernel) { - /* If the kernel is mesosphere, patch it. */ - if (*(volatile uint32_t *)((uintptr_t)_kernel + 4) == 0x3053534D) { - *out_ini1 = (void *)((uintptr_t)_kernel + *(volatile uint32_t *)((uintptr_t)_kernel + 8)); - *(volatile uint64_t *)((uintptr_t)_kernel + 8) = (uint64_t)*kernel_size; - } return; } diff --git a/fusee/fusee-secondary/src/nxboot.c b/fusee/fusee-secondary/src/nxboot.c index 7a8519208..907a95115 100644 --- a/fusee/fusee-secondary/src/nxboot.c +++ b/fusee/fusee-secondary/src/nxboot.c @@ -24,6 +24,7 @@ #include "nxboot.h" #include "nxfs.h" #include "bct.h" +#include "car.h" #include "di.h" #include "mc.h" #include "se.h" @@ -54,6 +55,7 @@ #define u8 uint8_t #define u32 uint32_t #include "exosphere_bin.h" +#include "mesosphere_bin.h" #include "sept_secondary_00_enc.h" #include "sept_secondary_01_enc.h" #include "sept_secondary_dev_00_enc.h" @@ -65,6 +67,8 @@ extern const uint8_t warmboot_bin[]; +extern int fusee_is_experimental(void); + static const uint8_t retail_pkc_modulus[0x100] = { 0xF7, 0x86, 0x47, 0xAB, 0x71, 0x89, 0x81, 0xB5, 0xCF, 0x0C, 0xB0, 0xE8, 0x48, 0xA7, 0xFD, 0xAD, 0xCB, 0x4E, 0x4A, 0x52, 0x0B, 0x1A, 0x8E, 0xDE, 0x41, 0x87, 0x6F, 0xB7, 0x31, 0x05, 0x5F, 0xAA, @@ -208,11 +212,11 @@ static int stratosphere_ini_handler(void *user, const char *section, const char strat_cfg->has_nogc_config = true; sscanf(value, "%d", &tmp); strat_cfg->enable_nogc = tmp != 0; - } else if (strcmp(name, STRATOSPHERE_ENABLE_NCM_KEY) == 0) { + } else if (strcmp(name, STRATOSPHERE_DISABLE_NCM_KEY) == 0) { sscanf(value, "%d", &tmp); - strat_cfg->ncm_enabled = tmp != 0; - if (strat_cfg->ncm_enabled) { - stratosphere_enable_ncm(); + strat_cfg->ncm_disabled = tmp != 0; + if (strat_cfg->ncm_disabled) { + stratosphere_disable_ncm(); } } else { return 0; @@ -235,6 +239,7 @@ static uint32_t nxboot_get_specific_target_firmware(uint32_t target_firmware){ #define CHECK_NCA(NCA_ID, VERSION) do { if (is_nca_present(NCA_ID)) { return ATMOSPHERE_TARGET_FIRMWARE_##VERSION; } } while(0) if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_10_0_0) { + CHECK_NCA("26325de4db3909e0ef2379787c7e671d", 10_2_0); CHECK_NCA("5077973537f6735b564dd7475b779f87", 10_1_1); /* Exclusive to China. */ CHECK_NCA("fd1faed0ca750700d254c0915b93d506", 10_1_0); CHECK_NCA("34728c771299443420820d8ae490ea41", 10_0_4); @@ -622,6 +627,7 @@ static nx_keyblob_t __attribute__((aligned(16))) g_keyblobs[32]; uint32_t nxboot_main(void) { volatile tegra_pmc_t *pmc = pmc_get_regs(); loader_ctx_t *loader_ctx = get_loader_ctx(); + const bool is_experimental = fusee_is_experimental(); package2_header_t *package2; size_t package2_size; void *tsec_fw; @@ -633,6 +639,8 @@ uint32_t nxboot_main(void) { void *warmboot_memaddr; void *package1loader; size_t package1loader_size; + void *mesosphere; + size_t mesosphere_size; void *emummc; size_t emummc_size; uint32_t available_revision; @@ -928,6 +936,41 @@ uint32_t nxboot_main(void) { pmc->scratch1 = (uint32_t)warmboot_memaddr; } + /* Configure mesosphere. */ + { + size_t sd_meso_size = get_file_size("atmosphere/mesosphere.bin"); + if (sd_meso_size != 0) { + if (sd_meso_size > PACKAGE2_SIZE_MAX) { + fatal_error("Error: atmosphere/mesosphere.bin is too large!\n"); + } + mesosphere = malloc(sd_meso_size); + if (mesosphere == NULL) { + fatal_error("Error: failed to allocate mesosphere!\n"); + } + if (read_from_file(mesosphere, sd_meso_size, "atmosphere/mesosphere.bin") != sd_meso_size) { + fatal_error("Error: failed to read atmosphere/mesosphere.bin!\n"); + } + mesosphere_size = sd_meso_size; + } else if (is_experimental) { + mesosphere_size = mesosphere_bin_size; + + mesosphere = malloc(mesosphere_size); + + if (mesosphere == NULL) { + fatal_error("[NXBOOT] Out of memory!\n"); + } + + memcpy(mesosphere, mesosphere_bin, mesosphere_size); + + if (mesosphere_size == 0) { + fatal_error("[NXBOOT] Could not read embedded mesosphere!\n"); + } + } else { + mesosphere = NULL; + mesosphere_size = 0; + } + } + print(SCREEN_LOG_LEVEL_INFO, "[NXBOOT] Rebuilding package2...\n"); /* Parse stratosphere config. */ @@ -936,7 +979,7 @@ uint32_t nxboot_main(void) { print(SCREEN_LOG_LEVEL_INFO, u8"[NXBOOT] Configured Stratosphere...\n"); /* Patch package2, adding Thermosphère + custom KIPs. */ - package2_rebuild_and_copy(package2, MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware, emummc, emummc_size); + package2_rebuild_and_copy(package2, MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware, mesosphere, mesosphere_size, emummc, emummc_size); /* Set detected FS version. */ MAILBOX_EXOSPHERE_CONFIGURATION->emummc_cfg.base_cfg.fs_version = stratosphere_get_fs_version(); @@ -991,6 +1034,12 @@ uint32_t nxboot_main(void) { /* Wait for the splash screen to have been displayed for as long as it should be. */ splash_screen_wait_delay(); + /* Set reset for USBD, USB2, AHBDMA, and APBDMA. */ + rst_enable(CARDEVICE_USBD); + rst_enable(CARDEVICE_USB2); + rst_enable(CARDEVICE_AHBDMA); + rst_enable(CARDEVICE_APBDMA); + /* Return the memory address for booting CPU0. */ return (uint32_t)exosphere_memaddr; } diff --git a/fusee/fusee-secondary/src/package2.c b/fusee/fusee-secondary/src/package2.c index 5987dd0f1..f4948ae1a 100644 --- a/fusee/fusee-secondary/src/package2.c +++ b/fusee/fusee-secondary/src/package2.c @@ -44,7 +44,7 @@ static inline size_t align_to_4(size_t s) { return ((s + 3) >> 2) << 2; } -void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware, void *emummc, size_t emummc_size) { +void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware, void *mesosphere, size_t mesosphere_size, void *emummc, size_t emummc_size) { package2_header_t *rebuilt_package2; size_t rebuilt_package2_size; void *kernel; @@ -95,10 +95,28 @@ void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firm fatal_error("Error: inappropriate kernel embedded ini context"); } + /* Use mesosphere instead of Nintendo's kernel when present. */ + const bool is_mesosphere = mesosphere != NULL && mesosphere_size != 0; + if (is_mesosphere) { + kernel = mesosphere; + kernel_size = mesosphere_size; + + /* Patch mesosphere to use our rebuilt ini. */ + *(volatile uint64_t *)((uintptr_t)mesosphere + 8) = (uint64_t)mesosphere_size; + + /* Place the kernel section at the correct location. */ + package2->metadata.section_offsets[PACKAGE2_SECTION_KERNEL] = 0x60000; + package2->metadata.entrypoint = 0x60000; + + print(SCREEN_LOG_LEVEL_DEBUG, "Using Mesosphere...\n"); + } + print(SCREEN_LOG_LEVEL_DEBUG, "Rebuilding the INI1 section...\n"); if (target_firmware < ATMOSPHERE_TARGET_FIRMWARE_8_0_0) { package2_get_src_section((void *)&orig_ini1, package2, PACKAGE2_SECTION_INI1); - } else { + } + + if (target_firmware >= ATMOSPHERE_TARGET_FIRMWARE_8_0_0 || is_mesosphere) { /* On 8.0.0, place INI1 right after kernelldr for our sanity. */ package2->metadata.section_offsets[PACKAGE2_SECTION_INI1] = package2->metadata.section_offsets[PACKAGE2_SECTION_KERNEL] + kernel_size; } diff --git a/fusee/fusee-secondary/src/package2.h b/fusee/fusee-secondary/src/package2.h index e2f89f3a4..d77eb2094 100644 --- a/fusee/fusee-secondary/src/package2.h +++ b/fusee/fusee-secondary/src/package2.h @@ -94,6 +94,6 @@ static inline uint8_t package2_meta_get_header_version(const package2_meta_t *me return (uint8_t)((metadata->ctr_dwords[1] ^ (metadata->ctr_dwords[1] >> 16) ^ (metadata->ctr_dwords[1] >> 24)) & 0xFF); } -void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware, void *emummc, size_t emummc_size); +void package2_rebuild_and_copy(package2_header_t *package2, uint32_t target_firmware, void *mesosphere, size_t mesosphere_size, void *emummc, size_t emummc_size); #endif diff --git a/fusee/fusee-secondary/src/start.s b/fusee/fusee-secondary/src/start.s index 0050b053d..b9c118519 100644 --- a/fusee/fusee-secondary/src/start.s +++ b/fusee/fusee-secondary/src/start.s @@ -30,6 +30,9 @@ _start: .word (_metadata - _start) +_is_experimental: +.word 0x00000001 /* is experimental */ + _crt0: /* Switch to system mode, mask all interrupts, clear all flags */ msr cpsr_cxsf, #0xDF @@ -68,6 +71,14 @@ _crt0: ldr r1, [r1] b main +.arm +.global fusee_is_experimental +.type fusee_is_experimental, %function +fusee_is_experimental: + ldr r0, =_is_experimental + ldr r0, [r0] + bx lr + /* Fusee-secondary header. */ .align 5 _metadata: @@ -135,6 +146,17 @@ _content_headers: .asciz "exosphere" .align 5 +/* mesosphere content header */ +.word __mesosphere_bin_start__ +.word __mesosphere_bin_size__ +.byte CONTENT_TYPE_KRN +.byte CONTENT_FLAG_NONE +.byte CONTENT_FLAG_NONE +.byte CONTENT_FLAG_NONE +.word 0xCCCCCCCC +.asciz "mesosphere" +.align 5 + /* fusee_primary content header */ .word __fusee_primary_bin_start__ .word __fusee_primary_bin_size__ @@ -249,7 +271,7 @@ _content_headers: .word __ncm_kip_start__ .word __ncm_kip_size__ .byte CONTENT_TYPE_KIP -.byte CONTENT_FLAG0_EXPERIMENTAL +.byte CONTENT_FLAG_NONE .byte CONTENT_FLAG_NONE .byte CONTENT_FLAG_NONE .word 0xCCCCCCCC @@ -267,17 +289,6 @@ _content_headers: .asciz "emummc" .align 5 -/* kernel_ldr content header */ -.word __kernel_ldr_bin_start__ -.word __kernel_ldr_bin_size__ -.byte CONTENT_TYPE_KLD -.byte CONTENT_FLAG_NONE -.byte CONTENT_FLAG_NONE -.byte CONTENT_FLAG_NONE -.word 0xCCCCCCCC -.asciz "kernel_ldr" -.align 5 - /* splash_screen content header */ .word __splash_screen_bmp_start__ .word __splash_screen_bmp_size__ diff --git a/fusee/fusee-secondary/src/stratosphere.c b/fusee/fusee-secondary/src/stratosphere.c index 903b24206..4a7e21a1b 100644 --- a/fusee/fusee-secondary/src/stratosphere.c +++ b/fusee/fusee-secondary/src/stratosphere.c @@ -48,7 +48,7 @@ static bool g_stratosphere_pm_enabled = true; static bool g_stratosphere_ams_mitm_enabled = true; static bool g_stratosphere_spl_enabled = true; static bool g_stratosphere_boot_enabled = true; -static bool g_stratosphere_ncm_enabled = false; +static bool g_stratosphere_ncm_enabled = true; extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], boot_kip[], ncm_kip[], ams_mitm_kip[]; @@ -58,17 +58,17 @@ emummc_fs_ver_t stratosphere_get_fs_version(void) { return g_fs_ver; } -void stratosphere_enable_ncm(void) { +void stratosphere_disable_ncm(void) { /* The Atmosphere team believes our implementation of NCM to be extremely accurate, */ /* and does not think it likely there is any possibility of undesirable behavior */ /* when using the NCM reimplementation. However, because NCM manages critical save games */ - /* the implementation will default to off for some time, until the code has been thoroughly */ - /* tested in practice. */ + /* the implementation may be optionally disabled for those not comfortable using it. */ - /* PLEASE NOTE: The default behavior will be NCM on in a future atmosphere release, */ - /* and this opt-in functionality will be removed at that time. */ - g_stratosphere_ncm_enabled = true; + /* PLEASE NOTE: The NCM reimplementation has been well-tested, and correspondingly opt-out */ + /* functionality will be removed in Atmosphere 1.0.0. */ + + g_stratosphere_ncm_enabled = false; } /* GCC doesn't consider the size as const... we have to write it ourselves. */ diff --git a/fusee/fusee-secondary/src/stratosphere.h b/fusee/fusee-secondary/src/stratosphere.h index 0f41d36b1..de5693edd 100644 --- a/fusee/fusee-secondary/src/stratosphere.h +++ b/fusee/fusee-secondary/src/stratosphere.h @@ -30,7 +30,7 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware); ini1_header_t *stratosphere_get_sd_files_ini1(void); void stratosphere_free_ini1(void); -void stratosphere_enable_ncm(void); +void stratosphere_disable_ncm(void); emummc_fs_ver_t stratosphere_get_fs_version(void); @@ -39,10 +39,10 @@ ini1_header_t *stratosphere_merge_inis(ini1_header_t **inis, unsigned int num_in typedef struct { bool has_nogc_config; bool enable_nogc; - bool ncm_enabled; + bool ncm_disabled; } stratosphere_cfg_t; #define STRATOSPHERE_NOGC_KEY "nogc" -#define STRATOSPHERE_ENABLE_NCM_KEY "enable_ncm" +#define STRATOSPHERE_DISABLE_NCM_KEY "disable_ncm" #endif diff --git a/libraries/.gitrepo b/libraries/.gitrepo index c5fbf5ca1..22a982b02 100644 --- a/libraries/.gitrepo +++ b/libraries/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/Atmosphere-NX/Atmosphere-libs branch = master - commit = cac5957d3f4b1417cf76a83cf704a14a254dd4dc - parent = 3726def6ecc547e64912ddb050737ebd296366e7 + commit = 10e9e0e8f926b11c2c7de16ffe15bea7d7ec2cdf + parent = 2ee2a4f1ac04bc7f15de8be8d57ad04d7e73f735 method = merge cmdver = 0.4.1 diff --git a/libraries/config/arch/arm64/arch.mk b/libraries/config/arch/arm64/arch.mk index e3162d2dd..468ae233b 100644 --- a/libraries/config/arch/arm64/arch.mk +++ b/libraries/config/arch/arm64/arch.mk @@ -5,7 +5,7 @@ endif include $(DEVKITPRO)/devkitA64/base_rules export ATMOSPHERE_DEFINES += -DATMOSPHERE_ARCH_ARM64 -export ATMOSPHERE_SETTINGS += -march=armv8-a+crc+crypto -mtp=soft +export ATMOSPHERE_SETTINGS += -mtp=soft export ATMOSPHERE_CFLAGS += export ATMOSPHERE_CXXFLAGS += export ATMOSPHERE_ASFLAGS += diff --git a/libraries/config/arch/arm64/cpu/cortex_a57/cpu.mk b/libraries/config/arch/arm64/cpu/cortex_a57/cpu.mk index 19831467f..b4cc52648 100644 --- a/libraries/config/arch/arm64/cpu/cortex_a57/cpu.mk +++ b/libraries/config/arch/arm64/cpu/cortex_a57/cpu.mk @@ -1,5 +1,5 @@ export ATMOSPHERE_DEFINES += -DATMOSPHERE_CPU_ARM_CORTEX_A57 -export ATMOSPHERE_SETTINGS += -mtune=cortex-a57 +export ATMOSPHERE_SETTINGS += -march=armv8-a+crc+crypto -mtune=cortex-a57 export ATMOSPHERE_CFLAGS += export ATMOSPHERE_CXXFLAGS += export ATMOSPHERE_ASFLAGS += \ No newline at end of file diff --git a/libraries/config/common.mk b/libraries/config/common.mk index 6c5516238..1ca25d415 100644 --- a/libraries/config/common.mk +++ b/libraries/config/common.mk @@ -10,13 +10,15 @@ ifeq ($(strip $(ATMOSPHERE_BOARD)),) export ATMOSPHERE_BOARD := nx-hac-001 ifeq ($(strip $(ATMOSPHERE_CPU)),) -export ATMOSPHERE_CPU := arm-cortex-a57 +export ATMOSPHERE_CPU := arm-cortex-a57 endif endif +ATMOSPHERE_BUILD_SETTINGS ?= + export ATMOSPHERE_DEFINES := -DATMOSPHERE -export ATMOSPHERE_SETTINGS := -fPIE -g +export ATMOSPHERE_SETTINGS := -fPIE -g $(ATMOSPHERE_BUILD_SETTINGS) export ATMOSPHERE_CFLAGS := -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -fwrapv \ -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-stack-protector \ -Wno-format-truncation -Wno-format-zero-length -Wno-stringop-truncation @@ -28,40 +30,44 @@ export ATMOSPHERE_ASFLAGS := ifeq ($(ATMOSPHERE_BOARD),nx-hac-001) ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57) -export ATMOSPHERE_ARCH_DIR := arch/arm64 -export ATMOSPHERE_BOARD_DIR := board/nintendo/nx -export ATMOSPHERE_OS_DIR := os/horizon +export ATMOSPHERE_ARCH_DIR := arm64 +export ATMOSPHERE_BOARD_DIR := nintendo/nx +export ATMOSPHERE_OS_DIR := horizon export ATMOSPHERE_ARCH_NAME := arm64 export ATMOSPHERE_BOARD_NAME := nintendo_nx export ATMOSPHERE_OS_NAME := horizon + +export ATMOSPHERE_CPU_EXTENSIONS := arm_crypto_extension aarch64_crypto_extension else ifeq ($(ATMOSPHERE_CPU),arm7tdmi) -export ATMOSPHERE_ARCH_DIR := arch/arm -export ATMOSPHERE_BOARD_DIR := board/nintendo/nx_bpmp -export ATMOSPHERE_OS_DIR := os/horizon +export ATMOSPHERE_ARCH_DIR := arm +export ATMOSPHERE_BOARD_DIR := nintendo/nx_bpmp +export ATMOSPHERE_OS_DIR := horizon export ATMOSPHERE_ARCH_NAME := arm export ATMOSPHERE_BOARD_NAME := nintendo_nx export ATMOSPHERE_OS_NAME := horizon + +export ATMOSPHERE_CPU_EXTENSIONS := endif endif ifeq ($(ATMOSPHERE_CPU),arm-cortex-a57) -export ATMOSPHERE_CPU_DIR := arch/arm64/cpu/cortex_a57 +export ATMOSPHERE_CPU_DIR := cortex_a57 export ATMOSPHERE_CPU_NAME := arm_cortex_a57 endif ifeq ($(ATMOSPHERE_CPU),arm7tdmi) -export ATMOSPHERE_CPU_DIR := arch/arm/cpu/arm7tdmi +export ATMOSPHERE_CPU_DIR := arm7tdmi export ATMOSPHERE_CPU_NAME := arm7tdmi endif -export ATMOSPHERE_ARCH_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_ARCH_DIR) -export ATMOSPHERE_BOARD_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_BOARD_DIR) -export ATMOSPHERE_OS_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_OS_DIR) -export ATMOSPHERE_CPU_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/$(ATMOSPHERE_CPU_DIR) +export ATMOSPHERE_ARCH_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/arch/$(ATMOSPHERE_ARCH_DIR) +export ATMOSPHERE_BOARD_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/board/$(ATMOSPHERE_BOARD_DIR) +export ATMOSPHERE_OS_MAKE_DIR := $(ATMOSPHERE_CONFIG_MAKE_DIR)/os/$(ATMOSPHERE_OS_DIR) +export ATMOSPHERE_CPU_MAKE_DIR := $(ATMOSPHERE_ARCH_MAKE_DIR)/cpu/$(ATMOSPHERE_CPU_DIR) export ATMOSPHERE_LIBRARY_DIR := lib_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME) export ATMOSPHERE_BUILD_DIR := build_$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME) @@ -102,16 +108,41 @@ BUILD := build DATA := data INCLUDES := include -GENERAL_SOURCE_DIRS=$1 $(foreach d,$(filter-out $1/arch $1/board $1,$(wildcard $1/*)),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,)) -SPECIFIC_SOURCE_DIRS=$(if $(wildcard $1/$2/.*),$1/$2 $(call DIR_WILDCARD,$1/$2),) -ALL_SOURCE_DIRS=$(call GENERAL_SOURCE_DIRS,$1) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_ARCH_DIR)) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_BOARD_DIR)) $(call SPECIFIC_SOURCE_DIRS,$1,$(ATMOSPHERE_OS_DIR)) +GENERAL_SOURCE_DIRS=$1 $(foreach d,$(filter-out $1/arch $1/board $1/os $1/cpu $1,$(wildcard $1/*)),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,)) +SPECIFIC_SOURCE_DIRS=$(if $(wildcard $1/$2/$3/.*),$1/$2/$3 $(call DIR_WILDCARD,$1/$2/$3),$(if $(wildcard $1/$2/generic/.*), $1/$2/generic $(call DIR_WILDCARD,$1/$2/generic),)) +UNFILTERED_SOURCE_DIRS=$1 $(foreach d,$(wildcard $1/*),$(if $(wildcard $d/.),$(call DIR_WILDCARD,$d) $d,)) + +ALL_SOURCE_DIRS=$(call GENERAL_SOURCE_DIRS,$1) \ + $(call SPECIFIC_SOURCE_DIRS,$1,arch,$(ATMOSPHERE_ARCH_DIR)) \ + $(call SPECIFIC_SOURCE_DIRS,$1,board,$(ATMOSPHERE_BOARD_DIR)) \ + $(call SPECIFIC_SOURCE_DIRS,$1,os,$(ATMOSPHERE_OS_DIR)) \ + $(call SPECIFIC_SOURCE_DIRS,$1,cpu,$(ATMOSPHERE_ARCH_DIR)/$(ATMOSPHERE_CPU_DIR)) SOURCES ?= $(call ALL_SOURCE_DIRS,source) +FIND_SPECIFIC_SOURCE_FILES= $(notdir $(wildcard $1/*.$2.$3.$4)) $(filter-out $(subst .$2.$3.,.$2.generic.,$(notdir $(wildcard $1/*.$2.$3.$4))),$(notdir $(wildcard $1/*.$2.generic.$4))) + +FIND_SPECIFIC_SOURCE_FILES_EX=$(foreach ext,$3,$(notdir $(wildcard $1/*.$2.$(ext).$4))) $(filter-out $(foreach ext,$3,$(subst .$2.$(ext).,.$2.generic.,$(notdir $(wildcard $1/*.$2.$(ext).$4)))),$(notdir $(wildcard $1/*.$2.generic.$4))) + +FIND_SOURCE_FILES=$(foreach dir,$1,$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.$2)) \ + $(notdir $(wildcard $(dir)/*.board.*.$2)) \ + $(notdir $(wildcard $(dir)/*.os.*.$2)) \ + $(notdir $(wildcard $(dir)/.cpu.*.$2)), \ + $(notdir $(wildcard $(dir)/*.$2)))) \ + $(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),arch,$(ATMOSPHERE_ARCH_NAME),$2)) \ + $(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),board,$(ATMOSPHERE_BOARD_NAME),$2)) \ + $(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES,$(dir),os,$(ATMOSPHERE_OS_NAME),$2)) \ + $(foreach dir,$1,$(call FIND_SPECIFIC_SOURCE_FILES_EX,$(dir),cpu,$(ATMOSPHERE_CPU_NAME) $(ATMOSPHERE_CPU_EXTENSIONS),$2)) + +ATMOSPHERE_GCH_IDENTIFIER ?= ams_placeholder_gch_identifier + #--------------------------------------------------------------------------------- # Rules for compiling pre-compiled headers #--------------------------------------------------------------------------------- -%.gch: %.hpp - @echo $< - $(CXX) -w -x c++-header -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER) - @cp $@ $(<).gch +%.hpp.gch/$(ATMOSPHERE_GCH_IDENTIFIER): %.hpp | %.hpp.gch + @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 + @echo Precompiling $(notdir $<) + $(SILENTCMD)$(CXX) -w -x c++-header -MMD -MP -MQ$@ -MF $(DEPSDIR)/$(notdir $*).d $(CXXFLAGS) -c $< -o $@ $(ERROR_FILTER) diff --git a/libraries/config/templates/exosphere.mk b/libraries/config/templates/exosphere.mk index 7025e615e..6387d8989 100644 --- a/libraries/config/templates/exosphere.mk +++ b/libraries/config/templates/exosphere.mk @@ -8,7 +8,7 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk #--------------------------------------------------------------------------------- ifeq ($(strip $(ATMOSPHERE_ARCH_NAME)),arm64) DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) @@ -37,7 +37,7 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ -Wl,--wrap,_ZSt20__throw_length_errorPKc \ -Wl,--wrap,_ZNSt11logic_errorC2EPKc -export LIBS := -lexosphere +export LIBS := -l$(LIBEXOSPHERE_NAME) #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/libraries/config/templates/mesosphere.mk b/libraries/config/templates/mesosphere.mk index 98dcf1235..19a8dd2a0 100644 --- a/libraries/config/templates/mesosphere.mk +++ b/libraries/config/templates/mesosphere.mk @@ -7,10 +7,10 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../common.mk # options for code generation #--------------------------------------------------------------------------------- export DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_MESOSPHERE -export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror -fno-non-call-exceptions +export SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Wextra -Werror -fno-non-call-exceptions export CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) export CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit -export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) +export ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) export LDFLAGS = -specs=$(TOPDIR)/$(notdir $(TOPDIR)).specs -fno-asynchronous-unwind-tables -fno-unwind-tables -nostdlib -nostartfiles -g $(SETTINGS) -Wl,-Map,$(notdir $*.map) -Wl,-z,relro,-z,now @@ -29,7 +29,7 @@ export CXXWRAPS := -Wl,--wrap,__cxa_pure_virtual \ -Wl,--wrap,_ZSt20__throw_length_errorPKc \ -Wl,--wrap,_ZNSt11logic_errorC2EPKc -export LIBS := -lmesosphere +export LIBS := -l$(LIBMESOSPHERE_NAME) #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing diff --git a/libraries/libexosphere/Makefile b/libraries/libexosphere/Makefile index 97b463c6b..de8a0a44f 100644 --- a/libraries/libexosphere/Makefile +++ b/libraries/libexosphere/Makefile @@ -1,19 +1,34 @@ -.PHONY: all arm64 arm clean arm64-clean arm-clean +ATMOSPHERE_BUILD_CONFIGS := +all: arm64-release arm-release -all: arm64 arm +define ATMOSPHERE_ADD_TARGET -arm64: - $(MAKE) -f arm64.mk +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) -arm: - $(MAKE) -f arm.mk +arm64-$(strip $1): + @$$(MAKE) -f arm64.mk $(strip $1) -#--------------------------------------------------------------------------------- +arm-$(strip $1): + @$$(MAKE) -f arm.mk $(strip $1) -clean: arm64-clean arm-clean +clean-arm64-$(strip $1): + @$$(MAKE) -f arm64.mk clean-$(strip $1) -arm64-clean: - $(MAKE) -f arm64.mk clean +clean-arm-$(strip $1): + @$$(MAKE) -f arm.mk clean-$(strip $1) -arm-clean: - $(MAKE) -f arm.mk clean +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release)) +$(eval $(call ATMOSPHERE_ADD_TARGET, debug)) +$(eval $(call ATMOSPHERE_ADD_TARGET, audit)) + +clean-arm64: + @$(MAKE) -f arm64.mk clean + +clean-arm: + @$(MAKE) -f arm.mk clean + +clean: clean-arm64 clean-arm + +.PHONY: all clean clean-arm64 clean-arm $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),arm64-$(config) arm-$(config) clean-arm64-$(config) clean-arm-$(config)) diff --git a/libraries/libexosphere/arm.mk b/libraries/libexosphere/arm.mk index 5819d270c..3b3abcd84 100644 --- a/libraries/libexosphere/arm.mk +++ b/libraries/libexosphere/arm.mk @@ -7,6 +7,8 @@ export ATMOSPHERE_CPU := arm7tdmi #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- @@ -14,10 +16,10 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Werror -flto -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -Os -Wextra -Werror -flto -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit -ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source) @@ -33,29 +35,15 @@ LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours # no real need to edit anything past this point unless you need to add additional # rules for different file extensions #--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +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 @@ -73,7 +61,7 @@ endif export OFILES_BIN := $(addsuffix .o,$(BINFILES)) export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) +export GCH_DIRS := $(PRECOMPILED_HEADERS:.hpp=.hpp.gch) export OFILES := $(OFILES_BIN) $(OFILES_SRC) export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) @@ -81,40 +69,70 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I. -.PHONY: clean all +#--------------------------------------------------------------------------------- + +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + +$$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) : $$(ATMOSPHERE_LIBRARY_DIR) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(SOURCES) $$(INCLUDES) $$(GCH_DIRS) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(CURDIR)/$$@ $(3) \ + ATMOSPHERE_GCH_IDENTIFIER="$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)" \ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + @rm -fr $$(foreach hdr,$$(GCH_DIRS),$$(hdr)/$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)) + @for i in $$(GCH_DIRS) $$(ATMOSPHERE_BUILD_DIR) $$(ATMOSPHERE_LIBRARY_DIR); do [ -d $$$$i ] && rmdir --ignore-fail-on-non-empty $$$$i || true; done + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, $(TARGET).a, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS " \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, $(TARGET)_debug.a, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \ +)) #--------------------------------------------------------------------------------- -all: $(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a -$(ATMOSPHERE_LIBRARY_DIR): +-include $(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME).mk + +ALL_GCH_IDENTIFIERS := $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(config)) +ALL_GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(foreach id,$(ALL_GCH_IDENTIFIERS),$(hdr)/$(id))) + +.PHONY: clean all $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) + +$(ATMOSPHERE_LIBRARY_DIR) $(GCH_DIRS): @[ -d $@ ] || mkdir -p $@ -$(ATMOSPHERE_BUILD_DIR): +$(ATMOSPHERE_BUILD_DIR)/%: @[ -d $@ ] || mkdir -p $@ -$(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a : $(ATMOSPHERE_LIBRARY_DIR) $(ATMOSPHERE_BUILD_DIR) $(SOURCES) $(INCLUDES) - @$(MAKE) BUILD=$(ATMOSPHERE_BUILD_DIR) OUTPUT=$(CURDIR)/$@ \ - DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ - --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ - -f $(CURDIR)/arm.mk - -dist-bin: all - @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include $(ATMOSPHERE_LIBRARY_DIR) - -dist-src: - @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source arm.mk - -dist: dist-src dist-bin - #--------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 + @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 $(ALL_GCH_FILES) + @for i in $(GCH_DIRS); do [ -d $$i ] && rmdir --ignore-fail-on-non-empty $$i || true; done #--------------------------------------------------------------------------------- else -DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d) +GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(CURRENT_DIRECTORY)/$(hdr)/$(ATMOSPHERE_GCH_IDENTIFIER)) +DEPENDS := $(OFILES:.o=.d) $(foreach hdr,$(GCH_FILES),$(notdir $(patsubst %.hpp.gch/,%.d,$(dir $(hdr))))) #--------------------------------------------------------------------------------- # main targets @@ -126,6 +144,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 @@ -139,4 +158,3 @@ libc.o: CFLAGS += -fno-builtin -fno-lto #--------------------------------------------------------------------------------------- endif #--------------------------------------------------------------------------------------- - diff --git a/libraries/libexosphere/arm64.mk b/libraries/libexosphere/arm64.mk index 1ad716a3e..624a28bb5 100644 --- a/libraries/libexosphere/arm64.mk +++ b/libraries/libexosphere/arm64.mk @@ -7,6 +7,8 @@ export ATMOSPHERE_CPU := arm-cortex-a57 #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- @@ -14,10 +16,10 @@ include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk #--------------------------------------------------------------------------------- DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_EXOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -mgeneral-regs-only -ffixed-x18 -Os -Wextra -Werror -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit -ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source) @@ -33,29 +35,15 @@ LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours # no real need to edit anything past this point unless you need to add additional # rules for different file extensions #--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +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 @@ -73,7 +61,7 @@ endif export OFILES_BIN := $(addsuffix .o,$(BINFILES)) export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) +export GCH_DIRS := $(PRECOMPILED_HEADERS:.hpp=.hpp.gch) export OFILES := $(OFILES_BIN) $(OFILES_SRC) export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) @@ -81,41 +69,70 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I. -.PHONY: clean all +#--------------------------------------------------------------------------------- + +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + +$$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) : $$(ATMOSPHERE_LIBRARY_DIR) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(SOURCES) $$(INCLUDES) $$(GCH_DIRS) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(CURDIR)/$$@ $(3) \ + ATMOSPHERE_GCH_IDENTIFIER="$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)" \ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + @rm -fr $$(foreach hdr,$$(GCH_DIRS),$$(hdr)/$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)) + @for i in $$(GCH_DIRS) $$(ATMOSPHERE_BUILD_DIR) $$(ATMOSPHERE_LIBRARY_DIR); do [ -d $$$$i ] && rmdir --ignore-fail-on-non-empty $$$$i || true; done + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, $(TARGET).a, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS " \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, $(TARGET)_debug.a, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \ + ATMOSPHERE_BUILD_SETTINGS="-DAMS_FORCE_DISABLE_DETAILED_ASSERTIONS -DAMS_BUILD_FOR_AUDITING" \ +)) #--------------------------------------------------------------------------------- -all: $(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a -$(ATMOSPHERE_LIBRARY_DIR): +-include $(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME).mk + +ALL_GCH_IDENTIFIERS := $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(config)) +ALL_GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(foreach id,$(ALL_GCH_IDENTIFIERS),$(hdr)/$(id))) + +.PHONY: clean all $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) + +$(ATMOSPHERE_LIBRARY_DIR) $(GCH_DIRS): @[ -d $@ ] || mkdir -p $@ -$(ATMOSPHERE_BUILD_DIR): +$(ATMOSPHERE_BUILD_DIR)/%: @[ -d $@ ] || mkdir -p $@ -$(ATMOSPHERE_LIBRARY_DIR)/$(TARGET).a : $(ATMOSPHERE_LIBRARY_DIR) $(ATMOSPHERE_BUILD_DIR) $(SOURCES) $(INCLUDES) - @$(MAKE) BUILD=$(ATMOSPHERE_BUILD_DIR) OUTPUT=$(CURDIR)/$@ \ - BUILD_CFLAGS="-DNDEBUG=1 -O2" \ - DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ - --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ - -f $(CURDIR)/arm64.mk - -dist-bin: all - @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include $(ATMOSPHERE_LIBRARY_DIR) - -dist-src: - @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source arm64.mk - -dist: dist-src dist-bin - #--------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 + @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 $(ALL_GCH_FILES) + @for i in $(GCH_DIRS); do [ -d $$i ] && rmdir --ignore-fail-on-non-empty $$i || true; done #--------------------------------------------------------------------------------- else -DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d) +GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(CURRENT_DIRECTORY)/$(hdr)/$(ATMOSPHERE_GCH_IDENTIFIER)) +DEPENDS := $(OFILES:.o=.d) $(foreach hdr,$(GCH_FILES),$(notdir $(patsubst %.hpp.gch/,%.d,$(dir $(hdr))))) #--------------------------------------------------------------------------------- # main targets diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index d7403cbf5..5fe60639c 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -41,4 +40,3 @@ #include #include #include -#include \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/diag/diag_detailed_assertion_impl.inc b/libraries/libexosphere/include/exosphere/diag/diag_detailed_assertion_impl.inc new file mode 100644 index 000000000..52eab968a --- /dev/null +++ b/libraries/libexosphere/include/exosphere/diag/diag_detailed_assertion_impl.inc @@ -0,0 +1,93 @@ +/* + * 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 . + */ + +void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value) { + #if defined(AMS_ENABLE_DETAILED_ASSERTIONS) + { + AMS_LOG("Abort Called\n"); + AMS_LOG(" Location: %s:%d\n", file, line); + AMS_LOG(" Function: %s\n", func); + AMS_LOG(" Expression: %s\n", expr); + AMS_LOG(" Value: %016" PRIx64 "\n", value); + AMS_LOG("\n"); + } + #else + AMS_UNUSED(file, line, func, expr, value); + #endif + AbortImpl(); +} + +void AbortImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) { + #if defined(AMS_ENABLE_DETAILED_ASSERTIONS) + { + AMS_LOG("Abort Called\n"); + AMS_LOG(" Location: %s:%d\n", file, line); + AMS_LOG(" Function: %s\n", func); + AMS_LOG(" Expression: %s\n", expr); + AMS_LOG(" Value: %016" PRIx64 "\n", value); + AMS_LOG("\n"); + { + ::std::va_list vl; + va_start(vl, format); + AMS_VLOG(format, vl); + va_end(vl); + AMS_LOG("\n"); + } + } + #else + AMS_UNUSED(file, line, func, expr, value, format); + #endif + AbortImpl(); +} + +void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value) { + #if defined(AMS_ENABLE_DETAILED_ASSERTIONS) + { + AMS_LOG("Assertion Failure\n"); + AMS_LOG(" Location: %s:%d\n", file, line); + AMS_LOG(" Function: %s\n", func); + AMS_LOG(" Expression: %s\n", expr); + AMS_LOG(" Value: %016" PRIx64 "\n", value); + AMS_LOG("\n"); + } + #else + AMS_UNUSED(file, line, func, expr, value); + #endif + AbortImpl(); +} + +void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) { + #if defined(AMS_ENABLE_DETAILED_ASSERTIONS) + { + AMS_LOG("Assertion Failure\n"); + AMS_LOG(" Location: %s:%d\n", file, line); + AMS_LOG(" Function: %s\n", func); + AMS_LOG(" Expression: %s\n", expr); + AMS_LOG(" Value: %016" PRIx64 "\n", value); + AMS_LOG("\n"); + { + ::std::va_list vl; + va_start(vl, format); + AMS_VLOG(format, vl); + va_end(vl); + AMS_LOG("\n"); + } + } + #else + AMS_UNUSED(file, line, func, expr, value, format); + #endif + AbortImpl(); +} diff --git a/libraries/libexosphere/include/exosphere/log.hpp b/libraries/libexosphere/include/exosphere/log.hpp index 3c54fbedc..7ddde611d 100644 --- a/libraries/libexosphere/include/exosphere/log.hpp +++ b/libraries/libexosphere/include/exosphere/log.hpp @@ -18,10 +18,30 @@ namespace ams::log { + #if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) + #define AMS_IMPL_ENABLE_LOG + #endif + + #if defined(AMS_IMPL_ENABLE_LOG) + #define AMS_LOG(...) ::ams::log::Printf(__VA_ARGS__) + #define AMS_VLOG(...) ::ams::log::VPrintf(__VA_ARGS__) + #define AMS_DUMP(...) ::ams::log::Dump(__VA_ARGS__) + #define AMS_LOG_FLUSH() ::ams::log::Flush() + #else + #define AMS_LOG(...) static_cast(0) + #define AMS_VLOG(...) static_cast(0) + #define AMS_DUMP(...) static_cast(0) + #define AMS_LOG_FLUSH() static_cast(0) + #endif + void Initialize(); void Finalize(); + void Printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); + void VPrintf(const char *fmt, ::std::va_list vl); + void Dump(const void *src, size_t size); + void SendText(const void *text, size_t size); void Flush(); -} \ No newline at end of file +} diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_log.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_log.hpp new file mode 100644 index 000000000..61e298d55 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_log.hpp @@ -0,0 +1,20 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +#define AMS_SECMON_LOG(...) AMS_LOG(" [secmon] " __VA_ARGS__) diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index c6ec12b37..7cc8d2c6d 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -89,8 +89,10 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionPhysicalIram = MemoryRegion(UINT64_C(0x40000000), 0x40000); constexpr inline const MemoryRegion MemoryRegionPhysicalTzram = MemoryRegion(UINT64_C(0x7C010000), 0x10000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramMariko = MemoryRegion(UINT64_C(0x7C010000), 0x40000); static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalIram)); static_assert(MemoryRegionPhysical.Contains(MemoryRegionPhysicalTzram)); + static_assert(MemoryRegionPhysicalTzramMariko.Contains(MemoryRegionPhysicalTzram)); constexpr inline const MemoryRegion MemoryRegionPhysicalTzramVolatile(UINT64_C(0x7C010000), 0x2000); static_assert(MemoryRegionPhysicalTzram.Contains(MemoryRegionPhysicalTzramVolatile)); @@ -193,6 +195,10 @@ namespace ams::secmon { constexpr inline const MemoryRegion MemoryRegionVirtualTzramProgramExceptionVectors(UINT64_C(0x1F00C0000), 0x800); static_assert(MemoryRegionVirtualTzramProgram.Contains(MemoryRegionVirtualTzramProgramExceptionVectors)); + constexpr inline const MemoryRegion MemoryRegionVirtualTzramMarikoProgram(UINT64_C(0x1F00D0000), 0x20000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramMarikoProgram(UINT64_C(0x7C020000), 0x20000); + static_assert(MemoryRegionPhysicalTzramMariko.Contains(MemoryRegionPhysicalTzramMarikoProgram)); + constexpr inline const MemoryRegion MemoryRegionVirtualTzramProgramMain(UINT64_C(0x1F00C0800), 0xB800); static_assert(MemoryRegionVirtualTzramProgram.Contains(MemoryRegionVirtualTzramProgramMain)); @@ -218,6 +224,13 @@ namespace ams::secmon { static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualIramSc7Firmware)); static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramSc7Firmware)); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramSecureMonitorDebug(UINT64_C(0x40030000), 0x8000); + static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramSecureMonitorDebug)); + + constexpr inline const MemoryRegion MemoryRegionVirtualDebugCode = MemoryRegion(UINT64_C(0x1F0150000), 0x4000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDebugCode = MemoryRegion(UINT64_C(0x40034000), 0x4000); + static_assert(MemoryRegionPhysicalIramSecureMonitorDebug.Contains(MemoryRegionPhysicalDebugCode)); + constexpr inline const MemoryRegion MemoryRegionVirtualDebug = MemoryRegion(UINT64_C(0x1F0160000), 0x10000); static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDebug)); @@ -233,6 +246,11 @@ namespace ams::secmon { static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDramSecureDataStore)); static_assert(MemoryRegionDram.Contains(MemoryRegionPhysicalDramSecureDataStore)); + constexpr inline const MemoryRegion MemoryRegionVirtualDramDebugDataStore = MemoryRegion(UINT64_C(0x1F0110000), 0x4000); + constexpr inline const MemoryRegion MemoryRegionPhysicalDramDebugDataStore = MemoryRegion( UINT64_C(0x8000C000), 0x4000); + static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDramSecureDataStore)); + static_assert(MemoryRegionDram.Contains(MemoryRegionPhysicalDramSecureDataStore)); + constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreTzram = MemoryRegion(UINT64_C(0x1F0100000), 0xE000); constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreWarmbootFirmware = MemoryRegion(UINT64_C(0x1F010E000), 0x17C0); constexpr inline const MemoryRegion MemoryRegionVirtualDramSecureDataStoreSecurityEngineState = MemoryRegion(UINT64_C(0x1F010F7C0), 0x0840); diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp index 263ab28f8..568812c7c 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_monitor_context.hpp @@ -89,7 +89,13 @@ namespace ams::secmon { constexpr inline const SecureMonitorConfiguration DefaultSecureMonitorConfiguration = { .target_firmware = ams::TargetFirmware_Current, + .key_generation = {}, + .hardware_type = {}, + .soc_type = {}, + .hardware_state = {}, + .pad_0B = {}, .flags = SecureMonitorConfigurationFlag_Default, + .reserved = {}, }; } diff --git a/libraries/libexosphere/include/exosphere/tegra.hpp b/libraries/libexosphere/include/exosphere/tegra.hpp deleted file mode 100644 index 7f988871f..000000000 --- a/libraries/libexosphere/include/exosphere/tegra.hpp +++ /dev/null @@ -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 . - */ -#pragma once -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/libraries/libexosphere/include/exosphere/util.hpp b/libraries/libexosphere/include/exosphere/util.hpp index 088033e7c..ce31b63ea 100644 --- a/libraries/libexosphere/include/exosphere/util.hpp +++ b/libraries/libexosphere/include/exosphere/util.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 requires std::integral && std::integral - constexpr T DivideUp(T x, U y) { - return (x + (y - 1)) / y; - } - } \ No newline at end of file diff --git a/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp b/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp index d71f72440..54fab167e 100644 --- a/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp +++ b/libraries/libexosphere/source/crypto/crypto_aes_impl_security_engine.cpp @@ -33,6 +33,8 @@ namespace ams::crypto::impl { template void AesImpl::Initialize(const void *key, size_t key_size, bool is_encrypt) { static_assert(IsSupportedKeySize(KeySize)); + AMS_ASSERT(key_size == sizeof(int)); + AMS_UNUSED(is_encrypt); /* Set the security engine keyslot. */ this->slot = *static_cast(key); @@ -50,9 +52,11 @@ namespace ams::crypto::impl { } else if constexpr (KeySize == 24) { /* Aes 192. */ /* TODO: se::EncryptAes192(dst, dst_size, this->slot, src, src_size); */ + AMS_UNUSED(dst, dst_size, src, src_size); } else if constexpr (KeySize == 32) { /* Aes 256. */ /* TODO: se::EncryptAes256(dst, dst_size, this->slot, src, src_size); */ + AMS_UNUSED(dst, dst_size, src, src_size); } else { /* Invalid key size. */ static_assert(!std::is_same, AesImpl>::value); @@ -71,9 +75,11 @@ namespace ams::crypto::impl { } else if constexpr (KeySize == 24) { /* Aes 192. */ /* TODO: se::DecryptAes192(dst, dst_size, this->slot, src, src_size); */ + AMS_UNUSED(dst, dst_size, src, src_size); } else if constexpr (KeySize == 32) { /* Aes 256. */ /* TODO: se::DecryptAes256(dst, dst_size, this->slot, src, src_size); */ + AMS_UNUSED(dst, dst_size, src, src_size); } else { /* Invalid key size. */ static_assert(!std::is_same, AesImpl>::value); diff --git a/libraries/libexosphere/source/i2c/i2c_api.cpp b/libraries/libexosphere/source/i2c/i2c_api.cpp index 108429768..40900ff25 100644 --- a/libraries/libexosphere/source/i2c/i2c_api.cpp +++ b/libraries/libexosphere/source/i2c/i2c_api.cpp @@ -14,7 +14,6 @@ * along with this program. If not, see . */ #include -#include "i2c_registers.hpp" namespace ams::i2c { @@ -88,6 +87,8 @@ namespace ams::i2c { } bool Write(uintptr_t base_address, Port port, int address, const void *src, size_t src_size, bool unused) { + AMS_UNUSED(port, unused); + /* Ensure we don't write too much. */ u32 data = 0; if (src_size > MaxTransferSize) { @@ -125,6 +126,8 @@ namespace ams::i2c { } bool Read(uintptr_t base_address, Port port, void *dst, size_t dst_size, int address, bool unused) { + AMS_UNUSED(port, unused); + /* Ensure we don't read too much. */ if (dst_size > MaxTransferSize) { return false; diff --git a/libraries/libexosphere/source/i2c/i2c_registers.hpp b/libraries/libexosphere/source/i2c/i2c_registers.hpp deleted file mode 100644 index 48f659574..000000000 --- a/libraries/libexosphere/source/i2c/i2c_registers.hpp +++ /dev/null @@ -1,77 +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 . - */ -#include - -namespace ams::i2c { - - #define I2C_I2C_CNFG (0x000) - #define I2C_I2C_CMD_ADDR0 (0x004) - #define I2C_I2C_CMD_DATA1 (0x00C) - #define I2C_I2C_STATUS (0x01C) - #define I2C_INTERRUPT_STATUS_REGISTER (0x068) - #define I2C_CLK_DIVISOR_REGISTER (0x06C) - #define I2C_BUS_CLEAR_CONFIG (0x084) - #define I2C_BUS_CLEAR_STATUS (0x088) - #define I2C_CONFIG_LOAD (0x08C) - - #define I2C_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (I2C, NAME) - #define I2C_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (I2C, NAME, VALUE) - #define I2C_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (I2C, NAME, ENUM) - #define I2C_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(I2C, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) - - #define DEFINE_I2C_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (I2C, NAME, __OFFSET__, __WIDTH__) - #define DEFINE_I2C_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE) - #define DEFINE_I2C_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) - #define DEFINE_I2C_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) - #define DEFINE_I2C_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) - - /* I2C_CNFG */ - DEFINE_I2C_REG(I2C_CNFG_LENGTH, 1, 3); - DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_CMD1, 6, WRITE, READ); - DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_SEND, 9, NOP, GO); - DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_NEW_MASTER_FSM, 11, DISABLE, ENABLE); - DEFINE_I2C_REG_THREE_BIT_ENUM(I2C_CNFG_DEBOUNCE_CNT, 12, NO_DEBOUNCE, DEBOUNCE_2T, DEBOUNCE_4T, DEBOUNCE_6T, DEBOUNCE_8T, DEBOUNCE_10T, DEBOUNCE_12T, DEBOUNCE_14T); - - /* I2C_CMD_ADDR0 */ - DEFINE_I2C_REG_BIT_ENUM(I2C_CMD_ADDR0_7BIT_RW, 0, WRITE, READ); - DEFINE_I2C_REG(I2C_CMD_ADDR0_7BIT_ADDR, 1, 7); - - /* I2C_STATUS */ - DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD1_STAT, 0, SL1_XFER_SUCCESSFUL, SL1_NOACK_FOR_BYTE1, SL1_NOACK_FOR_BYTE2, SL1_NOACK_FOR_BYTE3, SL1_NOACK_FOR_BYTE4, SL1_NOACK_FOR_BYTE5, SL1_NOACK_FOR_BYTE6, SL1_NOACK_FOR_BYTE7, SL1_NOACK_FOR_BYTE8, SL1_NOACK_FOR_BYTE9, SL1_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); - DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD2_STAT, 4, SL2_XFER_SUCCESSFUL, SL2_NOACK_FOR_BYTE1, SL2_NOACK_FOR_BYTE2, SL2_NOACK_FOR_BYTE3, SL2_NOACK_FOR_BYTE4, SL2_NOACK_FOR_BYTE5, SL2_NOACK_FOR_BYTE6, SL2_NOACK_FOR_BYTE7, SL2_NOACK_FOR_BYTE8, SL2_NOACK_FOR_BYTE9, SL2_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); - DEFINE_I2C_REG_BIT_ENUM(I2C_STATUS_BUSY, 8, NOT_BUSY, BUSY); - - /* INTERRUPT_STATUS_REGISTER */ - DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, 11, UNSET, SET); - - /* CLK_DIVISOR_REGISTER */ - DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_HSMODE, 0, 16); - DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_STD_FAST_MODE, 16, 16); - - /* BUS_CLEAR_CONFIG */ - DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, 0, DISABLE, ENABLE); - DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, 1, THRESHOLD, IMMEDIATE); - DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, 2, NO_STOP, STOP); - DEFINE_I2C_REG(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 16, 8); - - /* CONFIG_LOAD */ - DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, 0, DISABLE, ENABLE); - DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_SLV_CONFIG_LOAD, 1, DISABLE, ENABLE); - DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, 2, DISABLE, ENABLE); - DEFINE_I2C_REG(CONFIG_LOAD_RESERVED_BIT_5, 5, 1); - - -} diff --git a/libraries/libexosphere/source/libc/libexo_cxx.cpp b/libraries/libexosphere/source/libc/libexo_cxx.cpp new file mode 100644 index 000000000..64efc6786 --- /dev/null +++ b/libraries/libexosphere/source/libc/libexo_cxx.cpp @@ -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 . + */ +#include + +#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 diff --git a/libraries/libexosphere/source/libc/libgcc_division.arch.arm.c b/libraries/libexosphere/source/libc/libgcc_division.arch.arm.c new file mode 100644 index 000000000..d9e861777 --- /dev/null +++ b/libraries/libexosphere/source/libc/libgcc_division.arch.arm.c @@ -0,0 +1,260 @@ +// 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); +} + +/* struct lqr - stores qutient/remainder to handle divmod EABI interfaces. */ +struct lqr { + unsigned long long q; /* computed quotient */ + unsigned long long r; /* computed remainder */ + unsigned q_n; /* specficies if quotient shall be negative */ + unsigned r_n; /* specficies if remainder shall be negative */ +}; + +static void ul_div_qr(unsigned long long numerator, + unsigned long long denominator, struct lqr *qr); + + +static void division_lqr(unsigned long long n, unsigned long long p, + struct lqr *qr) +{ + unsigned long long i = 1, q = 0; + if (p == 0) { + qr->r = 0xFFFFFFFFFFFFFFFFULL; /* division by 0 */ + return; + } + + while ((p >> 63) == 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 ul_div_qr(unsigned long long numerator, + unsigned long long denominator, struct lqr *qr) +{ + + division_lqr(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; +} + +struct asm_ulqr { + unsigned long long v0; + unsigned long long v1; +}; + +/* called from assembly function __aeabi_uldivmod */ +void __ul_divmod(struct asm_ulqr *asm_ulqr); +void __ul_divmod(struct asm_ulqr *asm_ulqr) +{ + unsigned long long numerator = asm_ulqr->v0; + unsigned long long denominator = asm_ulqr->v1; + struct lqr qr = { .q_n = 0, .r_n = 0 }; + + ul_div_qr(numerator, denominator, &qr); + + asm_ulqr->v0 = qr.q; + asm_ulqr->v1 = qr.r; +} + +struct asm_lqr { + long long v0; + long long v1; +}; + +/* called from assembly function __aeabi_ldivmod */ +void __l_divmod(struct asm_lqr *asm_lqr); +void __l_divmod(struct asm_lqr *asm_lqr) +{ + long long numerator = asm_lqr->v0; + long long denominator = asm_lqr->v1; + struct lqr 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; + + ul_div_qr(numerator, denominator, &qr); + + asm_lqr->v0 = qr.q; + asm_lqr->v1 = qr.r; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif \ No newline at end of file diff --git a/libraries/libexosphere/source/libc/libgcc_division_asm.arch.arm.s b/libraries/libexosphere/source/libc/libgcc_division_asm.arch.arm.s new file mode 100644 index 000000000..d5217e0a3 --- /dev/null +++ b/libraries/libexosphere/source/libc/libgcc_division_asm.arch.arm.s @@ -0,0 +1,67 @@ +/* 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 + +/* + * __value_in_regs lldiv_t __aeabi_ldivmod( long long n, long long d) + */ +.section .text.__aeabi_ldivmod, "ax", %progbits +.globl __aeabi_ldivmod +.align 0 +.syntax unified +__aeabi_ldivmod: + push {ip, lr} + push {r0-r3} + mov r0, sp + bl __l_divmod + pop {r0-r3} + pop {ip, pc} + +.type __aeabi_ldivmod, %function +.size __aeabi_ldivmod, .-__aeabi_ldivmod + +/* + * __value_in_regs ulldiv_t __aeabi_uldivmod( + * unsigned long long n, unsigned long long d) + */ +.section .text.__aeabi_uldivmod , "ax", %progbits +.globl __aeabi_uldivmod +.align 0 +.syntax unified +__aeabi_uldivmod : + push {ip, lr} + push {r0-r3} + mov r0, sp + bl __ul_divmod + pop {r0-r3} + pop {ip, pc} + +.type __aeabi_uldivmod, %function +.size __aeabi_uldivmod, .-__aeabi_uldivmod diff --git a/libraries/libexosphere/source/libc/libgcc_thumb_case.arch.arm.s b/libraries/libexosphere/source/libc/libgcc_thumb_case.arch.arm.s new file mode 100644 index 000000000..a7726543a --- /dev/null +++ b/libraries/libexosphere/source/libc/libgcc_thumb_case.arch.arm.s @@ -0,0 +1,54 @@ +/* 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 +. */ + + +.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 + +.section .text.__gnu_thumb1_case_uhi, "ax", %progbits +.globl __gnu_thumb1_case_uhi +.align 0 +.thumb_func +.syntax unified +__gnu_thumb1_case_uhi: + push {r0, r1} + mov r1, lr + lsrs r1, r1, #1 + lsls r0, r0, #1 + lsls r1, r1, #1 + ldrh r1, [r1, r0] + lsls r1, r1, #1 + add lr, lr, r1 + pop {r0, r1} + bx lr +.type __gnu_thumb1_case_uhi, %function +.size __gnu_thumb1_case_uhi, .-__gnu_thumb1_case_uhi diff --git a/libraries/libexosphere/source/log/log_api.cpp b/libraries/libexosphere/source/log/log_api.cpp index b6c96ed02..068dddc20 100644 --- a/libraries/libexosphere/source/log/log_api.cpp +++ b/libraries/libexosphere/source/log/log_api.cpp @@ -20,8 +20,8 @@ namespace ams::log { namespace { constexpr inline uart::Port UartLogPort = uart::Port_ReservedDebug; + constexpr inline int UartBaudRate = 115200; constinit bool g_initialized_uart = false; - constinit bool g_logging_enabled = false; constexpr inline u32 UartPortFlags = [] { if constexpr (UartLogPort == uart::Port_ReservedDebug) { @@ -68,7 +68,7 @@ namespace ams::log { SetupUart(); /* Initialize the target uart port. */ - uart::Initialize(UartLogPort, 115200, UartPortFlags); + uart::Initialize(UartLogPort, UartBaudRate, UartPortFlags); /* Note that we've initialized. */ g_initialized_uart = true; @@ -78,20 +78,51 @@ namespace ams::log { g_initialized_uart = false; } - void SetDebugLogEnabled(bool en) { - g_logging_enabled = en; + NOINLINE void VPrintf(const char *fmt, ::std::va_list vl) { + /* TODO: What's a good size for the log buffer? Nintendo uses 0x100, but this seems big. */ + char log_buf[0x80]; + const auto len = util::TVSNPrintf(log_buf, sizeof(log_buf), fmt, vl); + + if (g_initialized_uart) { + uart::SendText(UartLogPort, log_buf, len); + } } + NOINLINE void Printf(const char *fmt, ...) { + ::std::va_list vl; + va_start(vl, fmt); + VPrintf(fmt, vl); + va_end(vl); + } + + NOINLINE void Dump(const void *src, size_t size) { + const u8 *src_u8 = static_cast(src); + + for (size_t i = 0; i < size; ++i) { + if ((i % 0x20) == 0x00) { + Printf("%03zx| ", i); + } + Printf("%02x ", src_u8[i]); + if ((i % 0x20) == 0x1F) { + Printf("\n"); + } + } + if ((size % 0x20) != 0) { + Printf("\n"); + } + } + + void SendText(const void *text, size_t size) { - if (g_initialized_uart && g_logging_enabled) { + if (g_initialized_uart) { uart::SendText(UartLogPort, text, size); } } void Flush() { - if (g_initialized_uart && g_logging_enabled) { + if (g_initialized_uart) { uart::WaitFlush(UartLogPort); } } -} \ No newline at end of file +} diff --git a/libraries/libexosphere/source/wdt/wdt_api.cpp b/libraries/libexosphere/source/wdt/wdt_api.cpp index cb26f5e05..ef9ad6139 100644 --- a/libraries/libexosphere/source/wdt/wdt_api.cpp +++ b/libraries/libexosphere/source/wdt/wdt_api.cpp @@ -81,7 +81,8 @@ namespace ams::wdt { /* Enable the counters. */ reg::Write(registers + 0x188, 0x1); - while (true) { /* ... */ } + /* Wait forever until the reboot takes. */ + AMS_INFINITE_LOOP(); } #endif diff --git a/libraries/libmesosphere/Makefile b/libraries/libmesosphere/Makefile index 747fd2856..14e7bc50d 100644 --- a/libraries/libmesosphere/Makefile +++ b/libraries/libmesosphere/Makefile @@ -1,18 +1,20 @@ #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) +include $(CURRENT_DIRECTORY)/../config/common.mk #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -PRECOMPILED_HEADERS := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/include/mesosphere.hpp +PRECOMPILED_HEADERS := include/mesosphere.hpp DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_MESOSPHERE -SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Werror -fno-non-call-exceptions +SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 -mgeneral-regs-only -ffixed-x18 -Wextra -Werror -fno-non-call-exceptions CFLAGS := $(ATMOSPHERE_CFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) CXXFLAGS := $(CFLAGS) $(ATMOSPHERE_CXXFLAGS) -fno-use-cxa-atexit -flto -ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) +ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) $(DEFINES) $(INCLUDE) SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source) @@ -28,29 +30,15 @@ LIBDIRS := $(ATMOSPHERE_LIBRARIES_DIR)/libvapours # no real need to edit anything past this point unless you need to add additional # rules for different file extensions #--------------------------------------------------------------------------------- -ifneq ($(BUILD),$(notdir $(CURDIR))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +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 @@ -68,7 +56,7 @@ endif export OFILES_BIN := $(addsuffix .o,$(BINFILES)) export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) +export GCH_DIRS := $(PRECOMPILED_HEADERS:.hpp=.hpp.gch) export OFILES := $(OFILES_BIN) $(OFILES_SRC) export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) @@ -76,41 +64,70 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I. -.PHONY: clean all +#--------------------------------------------------------------------------------- + +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + +$$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) : $$(ATMOSPHERE_LIBRARY_DIR) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(SOURCES) $$(INCLUDES) $$(GCH_DIRS) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(CURDIR)/$$@ $(3) \ + ATMOSPHERE_GCH_IDENTIFIER="$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)" \ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(ATMOSPHERE_LIBRARY_DIR)/$(strip $2) + @rm -fr $$(foreach hdr,$$(GCH_DIRS),$$(hdr)/$$(ATMOSPHERE_BOARD_NAME)_$$(ATMOSPHERE_ARCH_NAME)_$(strip $1)) + @for i in $$(GCH_DIRS) $$(ATMOSPHERE_BUILD_DIR) $$(ATMOSPHERE_LIBRARY_DIR); do [ -d $$$$i ] && rmdir --ignore-fail-on-non-empty $$$$i || true; done + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, $(TARGET).a, \ + ATMOSPHERE_BUILD_SETTINGS="" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, $(TARGET)_debug.a, \ + ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, $(TARGET)_audit.a, \ + ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \ +)) #--------------------------------------------------------------------------------- -all: lib/$(TARGET).a -lib: +-include $(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME).mk + +ALL_GCH_IDENTIFIERS := $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(ATMOSPHERE_BOARD_NAME)_$(ATMOSPHERE_ARCH_NAME)_$(config)) +ALL_GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(foreach id,$(ALL_GCH_IDENTIFIERS),$(hdr)/$(id))) + +.PHONY: clean all $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) + +$(ATMOSPHERE_LIBRARY_DIR) $(GCH_DIRS): @[ -d $@ ] || mkdir -p $@ -release: +$(ATMOSPHERE_BUILD_DIR)/%: @[ -d $@ ] || mkdir -p $@ -lib/$(TARGET).a : lib release $(SOURCES) $(INCLUDES) - @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ - BUILD_CFLAGS="-DNDEBUG=1 -O2" \ - DEPSDIR=$(CURDIR)/release \ - --no-print-directory -C release \ - -f $(CURDIR)/Makefile - -dist-bin: all - @tar --exclude=*~ -cjf $(TARGET).tar.bz2 include lib - -dist-src: - @tar --exclude=*~ -cjf $(TARGET)-src.tar.bz2 include source Makefile - -dist: dist-src dist-bin - #--------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr release lib *.bz2 + @rm -fr $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARY_DIR) *.bz2 $(ALL_GCH_FILES) + @for i in $(GCH_DIRS); do [ -d $$i ] && rmdir --ignore-fail-on-non-empty $$i || true; done #--------------------------------------------------------------------------------- else -DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d) +GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.hpp.gch),$(CURRENT_DIRECTORY)/$(hdr)/$(ATMOSPHERE_GCH_IDENTIFIER)) +DEPENDS := $(OFILES:.o=.d) $(foreach hdr,$(GCH_FILES),$(notdir $(patsubst %.hpp.gch/,%.d,$(dir $(hdr))))) #--------------------------------------------------------------------------------- # main targets diff --git a/libraries/libmesosphere/include/mesosphere.hpp b/libraries/libmesosphere/include/mesosphere.hpp index 279eb2a0a..66be8d6d8 100644 --- a/libraries/libmesosphere/include/mesosphere.hpp +++ b/libraries/libmesosphere/include/mesosphere.hpp @@ -27,6 +27,9 @@ #include #include +/* Tracing functionality. */ +#include + /* Core pre-initialization includes. */ #include #include diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm/kern_generic_interrupt_controller.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm/kern_generic_interrupt_controller.hpp new file mode 100644 index 000000000..267cc8ad0 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm/kern_generic_interrupt_controller.hpp @@ -0,0 +1,279 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::kern::arch::arm { + + struct GicDistributor { + u32 ctlr; + u32 typer; + u32 iidr; + u32 reserved_0x0c; + u32 statusr; + u32 reserved_0x14[3]; + u32 impldef_0x20[8]; + u32 setspi_nsr; + u32 reserved_0x44; + u32 clrspi_nsr; + u32 reserved_0x4c; + u32 setspi_sr; + u32 reserved_0x54; + u32 clrspi_sr; + u32 reserved_0x5c[9]; + u32 igroupr[32]; + u32 isenabler[32]; + u32 icenabler[32]; + u32 ispendr[32]; + u32 icpendr[32]; + u32 isactiver[32]; + u32 icactiver[32]; + union { + u8 bytes[1020]; + u32 words[255]; + } ipriorityr; + u32 _0x7fc; + union { + u8 bytes[1020]; + u32 words[255]; + } itargetsr; + u32 _0xbfc; + u32 icfgr[64]; + u32 igrpmodr[32]; + u32 _0xd80[32]; + u32 nsacr[64]; + u32 sgir; + u32 _0xf04[3]; + u32 cpendsgir[4]; + u32 spendsgir[4]; + u32 reserved_0xf30[52]; + + static constexpr size_t SgirCpuTargetListShift = 16; + + enum SgirTargetListFilter : u32 { + SgirTargetListFilter_CpuTargetList = (0 << 24), + SgirTargetListFilter_Others = (1 << 24), + SgirTargetListFilter_Self = (2 << 24), + SgirTargetListFilter_Reserved = (3 << 24), + }; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(GicDistributor) == 0x1000); + + struct GicCpuInterface { + u32 ctlr; + u32 pmr; + u32 bpr; + u32 iar; + u32 eoir; + u32 rpr; + u32 hppir; + u32 abpr; + u32 aiar; + u32 aeoir; + u32 ahppir; + u32 statusr; + u32 reserved_30[4]; + u32 impldef_40[36]; + u32 apr[4]; + u32 nsapr[4]; + u32 reserved_f0[3]; + u32 iidr; + u32 reserved_100[960]; + u32 dir; + u32 _0x1004[1023]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(GicCpuInterface) == 0x2000); + + struct KInterruptController { + NON_COPYABLE(KInterruptController); + NON_MOVEABLE(KInterruptController); + public: + static constexpr s32 NumSoftwareInterrupts = 16; + static constexpr s32 NumLocalInterrupts = NumSoftwareInterrupts + 16; + static constexpr s32 NumGlobalInterrupts = 988; + static constexpr s32 NumInterrupts = NumLocalInterrupts + NumGlobalInterrupts; + static constexpr s32 NumPriorityLevels = 4; + public: + struct LocalState { + u32 isenabler[NumLocalInterrupts / 32]; + u32 ipriorityr[NumLocalInterrupts / 4]; + u32 itargetsr[NumLocalInterrupts / 4]; + u32 icfgr[NumLocalInterrupts / 16]; + }; + + struct GlobalState { + u32 isenabler[NumGlobalInterrupts / 32]; + u32 ipriorityr[NumGlobalInterrupts / 4]; + u32 itargetsr[NumGlobalInterrupts / 4]; + u32 icfgr[NumGlobalInterrupts / 16]; + }; + + enum PriorityLevel : u8 { + PriorityLevel_High = 0, + PriorityLevel_Low = NumPriorityLevels - 1, + + PriorityLevel_Timer = 1, + PriorityLevel_Scheduler = 2, + }; + private: + static inline u32 s_mask[cpu::NumCores]; + private: + volatile GicDistributor *gicd; + volatile GicCpuInterface *gicc; + public: + constexpr KInterruptController() : gicd(nullptr), gicc(nullptr) { /* ... */ } + + void Initialize(s32 core_id); + void Finalize(s32 core_id); + + void SaveCoreLocal(LocalState *state) const; + void SaveGlobal(GlobalState *state) const; + void RestoreCoreLocal(const LocalState *state) const; + void RestoreGlobal(const GlobalState *state) const; + public: + u32 GetIrq() const { + return this->gicc->iar; + } + + static constexpr s32 ConvertRawIrq(u32 irq) { + return (irq == 0x3FF) ? -1 : (irq & 0x3FF); + } + + void Enable(s32 irq) const { + this->gicd->isenabler[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32))); + } + + void Disable(s32 irq) const { + this->gicd->icenabler[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32))); + } + + void Clear(s32 irq) const { + this->gicd->icpendr[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32))); + } + + void SetTarget(s32 irq, s32 core_id) const { + this->gicd->itargetsr.bytes[irq] = this->gicd->itargetsr.bytes[irq] | GetGicMask(core_id); + } + + void ClearTarget(s32 irq, s32 core_id) const { + this->gicd->itargetsr.bytes[irq] = this->gicd->itargetsr.bytes[irq] & ~GetGicMask(core_id); + } + + void SetPriorityLevel(s32 irq, s32 level) const { + MESOSPHERE_ASSERT(PriorityLevel_High <= level && level <= PriorityLevel_Low); + this->gicd->ipriorityr.bytes[irq] = ToGicPriorityValue(level); + } + + s32 GetPriorityLevel(s32 irq) const { + return FromGicPriorityValue(this->gicd->ipriorityr.bytes[irq]); + } + + void SetPriorityLevel(s32 level) const { + MESOSPHERE_ASSERT(PriorityLevel_High <= level && level <= PriorityLevel_Low); + this->gicc->pmr = ToGicPriorityValue(level); + } + + void SetEdge(s32 irq) const { + u32 cfg = this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)]; + cfg &= ~(0x3 << (2 * (irq % (BITSIZEOF(u32) / 2)))); + cfg |= (0x2 << (2 * (irq % (BITSIZEOF(u32) / 2)))); + this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)] = cfg; + } + + void SetLevel(s32 irq) const { + u32 cfg = this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)]; + cfg &= ~(0x3 << (2 * (irq % (BITSIZEOF(u32) / 2)))); + cfg |= (0x0 << (2 * (irq % (BITSIZEOF(u32) / 2)))); + this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)] = cfg; + } + + void SendInterProcessorInterrupt(s32 irq, u64 core_mask) { + MESOSPHERE_ASSERT(IsSoftware(irq)); + this->gicd->sgir = GetCpuTargetListMask(irq, core_mask); + } + + void SendInterProcessorInterrupt(s32 irq) { + MESOSPHERE_ASSERT(IsSoftware(irq)); + this->gicd->sgir = GicDistributor::SgirTargetListFilter_Others | irq; + } + + void EndOfInterrupt(u32 irq) const { + this->gicc->eoir = irq; + } + + bool IsInterruptDefined(s32 irq) const { + const s32 num_interrupts = std::min(32 + 32 * (this->gicd->typer & 0x1F), static_cast(NumInterrupts)); + return (0 <= irq && irq < num_interrupts); + } + public: + static constexpr ALWAYS_INLINE bool IsSoftware(s32 id) { + MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts); + return id < NumSoftwareInterrupts; + } + + static constexpr ALWAYS_INLINE bool IsLocal(s32 id) { + MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts); + return id < NumLocalInterrupts; + } + + static constexpr ALWAYS_INLINE bool IsGlobal(s32 id) { + MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts); + return NumLocalInterrupts <= id; + } + + static constexpr size_t GetGlobalInterruptIndex(s32 id) { + MESOSPHERE_ASSERT(IsGlobal(id)); + return id - NumLocalInterrupts; + } + + static constexpr size_t GetLocalInterruptIndex(s32 id) { + MESOSPHERE_ASSERT(IsLocal(id)); + return id; + } + private: + static constexpr size_t PriorityShift = BITSIZEOF(u8) - __builtin_ctz(NumPriorityLevels); + static_assert(PriorityShift < BITSIZEOF(u8)); + + static constexpr ALWAYS_INLINE u8 ToGicPriorityValue(s32 level) { + return (level << PriorityShift) | ((1 << PriorityShift) - 1); + } + + static constexpr ALWAYS_INLINE s32 FromGicPriorityValue(u8 priority) { + return (priority >> PriorityShift) & (NumPriorityLevels - 1); + } + + static constexpr ALWAYS_INLINE s32 GetCpuTargetListMask(s32 irq, u64 core_mask) { + MESOSPHERE_ASSERT(IsSoftware(irq)); + MESOSPHERE_ASSERT(core_mask < (1ul << cpu::NumCores)); + return GicDistributor::SgirTargetListFilter_CpuTargetList | irq | (static_cast(core_mask) << GicDistributor::SgirCpuTargetListShift); + } + + static ALWAYS_INLINE s32 GetGicMask(s32 core_id) { + return s_mask[core_id]; + } + + ALWAYS_INLINE void SetGicMask(s32 core_id) const { + s_mask[core_id] = this->gicd->itargetsr.bytes[0]; + } + + NOINLINE void SetupInterruptLines(s32 core_id) const; + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm/kern_k_interrupt_controller.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm/kern_k_interrupt_controller.hpp new file mode 100644 index 000000000..3399322ce --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm/kern_k_interrupt_controller.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +#if 1 + + #include + +#else + + #error "Unknown board for KInterruptController" + +#endif diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm/kern_k_memory_region_device_types.inc b/libraries/libmesosphere/include/mesosphere/arch/arm/kern_k_memory_region_device_types.inc new file mode 100644 index 000000000..65f6fe8cd --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm/kern_k_memory_region_device_types.inc @@ -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 . + */ + +/* All architectures must define NumArchitectureDeviceRegions. */ +constexpr inline const auto NumArchitectureDeviceRegions = 3; + +constexpr inline const auto KMemoryRegionType_Uart = KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 0); +constexpr inline const auto KMemoryRegionType_InterruptCpuInterface = KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 1).SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_InterruptDistributor = KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 2).SetAttribute(KMemoryRegionAttr_NoUserMap); +static_assert(KMemoryRegionType_Uart .GetValue() == (0x1D)); +static_assert(KMemoryRegionType_InterruptCpuInterface.GetValue() == (0x2D | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_InterruptDistributor .GetValue() == (0x4D | KMemoryRegionAttr_NoUserMap)); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp index a09523477..357a2ae5f 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/init/kern_k_init_page_table.hpp @@ -74,6 +74,10 @@ namespace ams::kern::arch::arm64::init { static ALWAYS_INLINE void ClearNewPageTable(KPhysicalAddress address) { ClearPhysicalMemory(address, PageSize); } + public: + static consteval size_t GetMaximumOverheadSize(size_t size) { + return (util::DivideUp(size, L1BlockSize) + util::DivideUp(size, L2BlockSize)) * PageSize; + } private: size_t NOINLINE GetBlockCount(KVirtualAddress virt_addr, size_t size, size_t block_size) { const KVirtualAddress end_virt_addr = virt_addr + size; diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp index e3e519020..4182c5197 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_cpu.hpp @@ -59,11 +59,6 @@ namespace ams::kern::arch::arm64::cpu { InstructionMemoryBarrier(); } - ALWAYS_INLINE void InvalidateEntireInstructionCache() { - __asm__ __volatile__("ic iallu" ::: "memory"); - EnsureInstructionConsistency(); - } - ALWAYS_INLINE void Yield() { __asm__ __volatile__("yield" ::: "memory"); } @@ -179,6 +174,7 @@ namespace ams::kern::arch::arm64::cpu { void ClearPageToZeroImpl(void *); void FlushEntireDataCacheSharedForInit(); void FlushEntireDataCacheLocalForInit(); + void InvalidateEntireInstructionCacheForInit(); void StoreEntireCacheForInit(); void FlushEntireDataCache(); @@ -188,6 +184,8 @@ namespace ams::kern::arch::arm64::cpu { Result FlushDataCache(const void *addr, size_t size); Result InvalidateInstructionCache(void *addr, size_t size); + void InvalidateEntireInstructionCache(); + ALWAYS_INLINE void ClearPageToZero(void *page) { MESOSPHERE_ASSERT(util::IsAligned(reinterpret_cast(page), PageSize)); MESOSPHERE_ASSERT(page != nullptr); diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp index f06acd990..b9735db84 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp @@ -33,7 +33,7 @@ namespace ams::kern::arch::arm64 { explicit KDebug() { /* ... */ } virtual ~KDebug() { /* ... */ } - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } public: virtual Result GetThreadContextImpl(ams::svc::ThreadContext *out, KThread *thread, u32 context_flags) override; virtual Result SetThreadContextImpl(const ams::svc::ThreadContext &ctx, KThread *thread, u32 context_flags) override; diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp index 4051d4c56..3e88b7536 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_interrupt_controller.hpp @@ -18,261 +18,19 @@ #include #include -namespace ams::kern::arch::arm64 { +#if 1 - struct GicDistributor { - u32 ctlr; - u32 typer; - u32 iidr; - u32 reserved_0x0c; - u32 statusr; - u32 reserved_0x14[3]; - u32 impldef_0x20[8]; - u32 setspi_nsr; - u32 reserved_0x44; - u32 clrspi_nsr; - u32 reserved_0x4c; - u32 setspi_sr; - u32 reserved_0x54; - u32 clrspi_sr; - u32 reserved_0x5c[9]; - u32 igroupr[32]; - u32 isenabler[32]; - u32 icenabler[32]; - u32 ispendr[32]; - u32 icpendr[32]; - u32 isactiver[32]; - u32 icactiver[32]; - union { - u8 bytes[1020]; - u32 words[255]; - } ipriorityr; - u32 _0x7fc; - union { - u8 bytes[1020]; - u32 words[255]; - } itargetsr; - u32 _0xbfc; - u32 icfgr[64]; - u32 igrpmodr[32]; - u32 _0xd80[32]; - u32 nsacr[64]; - u32 sgir; - u32 _0xf04[3]; - u32 cpendsgir[4]; - u32 spendsgir[4]; - u32 reserved_0xf30[52]; + #include + namespace ams::kern::arch::arm64 { - static constexpr size_t SgirCpuTargetListShift = 16; + using ams::kern::arch::arm::GicDistributor; + using ams::kern::arch::arm::GicCpuInterface; + using ams::kern::arch::arm::KInterruptController; - enum SgirTargetListFilter : u32 { - SgirTargetListFilter_CpuTargetList = (0 << 24), - SgirTargetListFilter_Others = (1 << 24), - SgirTargetListFilter_Self = (2 << 24), - SgirTargetListFilter_Reserved = (3 << 24), - }; - }; - static_assert(util::is_pod::value); - static_assert(sizeof(GicDistributor) == 0x1000); + } - struct GicCpuInterface { - u32 ctlr; - u32 pmr; - u32 bpr; - u32 iar; - u32 eoir; - u32 rpr; - u32 hppir; - u32 abpr; - u32 aiar; - u32 aeoir; - u32 ahppir; - u32 statusr; - u32 reserved_30[4]; - u32 impldef_40[36]; - u32 apr[4]; - u32 nsapr[4]; - u32 reserved_f0[3]; - u32 iidr; - u32 reserved_100[960]; - u32 dir; - u32 _0x1004[1023]; - }; - static_assert(util::is_pod::value); - static_assert(sizeof(GicCpuInterface) == 0x2000); +#else - struct KInterruptController { - NON_COPYABLE(KInterruptController); - NON_MOVEABLE(KInterruptController); - public: - static constexpr s32 NumSoftwareInterrupts = 16; - static constexpr s32 NumLocalInterrupts = NumSoftwareInterrupts + 16; - static constexpr s32 NumGlobalInterrupts = 988; - static constexpr s32 NumInterrupts = NumLocalInterrupts + NumGlobalInterrupts; - static constexpr s32 NumPriorityLevels = 4; - public: - struct LocalState { - u32 isenabler[NumLocalInterrupts / 32]; - u32 ipriorityr[NumLocalInterrupts / 4]; - u32 itargetsr[NumLocalInterrupts / 4]; - u32 icfgr[NumLocalInterrupts / 16]; - }; + #error "Unknown board for KInterruptController" - struct GlobalState { - u32 isenabler[NumGlobalInterrupts / 32]; - u32 ipriorityr[NumGlobalInterrupts / 4]; - u32 itargetsr[NumGlobalInterrupts / 4]; - u32 icfgr[NumGlobalInterrupts / 16]; - }; - - enum PriorityLevel : u8 { - PriorityLevel_High = 0, - PriorityLevel_Low = NumPriorityLevels - 1, - - PriorityLevel_Timer = 1, - PriorityLevel_Scheduler = 2, - }; - private: - static inline u32 s_mask[cpu::NumCores]; - private: - volatile GicDistributor *gicd; - volatile GicCpuInterface *gicc; - public: - constexpr KInterruptController() : gicd(nullptr), gicc(nullptr) { /* ... */ } - - void Initialize(s32 core_id); - void Finalize(s32 core_id); - - void SaveCoreLocal(LocalState *state) const; - void SaveGlobal(GlobalState *state) const; - void RestoreCoreLocal(const LocalState *state) const; - void RestoreGlobal(const GlobalState *state) const; - public: - u32 GetIrq() const { - return this->gicc->iar; - } - - static constexpr s32 ConvertRawIrq(u32 irq) { - return (irq == 0x3FF) ? -1 : (irq & 0x3FF); - } - - void Enable(s32 irq) const { - this->gicd->isenabler[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32))); - } - - void Disable(s32 irq) const { - this->gicd->icenabler[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32))); - } - - void Clear(s32 irq) const { - this->gicd->icpendr[irq / BITSIZEOF(u32)] = (1u << (irq % BITSIZEOF(u32))); - } - - void SetTarget(s32 irq, s32 core_id) const { - this->gicd->itargetsr.bytes[irq] = this->gicd->itargetsr.bytes[irq] | GetGicMask(core_id); - } - - void ClearTarget(s32 irq, s32 core_id) const { - this->gicd->itargetsr.bytes[irq] = this->gicd->itargetsr.bytes[irq] & ~GetGicMask(core_id); - } - - void SetPriorityLevel(s32 irq, s32 level) const { - MESOSPHERE_ASSERT(PriorityLevel_High <= level && level <= PriorityLevel_Low); - this->gicd->ipriorityr.bytes[irq] = ToGicPriorityValue(level); - } - - s32 GetPriorityLevel(s32 irq) const { - return FromGicPriorityValue(this->gicd->ipriorityr.bytes[irq]); - } - - void SetPriorityLevel(s32 level) const { - MESOSPHERE_ASSERT(PriorityLevel_High <= level && level <= PriorityLevel_Low); - this->gicc->pmr = ToGicPriorityValue(level); - } - - void SetEdge(s32 irq) const { - u32 cfg = this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)]; - cfg &= ~(0x3 << (2 * (irq % (BITSIZEOF(u32) / 2)))); - cfg |= (0x2 << (2 * (irq % (BITSIZEOF(u32) / 2)))); - this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)] = cfg; - } - - void SetLevel(s32 irq) const { - u32 cfg = this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)]; - cfg &= ~(0x3 << (2 * (irq % (BITSIZEOF(u32) / 2)))); - cfg |= (0x0 << (2 * (irq % (BITSIZEOF(u32) / 2)))); - this->gicd->icfgr[irq / (BITSIZEOF(u32) / 2)] = cfg; - } - - void SendInterProcessorInterrupt(s32 irq, u64 core_mask) { - MESOSPHERE_ASSERT(IsSoftware(irq)); - this->gicd->sgir = GetCpuTargetListMask(irq, core_mask); - } - - void SendInterProcessorInterrupt(s32 irq) { - MESOSPHERE_ASSERT(IsSoftware(irq)); - this->gicd->sgir = GicDistributor::SgirTargetListFilter_Others | irq; - } - - void EndOfInterrupt(u32 irq) const { - this->gicc->eoir = irq; - } - - bool IsInterruptDefined(s32 irq) const { - const s32 num_interrupts = std::min(32 + 32 * (this->gicd->typer & 0x1F), static_cast(NumInterrupts)); - return (0 <= irq && irq < num_interrupts); - } - public: - static constexpr ALWAYS_INLINE bool IsSoftware(s32 id) { - MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts); - return id < NumSoftwareInterrupts; - } - - static constexpr ALWAYS_INLINE bool IsLocal(s32 id) { - MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts); - return id < NumLocalInterrupts; - } - - static constexpr ALWAYS_INLINE bool IsGlobal(s32 id) { - MESOSPHERE_ASSERT(0 <= id && id < NumInterrupts); - return NumLocalInterrupts <= id; - } - - static constexpr size_t GetGlobalInterruptIndex(s32 id) { - MESOSPHERE_ASSERT(IsGlobal(id)); - return id - NumLocalInterrupts; - } - - static constexpr size_t GetLocalInterruptIndex(s32 id) { - MESOSPHERE_ASSERT(IsLocal(id)); - return id; - } - private: - static constexpr size_t PriorityShift = BITSIZEOF(u8) - __builtin_ctz(NumPriorityLevels); - static_assert(PriorityShift < BITSIZEOF(u8)); - - static constexpr ALWAYS_INLINE u8 ToGicPriorityValue(s32 level) { - return (level << PriorityShift) | ((1 << PriorityShift) - 1); - } - - static constexpr ALWAYS_INLINE s32 FromGicPriorityValue(u8 priority) { - return (priority >> PriorityShift) & (NumPriorityLevels - 1); - } - - static constexpr ALWAYS_INLINE s32 GetCpuTargetListMask(s32 irq, u64 core_mask) { - MESOSPHERE_ASSERT(IsSoftware(irq)); - MESOSPHERE_ASSERT(core_mask < (1ul << cpu::NumCores)); - return GicDistributor::SgirTargetListFilter_CpuTargetList | irq | (static_cast(core_mask) << GicDistributor::SgirCpuTargetListShift); - } - - static ALWAYS_INLINE s32 GetGicMask(s32 core_id) { - return s_mask[core_id]; - } - - ALWAYS_INLINE void SetGicMask(s32 core_id) const { - s_mask[core_id] = this->gicd->itargetsr.bytes[0]; - } - - NOINLINE void SetupInterruptLines(s32 core_id) const; - }; -} +#endif diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_memory_region_device_types.inc b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_memory_region_device_types.inc new file mode 100644 index 000000000..65f6fe8cd --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_memory_region_device_types.inc @@ -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 . + */ + +/* All architectures must define NumArchitectureDeviceRegions. */ +constexpr inline const auto NumArchitectureDeviceRegions = 3; + +constexpr inline const auto KMemoryRegionType_Uart = KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 0); +constexpr inline const auto KMemoryRegionType_InterruptCpuInterface = KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 1).SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_InterruptDistributor = KMemoryRegionType_ArchDeviceBase.DeriveSparse(0, NumArchitectureDeviceRegions, 2).SetAttribute(KMemoryRegionAttr_NoUserMap); +static_assert(KMemoryRegionType_Uart .GetValue() == (0x1D)); +static_assert(KMemoryRegionType_InterruptCpuInterface.GetValue() == (0x2D | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_InterruptDistributor .GetValue() == (0x4D | KMemoryRegionAttr_NoUserMap)); diff --git a/libraries/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp b/libraries/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp new file mode 100644 index 000000000..e87148680 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/board/generic/kern_k_device_page_table.hpp @@ -0,0 +1,47 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::kern::board::generic { + + using KDeviceVirtualAddress = u64; + + class KDevicePageTable { + public: + constexpr KDevicePageTable() { /* ... */ } + + Result ALWAYS_INLINE Initialize(u64 space_address, u64 space_size) { return ams::kern::svc::ResultNotImplemented(); } + void ALWAYS_INLINE Finalize() { /* ... */ } + + Result ALWAYS_INLINE Attach(ams::svc::DeviceName device_name, u64 space_address, u64 space_size) { return ams::kern::svc::ResultNotImplemented(); } + Result ALWAYS_INLINE Detach(ams::svc::DeviceName device_name) { return ams::kern::svc::ResultNotImplemented(); } + + Result ALWAYS_INLINE Map(size_t *out_mapped_size, const KPageGroup &pg, KDeviceVirtualAddress device_address, ams::svc::MemoryPermission device_perm, bool refresh_mappings) { return ams::kern::svc::ResultNotImplemented(); } + Result ALWAYS_INLINE Unmap(const KPageGroup &pg, KDeviceVirtualAddress device_address) { return ams::kern::svc::ResultNotImplemented(); } + public: + static ALWAYS_INLINE void Initialize() { /* ... */ } + + static ALWAYS_INLINE void Lock() { /* ... */ } + static ALWAYS_INLINE void Unlock() { /* ... */ } + static ALWAYS_INLINE void Sleep() { /* ... */ } + static ALWAYS_INLINE void Wakeup() { /* ... */ } + }; + +} \ No newline at end of file diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp index 98b110944..b00ed7d7c 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_device_page_table.hpp @@ -36,11 +36,13 @@ namespace ams::kern::board::nintendo::nx { u32 hs_detached_value; private: static ALWAYS_INLINE bool IsHeapVirtualAddress(KVirtualAddress addr) { - return KMemoryLayout::IsHeapVirtualAddress(nullptr, addr); + const KMemoryRegion *hint = nullptr; + return KMemoryLayout::IsHeapVirtualAddress(hint, addr); } static ALWAYS_INLINE bool IsHeapPhysicalAddress(KPhysicalAddress addr) { - return KMemoryLayout::IsHeapPhysicalAddress(nullptr, addr); + const KMemoryRegion *hint = nullptr; + return KMemoryLayout::IsHeapPhysicalAddress(hint, addr); } static ALWAYS_INLINE KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress addr) { diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.board.nintendo_nx.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp similarity index 87% rename from libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.board.nintendo_nx.hpp rename to libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp index beb1775f9..7ddb4a62b 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.board.nintendo_nx.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_layout.hpp @@ -18,6 +18,7 @@ namespace ams::kern { - constexpr inline size_t MainMemorySize = 4_GB; + constexpr inline size_t MainMemorySize = 4_GB; + constexpr inline size_t MainMemorySizeMax = 8_GB; } diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_region_device_types.inc b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_region_device_types.inc new file mode 100644 index 000000000..7fc77b6c1 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_memory_region_device_types.inc @@ -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 . + */ + +/* All architectures must define NumBoardDeviceRegions. */ +constexpr inline const auto NumBoardDeviceRegions = 6; + /* UNUSED: .Derive(NumBoardDeviceRegions, 0); */ +constexpr inline const auto KMemoryRegionType_MemoryController = KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 1).SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_MemoryController1 = KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 2).SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_MemoryController0 = KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 3).SetAttribute(KMemoryRegionAttr_NoUserMap); +constexpr inline const auto KMemoryRegionType_PowerManagementController = KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 4).DeriveTransition(); +constexpr inline const auto KMemoryRegionType_LegacyLpsDevices = KMemoryRegionType_BoardDeviceBase.Derive(NumBoardDeviceRegions, 5); +static_assert(KMemoryRegionType_MemoryController .GetValue() == (0x55 | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_MemoryController1 .GetValue() == (0x65 | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_MemoryController0 .GetValue() == (0x95 | KMemoryRegionAttr_NoUserMap)); +static_assert(KMemoryRegionType_PowerManagementController.GetValue() == (0x1A5)); + +static_assert(KMemoryRegionType_LegacyLpsDevices.GetValue() == 0xC5); + +constexpr inline const auto NumLegacyLpsDevices = 7; +constexpr inline const auto KMemoryRegionType_LegacyLpsExceptionVectors = KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 0); +constexpr inline const auto KMemoryRegionType_LegacyLpsIram = KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 1); +constexpr inline const auto KMemoryRegionType_LegacyLpsFlowController = KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 2); +constexpr inline const auto KMemoryRegionType_LegacyLpsPrimaryICtlr = KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 3); +constexpr inline const auto KMemoryRegionType_LegacyLpsSemaphore = KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 4); +constexpr inline const auto KMemoryRegionType_LegacyLpsAtomics = KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 5); +constexpr inline const auto KMemoryRegionType_LegacyLpsClkRst = KMemoryRegionType_LegacyLpsDevices.Derive(NumLegacyLpsDevices, 6); +static_assert(KMemoryRegionType_LegacyLpsExceptionVectors.GetValue() == 0x3C5); +static_assert(KMemoryRegionType_LegacyLpsIram .GetValue() == 0x5C5); +static_assert(KMemoryRegionType_LegacyLpsFlowController .GetValue() == 0x6C5); +static_assert(KMemoryRegionType_LegacyLpsPrimaryICtlr .GetValue() == 0x9C5); +static_assert(KMemoryRegionType_LegacyLpsSemaphore .GetValue() == 0xAC5); +static_assert(KMemoryRegionType_LegacyLpsAtomics .GetValue() == 0xCC5); +static_assert(KMemoryRegionType_LegacyLpsClkRst .GetValue() == 0x11C5); diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index 080cc8c32..c72015d01 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp @@ -39,7 +39,7 @@ namespace ams::kern::board::nintendo::nx { /* Initialization. */ static NOINLINE void InitializePhase1(); static NOINLINE void InitializePhase2(); - static NOINLINE u32 GetInitialProcessBinaryPool(); + static NOINLINE u32 GetCreateProcessMemoryPool(); /* Randomness. */ static void GenerateRandomBytes(void *dst, size_t size); @@ -63,7 +63,7 @@ namespace ams::kern::board::nintendo::nx { /* Power management. */ static void SleepSystem(); - static NORETURN void StopSystem(); + static NORETURN void StopSystem(void *arg = nullptr); /* User access. */ static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args); diff --git a/libraries/libmesosphere/include/mesosphere/init/kern_init_arguments_select.hpp b/libraries/libmesosphere/include/mesosphere/init/kern_init_arguments_select.hpp index 09360267f..46190b021 100644 --- a/libraries/libmesosphere/include/mesosphere/init/kern_init_arguments_select.hpp +++ b/libraries/libmesosphere/include/mesosphere/init/kern_init_arguments_select.hpp @@ -26,6 +26,5 @@ namespace ams::kern::init { KPhysicalAddress GetInitArgumentsAddress(s32 core_id); void SetInitArguments(s32 core_id, KPhysicalAddress address, uintptr_t arg); - void StoreInitArguments(); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp new file mode 100644 index 000000000..6f026f721 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp @@ -0,0 +1,32 @@ +/* + * 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 . + */ +#pragma once + +#if defined(AMS_BUILD_FOR_AUDITING) +#define MESOSPHERE_BUILD_FOR_AUDITING +#endif + +#if defined(MESOSPHERE_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) +#define MESOSPHERE_BUILD_FOR_DEBUGGING +#endif + +#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING +#define MESOSPHERE_ENABLE_ASSERTIONS +#define MESOSPHERE_ENABLE_DEBUG_PRINT +#endif + +//#define MESOSPHERE_BUILD_FOR_TRACING +#define MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP diff --git a/libraries/libmesosphere/include/mesosphere/kern_common.hpp b/libraries/libmesosphere/include/mesosphere/kern_common.hpp index e27cfc96e..091d4ba68 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_common.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_common.hpp @@ -15,26 +15,19 @@ */ #pragma once #include +#include +#include namespace ams::kern { constexpr size_t PageSize = 4_KB; +#ifdef ATMOSPHERE_BOARD_NINTENDO_NX ams::TargetFirmware GetTargetFirmware(); +#else + consteval ALWAYS_INLINE ams::TargetFirmware GetTargetFirmware() { + return ams::TargetFirmware_Current; + } +#endif } - -#if 1 || defined(AMS_BUILD_FOR_AUDITING) -#define MESOSPHERE_BUILD_FOR_AUDITING -#endif - -#if defined(MESOSPHERE_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) -#define MESOSPHERE_BUILD_FOR_DEBUGGING -#endif - -#ifdef MESOSPHERE_BUILD_FOR_DEBUGGING -#define MESOSPHERE_ENABLE_ASSERTIONS -#define MESOSPHERE_ENABLE_DEBUG_PRINT -#endif - -#include diff --git a/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp b/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp index dfabbaa05..e2cde1342 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_debug_log.hpp @@ -40,7 +40,7 @@ namespace ams::kern { #ifndef MESOSPHERE_DEBUG_LOG_SELECTED #ifdef ATMOSPHERE_BOARD_NINTENDO_NX - #define MESOSPHERE_DEBUG_LOG_USE_UART_C + #define MESOSPHERE_DEBUG_LOG_USE_UART_A #else #error "Unknown board for Default Debug Log Source" #endif diff --git a/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp index e5e6c2f40..bc15b0b88 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_initial_process.hpp @@ -20,7 +20,7 @@ namespace ams::kern { constexpr u32 InitialProcessBinaryMagic = util::FourCC<'I','N','I','1'>::Code; - constexpr size_t InitialProcessBinarySizeMax = 0xC00000; + constexpr size_t InitialProcessBinarySizeMax = 12_MB; struct InitialProcessBinaryHeader { u32 magic; @@ -34,5 +34,6 @@ namespace ams::kern { u64 GetInitialProcessIdMin(); u64 GetInitialProcessIdMax(); + size_t GetInitialProcessesSecureMemorySize(); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp index 9c590e3db..7c9863049 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_capabilities.hpp @@ -354,6 +354,10 @@ namespace ams::kern { constexpr bool CanForceDebug() const { return this->debug_capabilities.Get(); } + + constexpr u32 GetIntendedKernelMajorVersion() const { return this->intended_kernel_version.Get(); } + constexpr u32 GetIntendedKernelMinorVersion() const { return this->intended_kernel_version.Get(); } + constexpr u32 GetIntendedKernelVersion() const { return ams::svc::EncodeKernelVersion(this->GetIntendedKernelMajorVersion(), this->GetIntendedKernelMinorVersion()); } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp index 953e247e1..2725100fe 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_client_session.hpp @@ -35,7 +35,7 @@ namespace ams::kern { } virtual void Destroy() override; - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } constexpr KSession *GetParent() const { return this->parent; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp index 29467b701..8fcbe4a17 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_code_memory.hpp @@ -46,7 +46,7 @@ namespace ams::kern { Result UnmapFromOwner(KProcessAddress address, size_t size); virtual bool IsInitialized() const override { return this->is_initialized; } - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } KProcess *GetOwner() const { return this->owner; } KProcessAddress GetSourceAddress() { return this->address; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_device_address_space.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_device_address_space.hpp index 03b302f9a..42e72a71e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_device_address_space.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_device_address_space.hpp @@ -37,7 +37,7 @@ namespace ams::kern { virtual void Finalize() override; virtual bool IsInitialized() const override { return this->is_initialized; } - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } Result Attach(ams::svc::DeviceName device_name); Result Detach(ams::svc::DeviceName device_name); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_page_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_page_manager.hpp index cc772876b..0943e32ab 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_page_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_dynamic_page_manager.hpp @@ -46,9 +46,9 @@ namespace ams::kern { /* We need to have positive size. */ R_UNLESS(sz > 0, svc::ResultOutOfMemory()); - /* Calculate metadata overhead. */ - const size_t metadata_size = KPageBitmap::CalculateMetadataOverheadSize(sz / sizeof(PageBuffer)); - const size_t allocatable_size = sz - metadata_size; + /* Calculate management overhead. */ + const size_t management_size = KPageBitmap::CalculateManagementOverheadSize(sz / sizeof(PageBuffer)); + const size_t allocatable_size = sz - management_size; /* Set tracking fields. */ this->address = memory; @@ -56,12 +56,12 @@ namespace ams::kern { this->count = allocatable_size / sizeof(PageBuffer); R_UNLESS(this->count > 0, svc::ResultOutOfMemory()); - /* Clear the metadata region. */ - u64 *metadata_ptr = GetPointer(this->address + allocatable_size); - std::memset(metadata_ptr, 0, metadata_size); + /* Clear the management region. */ + u64 *management_ptr = GetPointer(this->address + allocatable_size); + std::memset(management_ptr, 0, management_size); /* Initialize the bitmap. */ - this->page_bitmap.Initialize(metadata_ptr, this->count); + this->page_bitmap.Initialize(management_ptr, this->count); /* Free the pages to the bitmap. */ std::memset(GetPointer(this->address), 0, this->count * sizeof(PageBuffer)); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp index 326994b10..5231f805f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_interrupt_event.hpp @@ -41,7 +41,7 @@ namespace ams::kern { virtual bool IsInitialized() const override { return this->is_initialized; } - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } constexpr s32 GetInterruptId() const { return this->interrupt_id; } }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp index f8c429bce..93eb7ae77 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_client_session.hpp @@ -35,7 +35,7 @@ namespace ams::kern { } virtual void Destroy() override; - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } constexpr const KLightSession *GetParent() const { return this->parent; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp index 1487a714d..7dd58c031 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_light_server_session.hpp @@ -41,7 +41,7 @@ namespace ams::kern { } virtual void Destroy() override; - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } constexpr const KLightSession *GetParent() const { return this->parent; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp index b5f4dbd3f..09a761db1 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_block.hpp @@ -197,6 +197,7 @@ namespace ams::kern { .perm = static_cast(this->perm & KMemoryPermission_UserMask), .ipc_refcount = this->ipc_lock_count, .device_refcount = this->device_use_count, + .padding = {}, }; } @@ -396,6 +397,9 @@ namespace ams::kern { } constexpr void ShareToDevice(KMemoryPermission new_perm) { + /* New permission isn't used. */ + MESOSPHERE_UNUSED(new_perm); + /* We must either be shared or have a zero lock count. */ MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared || this->device_use_count == 0); @@ -407,6 +411,9 @@ namespace ams::kern { } constexpr void UnshareToDevice(KMemoryPermission new_perm) { + /* New permission isn't used. */ + MESOSPHERE_UNUSED(new_perm); + /* We must be shared. */ MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_DeviceShared) == KMemoryAttribute_DeviceShared); @@ -439,6 +446,9 @@ namespace ams::kern { } constexpr void UnlockForIpc(KMemoryPermission new_perm) { + /* New permission isn't used. */ + MESOSPHERE_UNUSED(new_perm); + /* We must be locked. */ MESOSPHERE_ASSERT((this->attribute & KMemoryAttribute_IpcLocked) == KMemoryAttribute_IpcLocked); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index d70485b62..9313c69b8 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp @@ -16,9 +16,10 @@ #pragma once #include #include +#include #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) - #include + #include #else #error "Unknown board for KMemoryLayout" #endif @@ -39,657 +40,136 @@ namespace ams::kern { constexpr size_t KernelPhysicalAddressSpaceLast = KernelPhysicalAddressSpaceEnd - 1ul; constexpr size_t KernelPhysicalAddressSpaceSize = KernelPhysicalAddressSpaceEnd - KernelPhysicalAddressSpaceBase; - enum KMemoryRegionType : u32 { - KMemoryRegionAttr_CarveoutProtected = 0x04000000, - KMemoryRegionAttr_DidKernelMap = 0x08000000, - KMemoryRegionAttr_ShouldKernelMap = 0x10000000, - KMemoryRegionAttr_UserReadOnly = 0x20000000, - KMemoryRegionAttr_NoUserMap = 0x40000000, - KMemoryRegionAttr_LinearMapped = 0x80000000, + constexpr size_t KernelPageTableHeapSize = init::KInitialPageTable::GetMaximumOverheadSize(kern::MainMemorySizeMax); + constexpr size_t KernelInitialPageHeapSize = 128_KB; - KMemoryRegionType_None = 0, - KMemoryRegionType_Kernel = 1, - KMemoryRegionType_Dram = 2, - KMemoryRegionType_CoreLocal = 4, + constexpr size_t KernelSlabHeapDataSize = 5_MB; + constexpr size_t KernelSlabHeapGapsSize = 2_MB - 64_KB; + constexpr size_t KernelSlabHeapGapsSizeDeprecated = 2_MB; + constexpr size_t KernelSlabHeapSize = KernelSlabHeapDataSize + KernelSlabHeapGapsSize; - KMemoryRegionType_VirtualKernelPtHeap = 0x2A, - KMemoryRegionType_VirtualKernelTraceBuffer = 0x4A, - KMemoryRegionType_VirtualKernelInitPt = 0x19A, + /* NOTE: This is calculated from KThread slab counts, assuming KThread size <= 0x860. */ + constexpr size_t KernelSlabHeapAdditionalSize = 0x68000; - KMemoryRegionType_VirtualDramMetadataPool = 0x29A, - KMemoryRegionType_VirtualDramManagedPool = 0x31A, - KMemoryRegionType_VirtualDramApplicationPool = 0x271A, - KMemoryRegionType_VirtualDramAppletPool = 0x1B1A, - KMemoryRegionType_VirtualDramSystemPool = 0x2B1A, - KMemoryRegionType_VirtualDramSystemNonSecurePool = 0x331A, - - KMemoryRegionType_Uart = 0x1D, - KMemoryRegionType_InterruptDistributor = 0x4D | KMemoryRegionAttr_NoUserMap, - KMemoryRegionType_InterruptCpuInterface = 0x2D | KMemoryRegionAttr_NoUserMap, - - KMemoryRegionType_MemoryController = 0x55, - KMemoryRegionType_MemoryController0 = 0x95, - KMemoryRegionType_MemoryController1 = 0x65, - KMemoryRegionType_PowerManagementController = 0x1A5, - - KMemoryRegionType_KernelAutoMap = KMemoryRegionType_Kernel | KMemoryRegionAttr_ShouldKernelMap, - - KMemoryRegionType_KernelTemp = 0x31, - - KMemoryRegionType_KernelCode = 0x19, - KMemoryRegionType_KernelStack = 0x29, - KMemoryRegionType_KernelMisc = 0x49, - KMemoryRegionType_KernelSlab = 0x89, - - KMemoryRegionType_KernelMiscMainStack = 0xB49, - KMemoryRegionType_KernelMiscMappedDevice = 0xD49, - KMemoryRegionType_KernelMiscIdleStack = 0x1349, - KMemoryRegionType_KernelMiscUnknownDebug = 0x1549, - KMemoryRegionType_KernelMiscExceptionStack = 0x2349, - - KMemoryRegionType_DramLinearMapped = KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped, - - KMemoryRegionType_DramReservedEarly = 0x16 | KMemoryRegionAttr_NoUserMap, - KMemoryRegionType_DramPoolPartition = 0x26 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, - KMemoryRegionType_DramMetadataPool = 0x166 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_CarveoutProtected, - - KMemoryRegionType_DramNonKernel = 0x1A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, - - KMemoryRegionType_DramApplicationPool = 0x7A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, - KMemoryRegionType_DramAppletPool = 0xBA6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, - KMemoryRegionType_DramSystemNonSecurePool = 0xDA6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped, - KMemoryRegionType_DramSystemPool = 0x13A6 | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_CarveoutProtected, - - - - KMemoryRegionType_DramKernel = 0xE | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected, - KMemoryRegionType_DramKernelCode = 0xCE | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected, - KMemoryRegionType_DramKernelSlab = 0x14E | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected, - KMemoryRegionType_DramKernelPtHeap = 0x24E | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_LinearMapped, - KMemoryRegionType_DramKernelInitPt = 0x44E | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_LinearMapped, - - /* These regions aren't normally mapped in retail kernel. */ - KMemoryRegionType_KernelTraceBuffer = 0xA6 | KMemoryRegionAttr_UserReadOnly | KMemoryRegionAttr_LinearMapped, - KMemoryRegionType_OnMemoryBootImage = 0x156, - KMemoryRegionType_DTB = 0x256, - }; - - constexpr ALWAYS_INLINE KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { - if (type_id == (type_id | KMemoryRegionType_KernelTraceBuffer)) { - return KMemoryRegionType_VirtualKernelTraceBuffer; - } else if (type_id == (type_id | KMemoryRegionType_DramKernelPtHeap)) { - return KMemoryRegionType_VirtualKernelPtHeap; - } else { - return KMemoryRegionType_Dram; - } - } - - class KMemoryRegion : public util::IntrusiveRedBlackTreeBaseNode { - NON_COPYABLE(KMemoryRegion); - NON_MOVEABLE(KMemoryRegion); - private: - uintptr_t address; - uintptr_t pair_address; - size_t region_size; - u32 attributes; - u32 type_id; - public: - static constexpr ALWAYS_INLINE int Compare(const KMemoryRegion &lhs, const KMemoryRegion &rhs) { - if (lhs.GetAddress() < rhs.GetAddress()) { - return -1; - } else if (lhs.GetAddress() <= rhs.GetLastAddress()) { - return 0; - } else { - return 1; - } - } - public: - constexpr ALWAYS_INLINE KMemoryRegion() : address(0), pair_address(0), region_size(0), attributes(0), type_id(0) { /* ... */ } - constexpr ALWAYS_INLINE KMemoryRegion(uintptr_t a, size_t rs, uintptr_t p, u32 r, u32 t) : - address(a), pair_address(p), region_size(rs), attributes(r), type_id(t) - { - /* ... */ - } - constexpr ALWAYS_INLINE KMemoryRegion(uintptr_t a, size_t rs, u32 r, u32 t) : KMemoryRegion(a, rs, std::numeric_limits::max(), r, t) { /* ... */ } - - constexpr ALWAYS_INLINE uintptr_t GetAddress() const { - return this->address; - } - - constexpr ALWAYS_INLINE uintptr_t GetPairAddress() const { - return this->pair_address; - } - - constexpr ALWAYS_INLINE size_t GetSize() const { - return this->region_size; - } - - constexpr ALWAYS_INLINE uintptr_t GetEndAddress() const { - return this->GetAddress() + this->GetSize(); - } - - constexpr ALWAYS_INLINE uintptr_t GetLastAddress() const { - return this->GetEndAddress() - 1; - } - - constexpr ALWAYS_INLINE u32 GetAttributes() const { - return this->attributes; - } - - constexpr ALWAYS_INLINE u32 GetType() const { - return this->type_id; - } - - constexpr ALWAYS_INLINE void SetType(u32 type) { - MESOSPHERE_INIT_ABORT_UNLESS(this->CanDerive(type)); - this->type_id = type; - } - - constexpr ALWAYS_INLINE bool Contains(uintptr_t address) const { - return this->GetAddress() <= address && address <= this->GetLastAddress(); - } - - constexpr ALWAYS_INLINE bool IsDerivedFrom(u32 type) const { - return (this->GetType() | type) == this->GetType(); - } - - constexpr ALWAYS_INLINE bool HasTypeAttribute(KMemoryRegionType attr) const { - return (this->GetType() | attr) == this->GetType(); - } - - constexpr ALWAYS_INLINE bool CanDerive(u32 type) const { - return (this->GetType() | type) == type; - } - - constexpr ALWAYS_INLINE void SetPairAddress(uintptr_t a) { - this->pair_address = a; - } - - constexpr ALWAYS_INLINE void SetTypeAttribute(KMemoryRegionType attr) { - this->type_id |= attr; - } - }; - static_assert(std::is_trivially_destructible::value); - - class KMemoryRegionTree { - public: - struct DerivedRegionExtents { - const KMemoryRegion *first_region; - const KMemoryRegion *last_region; - - constexpr DerivedRegionExtents() : first_region(nullptr), last_region(nullptr) { /* ... */ } - - constexpr ALWAYS_INLINE uintptr_t GetAddress() const { - return this->first_region->GetAddress(); - } - - constexpr ALWAYS_INLINE uintptr_t GetEndAddress() const { - return this->last_region->GetEndAddress(); - } - - constexpr ALWAYS_INLINE size_t GetSize() const { - return this->GetEndAddress() - this->GetAddress(); - } - - constexpr ALWAYS_INLINE uintptr_t GetLastAddress() const { - return this->GetEndAddress() - 1; - } - }; - private: - using TreeType = util::IntrusiveRedBlackTreeBaseTraits::TreeType; - public: - using value_type = TreeType::value_type; - using size_type = TreeType::size_type; - using difference_type = TreeType::difference_type; - using pointer = TreeType::pointer; - using const_pointer = TreeType::const_pointer; - using reference = TreeType::reference; - using const_reference = TreeType::const_reference; - using iterator = TreeType::iterator; - using const_iterator = TreeType::const_iterator; - private: - TreeType tree; - public: - constexpr ALWAYS_INLINE KMemoryRegionTree() : tree() { /* ... */ } - public: - iterator FindContainingRegion(uintptr_t address) { - return this->find(KMemoryRegion(address, 1, 0, 0)); - } - - iterator FindFirstRegionByTypeAttr(u32 type_id, u32 attr = 0) { - for (auto it = this->begin(); it != this->end(); it++) { - if (it->GetType() == type_id && it->GetAttributes() == attr) { - return it; - } - } - MESOSPHERE_INIT_ABORT(); - } - - iterator FindFirstRegionByType(u32 type_id) { - for (auto it = this->begin(); it != this->end(); it++) { - if (it->GetType() == type_id) { - return it; - } - } - MESOSPHERE_INIT_ABORT(); - } - - iterator TryFindFirstRegionByType(u32 type_id) { - for (auto it = this->begin(); it != this->end(); it++) { - if (it->GetType() == type_id) { - return it; - } - } - - return this->end(); - } - - iterator FindFirstDerivedRegion(u32 type_id) { - for (auto it = this->begin(); it != this->end(); it++) { - if (it->IsDerivedFrom(type_id)) { - return it; - } - } - MESOSPHERE_INIT_ABORT(); - } - - iterator TryFindFirstDerivedRegion(u32 type_id) { - for (auto it = this->begin(); it != this->end(); it++) { - if (it->IsDerivedFrom(type_id)) { - return it; - } - } - - return this->end(); - } - - DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const { - DerivedRegionExtents extents; - - MESOSPHERE_INIT_ABORT_UNLESS(extents.first_region == nullptr); - MESOSPHERE_INIT_ABORT_UNLESS(extents.last_region == nullptr); - - for (auto it = this->cbegin(); it != this->cend(); it++) { - if (it->IsDerivedFrom(type_id)) { - if (extents.first_region == nullptr) { - extents.first_region = std::addressof(*it); - } - extents.last_region = std::addressof(*it); - } - } - - MESOSPHERE_INIT_ABORT_UNLESS(extents.first_region != nullptr); - MESOSPHERE_INIT_ABORT_UNLESS(extents.last_region != nullptr); - - return extents; - } - public: - NOINLINE bool Insert(uintptr_t address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0); - NOINLINE KVirtualAddress GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id); - - ALWAYS_INLINE KVirtualAddress GetRandomAlignedRegionWithGuard(size_t size, size_t alignment, u32 type_id, size_t guard_size) { - return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size; - } - public: - /* Iterator accessors. */ - iterator begin() { - return this->tree.begin(); - } - - const_iterator begin() const { - return this->tree.begin(); - } - - iterator end() { - return this->tree.end(); - } - - const_iterator end() const { - return this->tree.end(); - } - - const_iterator cbegin() const { - return this->begin(); - } - - const_iterator cend() const { - return this->end(); - } - - iterator iterator_to(reference ref) { - return this->tree.iterator_to(ref); - } - - const_iterator iterator_to(const_reference ref) const { - return this->tree.iterator_to(ref); - } - - /* Content management. */ - bool empty() const { - return this->tree.empty(); - } - - reference back() { - return this->tree.back(); - } - - const_reference back() const { - return this->tree.back(); - } - - reference front() { - return this->tree.front(); - } - - const_reference front() const { - return this->tree.front(); - } - - /* GCC over-eagerly inlines this operation. */ - NOINLINE iterator insert(reference ref) { - return this->tree.insert(ref); - } - - NOINLINE iterator erase(iterator it) { - return this->tree.erase(it); - } - - iterator find(const_reference ref) const { - return this->tree.find(ref); - } - - iterator nfind(const_reference ref) const { - return this->tree.nfind(ref); - } - }; - - class KMemoryRegionAllocator { - NON_COPYABLE(KMemoryRegionAllocator); - NON_MOVEABLE(KMemoryRegionAllocator); - public: - static constexpr size_t MaxMemoryRegions = 1000; - friend class KMemoryLayout; - private: - KMemoryRegion region_heap[MaxMemoryRegions]; - size_t num_regions; - private: - constexpr ALWAYS_INLINE KMemoryRegionAllocator() : region_heap(), num_regions() { /* ... */ } - public: - ALWAYS_INLINE KMemoryRegion *Allocate() { - /* Ensure we stay within the bounds of our heap. */ - MESOSPHERE_INIT_ABORT_UNLESS(this->num_regions < MaxMemoryRegions); - - return &this->region_heap[this->num_regions++]; - } - - template - ALWAYS_INLINE KMemoryRegion *Create(Args&&... args) { - KMemoryRegion *region = this->Allocate(); - new (region) KMemoryRegion(std::forward(args)...); - return region; - } - }; + constexpr size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + KernelSlabHeapSize; class KMemoryLayout { private: static /* constinit */ inline uintptr_t s_linear_phys_to_virt_diff; static /* constinit */ inline uintptr_t s_linear_virt_to_phys_diff; - static /* constinit */ inline KMemoryRegionAllocator s_region_allocator; static /* constinit */ inline KMemoryRegionTree s_virtual_tree; static /* constinit */ inline KMemoryRegionTree s_physical_tree; static /* constinit */ inline KMemoryRegionTree s_virtual_linear_tree; static /* constinit */ inline KMemoryRegionTree s_physical_linear_tree; private: - static ALWAYS_INLINE auto GetVirtualLinearExtents(const KMemoryRegionTree::DerivedRegionExtents physical) { - return KMemoryRegion(GetInteger(GetLinearVirtualAddress(physical.GetAddress())), physical.GetSize(), 0, KMemoryRegionType_None); + template requires IsKTypedAddress + static ALWAYS_INLINE bool IsTypedAddress(const KMemoryRegion *®ion, AddressType address, KMemoryRegionTree &tree, KMemoryRegionType type) { + /* Check if the cached region already contains the address. */ + if (region != nullptr && region->Contains(GetInteger(address))) { + return true; + } + + /* Find the containing region, and update the cache. */ + if (const KMemoryRegion *found = tree.Find(GetInteger(address)); found != nullptr && found->IsDerivedFrom(type)) { + region = found; + return true; + } else { + return false; + } + } + + template requires IsKTypedAddress + static ALWAYS_INLINE bool IsTypedAddress(const KMemoryRegion *®ion, AddressType address, size_t size, KMemoryRegionTree &tree, KMemoryRegionType type) { + /* Get the end of the checked region. */ + const uintptr_t last_address = GetInteger(address) + size - 1; + + /* Walk the tree to verify the region is correct. */ + const KMemoryRegion *cur = (region != nullptr && region->Contains(GetInteger(address))) ? region : tree.Find(GetInteger(address)); + while (cur != nullptr && cur->IsDerivedFrom(type)) { + if (last_address <= cur->GetLastAddress()) { + region = cur; + return true; + } + + cur = cur->GetNext(); + } + return false; + } + + template requires IsKTypedAddress + static ALWAYS_INLINE const KMemoryRegion *Find(AddressType address, const KMemoryRegionTree &tree) { + return tree.Find(GetInteger(address)); + } + + static ALWAYS_INLINE KMemoryRegion &Dereference(KMemoryRegion *region) { + MESOSPHERE_INIT_ABORT_UNLESS(region != nullptr); + return *region; + } + + static ALWAYS_INLINE const KMemoryRegion &Dereference(const KMemoryRegion *region) { + MESOSPHERE_INIT_ABORT_UNLESS(region != nullptr); + return *region; + } + + static ALWAYS_INLINE KVirtualAddress GetStackTopAddress(s32 core_id, KMemoryRegionType type) { + return Dereference(GetVirtualMemoryRegionTree().FindByTypeAndAttribute(type, static_cast(core_id))).GetEndAddress(); } public: - static ALWAYS_INLINE KMemoryRegionAllocator &GetMemoryRegionAllocator() { return s_region_allocator; } - static ALWAYS_INLINE KMemoryRegionTree &GetVirtualMemoryRegionTree() { return s_virtual_tree; } - static ALWAYS_INLINE KMemoryRegionTree &GetPhysicalMemoryRegionTree() { return s_physical_tree; } - static ALWAYS_INLINE KMemoryRegionTree &GetVirtualLinearMemoryRegionTree() { return s_virtual_linear_tree; } - static ALWAYS_INLINE KMemoryRegionTree &GetPhysicalLinearMemoryRegionTree() { return s_physical_linear_tree; } + static ALWAYS_INLINE KMemoryRegionTree &GetVirtualMemoryRegionTree() { return s_virtual_tree; } + static ALWAYS_INLINE KMemoryRegionTree &GetPhysicalMemoryRegionTree() { return s_physical_tree; } + static ALWAYS_INLINE KMemoryRegionTree &GetVirtualLinearMemoryRegionTree() { return s_virtual_linear_tree; } + static ALWAYS_INLINE KMemoryRegionTree &GetPhysicalLinearMemoryRegionTree() { return s_physical_linear_tree; } - static ALWAYS_INLINE KMemoryRegionTree::iterator GetEnd(KVirtualAddress) { - return GetVirtualLinearMemoryRegionTree().end(); - } + static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress address) { return GetInteger(address) + s_linear_phys_to_virt_diff; } + static ALWAYS_INLINE KPhysicalAddress GetLinearPhysicalAddress(KVirtualAddress address) { return GetInteger(address) + s_linear_virt_to_phys_diff; } - static ALWAYS_INLINE KMemoryRegionTree::iterator GetEnd(KPhysicalAddress) { - return GetPhysicalMemoryRegionTree().end(); - } + static NOINLINE const KMemoryRegion *Find(KVirtualAddress address) { return Find(address, GetVirtualMemoryRegionTree()); } + static NOINLINE const KMemoryRegion *Find(KPhysicalAddress address) { return Find(address, GetPhysicalMemoryRegionTree()); } - static NOINLINE KMemoryRegionTree::iterator FindContainingRegion(KVirtualAddress address) { - return GetVirtualMemoryRegionTree().FindContainingRegion(GetInteger(address)); - } + static NOINLINE const KMemoryRegion *FindLinear(KVirtualAddress address) { return Find(address, GetVirtualLinearMemoryRegionTree()); } + static NOINLINE const KMemoryRegion *FindLinear(KPhysicalAddress address) { return Find(address, GetPhysicalLinearMemoryRegionTree()); } - static NOINLINE KMemoryRegionTree::iterator FindContainingRegion(KPhysicalAddress address) { - return GetPhysicalMemoryRegionTree().FindContainingRegion(GetInteger(address)); - } + static NOINLINE KVirtualAddress GetMainStackTopAddress(s32 core_id) { return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscMainStack); } + static NOINLINE KVirtualAddress GetIdleStackTopAddress(s32 core_id) { return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscIdleStack); } + static NOINLINE KVirtualAddress GetExceptionStackTopAddress(s32 core_id) { return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack); } - static ALWAYS_INLINE KVirtualAddress GetLinearVirtualAddress(KPhysicalAddress address) { - return GetInteger(address) + s_linear_phys_to_virt_diff; - } + static NOINLINE KVirtualAddress GetSlabRegionAddress() { return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab)).GetAddress(); } + static NOINLINE KVirtualAddress GetCoreLocalRegionAddress() { return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_CoreLocalRegion)).GetAddress(); } - static ALWAYS_INLINE KPhysicalAddress GetLinearPhysicalAddress(KVirtualAddress address) { - return GetInteger(address) + s_linear_virt_to_phys_diff; - } + static NOINLINE const KMemoryRegion &GetDeviceRegion(KMemoryRegionType type) { return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type)); } + static KPhysicalAddress GetDevicePhysicalAddress(KMemoryRegionType type) { return GetDeviceRegion(type).GetAddress(); } + static KVirtualAddress GetDeviceVirtualAddress(KMemoryRegionType type) { return GetDeviceRegion(type).GetPairAddress(); } - static NOINLINE KVirtualAddress GetMainStackTopAddress(s32 core_id) { - return GetVirtualMemoryRegionTree().FindFirstRegionByTypeAttr(KMemoryRegionType_KernelMiscMainStack, static_cast(core_id))->GetEndAddress(); - } + static NOINLINE const KMemoryRegion &GetPoolManagementRegion() { return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramPoolManagement)); } + static NOINLINE const KMemoryRegion &GetPageTableHeapRegion() { return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelPtHeap)); } + static NOINLINE const KMemoryRegion &GetKernelStackRegion() { return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelStack)); } + static NOINLINE const KMemoryRegion &GetTempRegion() { return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelTemp)); } + static NOINLINE const KMemoryRegion &GetCoreLocalRegion() { return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_CoreLocalRegion)); } - static NOINLINE KVirtualAddress GetIdleStackTopAddress(s32 core_id) { - return GetVirtualMemoryRegionTree().FindFirstRegionByTypeAttr(KMemoryRegionType_KernelMiscIdleStack, static_cast(core_id))->GetEndAddress(); - } + static NOINLINE const KMemoryRegion &GetKernelTraceBufferRegion() { return Dereference(GetVirtualLinearMemoryRegionTree().FindByType(KMemoryRegionType_VirtualDramKernelTraceBuffer)); } - static NOINLINE KVirtualAddress GetExceptionStackTopAddress(s32 core_id) { - return GetVirtualMemoryRegionTree().FindFirstRegionByTypeAttr(KMemoryRegionType_KernelMiscExceptionStack, static_cast(core_id))->GetEndAddress(); - } + static NOINLINE const KMemoryRegion &GetVirtualLinearRegion(KVirtualAddress address) { return Dereference(FindLinear(address)); } - static NOINLINE KVirtualAddress GetSlabRegionAddress() { - return GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_KernelSlab)->GetAddress(); - } + static NOINLINE const KMemoryRegion *GetPhysicalKernelTraceBufferRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_KernelTraceBuffer); } + static NOINLINE const KMemoryRegion *GetPhysicalOnMemoryBootImageRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_OnMemoryBootImage); } + static NOINLINE const KMemoryRegion *GetPhysicalDTBRegion() { return GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DTB); } - static NOINLINE KVirtualAddress GetCoreLocalRegionAddress() { - return GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_CoreLocal)->GetAddress(); - } + static NOINLINE bool IsHeapPhysicalAddress(const KMemoryRegion *®ion, KPhysicalAddress address) { return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(), KMemoryRegionType_DramUserPool); } + static NOINLINE bool IsHeapVirtualAddress(const KMemoryRegion *®ion, KVirtualAddress address) { return IsTypedAddress(region, address, GetVirtualLinearMemoryRegionTree(), KMemoryRegionType_VirtualDramUserPool); } - static NOINLINE KVirtualAddress GetInterruptDistributorAddress() { - return GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_InterruptDistributor)->GetPairAddress(); - } + static NOINLINE bool IsHeapPhysicalAddress(const KMemoryRegion *®ion, KPhysicalAddress address, size_t size) { return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(), KMemoryRegionType_DramUserPool); } + static NOINLINE bool IsHeapVirtualAddress(const KMemoryRegion *®ion, KVirtualAddress address, size_t size) { return IsTypedAddress(region, address, size, GetVirtualLinearMemoryRegionTree(), KMemoryRegionType_VirtualDramUserPool); } - static NOINLINE KVirtualAddress GetInterruptCpuInterfaceAddress() { - return GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_InterruptCpuInterface)->GetPairAddress(); - } - - static NOINLINE KVirtualAddress GetUartAddress() { - return GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_Uart)->GetPairAddress(); - } - - static NOINLINE KMemoryRegion &GetMemoryControllerRegion() { - return *GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_MemoryController); - } - - static NOINLINE KMemoryRegion &GetMetadataPoolRegion() { - return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_VirtualDramMetadataPool); - } - - static NOINLINE KMemoryRegion &GetPageTableHeapRegion() { - return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_VirtualKernelPtHeap); - } - - static NOINLINE KMemoryRegion &GetKernelStackRegion() { - return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_KernelStack); - } - - static NOINLINE KMemoryRegion &GetTempRegion() { - return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_KernelTemp); - } - - static NOINLINE KMemoryRegion &GetVirtualLinearRegion(KVirtualAddress address) { - return *GetVirtualLinearMemoryRegionTree().FindContainingRegion(GetInteger(address)); - } - - static NOINLINE const KMemoryRegion *TryGetKernelTraceBufferRegion() { - auto &tree = GetPhysicalMemoryRegionTree(); - if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_KernelTraceBuffer); it != tree.end()) { - return std::addressof(*it); - } else { - return nullptr; - } - } - - static NOINLINE const KMemoryRegion *TryGetOnMemoryBootImageRegion() { - auto &tree = GetPhysicalMemoryRegionTree(); - if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_OnMemoryBootImage); it != tree.end()) { - return std::addressof(*it); - } else { - return nullptr; - } - } - - static NOINLINE const KMemoryRegion *TryGetDTBRegion() { - auto &tree = GetPhysicalMemoryRegionTree(); - if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_DTB); it != tree.end()) { - return std::addressof(*it); - } else { - return nullptr; - } - } - - static NOINLINE bool IsHeapPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, const KMemoryRegion *hint = nullptr) { - auto &tree = GetPhysicalLinearMemoryRegionTree(); - KMemoryRegionTree::const_iterator it = tree.end(); - if (hint != nullptr) { - it = tree.iterator_to(*hint); - } - if (it == tree.end() || !it->Contains(GetInteger(address))) { - it = tree.FindContainingRegion(GetInteger(address)); - } - if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_DramNonKernel)) { - if (out) { - *out = std::addressof(*it); - } - return true; - } - return false; - } - - static NOINLINE bool IsHeapPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, size_t size, const KMemoryRegion *hint = nullptr) { - auto &tree = GetPhysicalLinearMemoryRegionTree(); - KMemoryRegionTree::const_iterator it = tree.end(); - if (hint != nullptr) { - it = tree.iterator_to(*hint); - } - if (it == tree.end() || !it->Contains(GetInteger(address))) { - it = tree.FindContainingRegion(GetInteger(address)); - } - if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_DramNonKernel)) { - const uintptr_t last_address = GetInteger(address) + size - 1; - do { - if (last_address <= it->GetLastAddress()) { - if (out) { - *out = std::addressof(*it); - } - return true; - } - it++; - } while (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_DramNonKernel)); - } - return false; - } - - static NOINLINE bool IsLinearMappedPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, const KMemoryRegion *hint = nullptr) { - auto &tree = GetPhysicalLinearMemoryRegionTree(); - KMemoryRegionTree::const_iterator it = tree.end(); - if (hint != nullptr) { - it = tree.iterator_to(*hint); - } - if (it == tree.end() || !it->Contains(GetInteger(address))) { - it = tree.FindContainingRegion(GetInteger(address)); - } - if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionAttr_LinearMapped)) { - if (out) { - *out = std::addressof(*it); - } - return true; - } - return false; - } - - static NOINLINE bool IsLinearMappedPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, size_t size, const KMemoryRegion *hint = nullptr) { - auto &tree = GetPhysicalLinearMemoryRegionTree(); - KMemoryRegionTree::const_iterator it = tree.end(); - if (hint != nullptr) { - it = tree.iterator_to(*hint); - } - if (it == tree.end() || !it->Contains(GetInteger(address))) { - it = tree.FindContainingRegion(GetInteger(address)); - } - if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionAttr_LinearMapped)) { - const uintptr_t last_address = GetInteger(address) + size - 1; - do { - if (last_address <= it->GetLastAddress()) { - if (out) { - *out = std::addressof(*it); - } - return true; - } - it++; - } while (it != tree.end() && it->IsDerivedFrom(KMemoryRegionAttr_LinearMapped)); - } - return false; - } - - static NOINLINE bool IsHeapVirtualAddress(const KMemoryRegion **out, KVirtualAddress address, const KMemoryRegion *hint = nullptr) { - auto &tree = GetVirtualLinearMemoryRegionTree(); - KMemoryRegionTree::const_iterator it = tree.end(); - if (hint != nullptr) { - it = tree.iterator_to(*hint); - } - if (it == tree.end() || !it->Contains(GetInteger(address))) { - it = tree.FindContainingRegion(GetInteger(address)); - } - if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_VirtualDramManagedPool)) { - if (out) { - *out = std::addressof(*it); - } - return true; - } - return false; - } - - static NOINLINE bool IsHeapVirtualAddress(const KMemoryRegion **out, KVirtualAddress address, size_t size, const KMemoryRegion *hint = nullptr) { - auto &tree = GetVirtualLinearMemoryRegionTree(); - KMemoryRegionTree::const_iterator it = tree.end(); - if (hint != nullptr) { - it = tree.iterator_to(*hint); - } - if (it == tree.end() || !it->Contains(GetInteger(address))) { - it = tree.FindContainingRegion(GetInteger(address)); - } - if (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_VirtualDramManagedPool)) { - const uintptr_t last_address = GetInteger(address) + size - 1; - do { - if (last_address <= it->GetLastAddress()) { - if (out) { - *out = std::addressof(*it); - } - return true; - } - it++; - } while (it != tree.end() && it->IsDerivedFrom(KMemoryRegionType_VirtualDramManagedPool)); - } - return false; - } + static NOINLINE bool IsLinearMappedPhysicalAddress(const KMemoryRegion *®ion, KPhysicalAddress address) { return IsTypedAddress(region, address, GetPhysicalLinearMemoryRegionTree(), static_cast(KMemoryRegionAttr_LinearMapped)); } + static NOINLINE bool IsLinearMappedPhysicalAddress(const KMemoryRegion *®ion, KPhysicalAddress address, size_t size) { return IsTypedAddress(region, address, size, GetPhysicalLinearMemoryRegionTree(), static_cast(KMemoryRegionAttr_LinearMapped)); } static NOINLINE std::tuple GetTotalAndKernelMemorySizes() { size_t total_size = 0, kernel_size = 0; - for (auto it = GetPhysicalMemoryRegionTree().cbegin(); it != GetPhysicalMemoryRegionTree().cend(); it++) { - if (it->IsDerivedFrom(KMemoryRegionType_Dram)) { - total_size += it->GetSize(); - if (!it->IsDerivedFrom(KMemoryRegionType_DramNonKernel)) { - kernel_size += it->GetSize(); + for (const auto ®ion : GetPhysicalMemoryRegionTree()) { + if (region.IsDerivedFrom(KMemoryRegionType_Dram)) { + total_size += region.GetSize(); + if (!region.IsDerivedFrom(KMemoryRegionType_DramUserPool)) { + kernel_size += region.GetSize(); } } } @@ -697,86 +177,39 @@ namespace ams::kern { } static void InitializeLinearMemoryRegionTrees(KPhysicalAddress aligned_linear_phys_start, KVirtualAddress linear_virtual_start); + static size_t GetResourceRegionSizeForInit(); - static NOINLINE auto GetKernelRegionExtents() { - return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel); + static NOINLINE auto GetKernelRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Kernel); } + static NOINLINE auto GetKernelCodeRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode); } + static NOINLINE auto GetKernelStackRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelStack); } + static NOINLINE auto GetKernelMiscRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelMisc); } + static NOINLINE auto GetKernelSlabRegionExtents() { return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelSlab); } + + + static NOINLINE auto GetLinearRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionAttr_LinearMapped); } + + static NOINLINE auto GetLinearRegionVirtualExtents() { + auto physical = GetLinearRegionPhysicalExtents(); + return KMemoryRegion(GetInteger(GetLinearVirtualAddress(physical.GetAddress())), physical.GetSize(), 0, KMemoryRegionType_None); } - static NOINLINE auto GetKernelCodeRegionExtents() { - return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelCode); - } + static NOINLINE auto GetMainMemoryPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Dram); } + static NOINLINE auto GetCarveoutRegionExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionAttr_CarveoutProtected); } - static NOINLINE auto GetKernelStackRegionExtents() { - return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelStack); - } + static NOINLINE auto GetKernelRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelBase); } + static NOINLINE auto GetKernelCodeRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelCode); } + static NOINLINE auto GetKernelSlabRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelSlab); } + static NOINLINE auto GetKernelPageTableHeapRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelPtHeap); } + static NOINLINE auto GetKernelInitPageTableRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelInitPt); } - static NOINLINE auto GetKernelMiscRegionExtents() { - return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelMisc); - } + static NOINLINE auto GetKernelPoolManagementRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolManagement); } + static NOINLINE auto GetKernelPoolPartitionRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolPartition); } + static NOINLINE auto GetKernelSystemPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemPool); } + static NOINLINE auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemNonSecurePool); } + static NOINLINE auto GetKernelAppletPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramAppletPool); } + static NOINLINE auto GetKernelApplicationPoolRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramApplicationPool); } - static NOINLINE auto GetKernelSlabRegionExtents() { - return GetVirtualMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelSlab); - } - - static NOINLINE const KMemoryRegion &GetCoreLocalRegion() { - return *GetVirtualMemoryRegionTree().FindFirstRegionByType(KMemoryRegionType_CoreLocal); - } - - static NOINLINE auto GetLinearRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionAttr_LinearMapped); - } - - static NOINLINE auto GetLinearRegionExtents() { - return GetVirtualLinearExtents(GetLinearRegionPhysicalExtents()); - } - - static NOINLINE auto GetCarveoutRegionExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionAttr_CarveoutProtected); - } - - static NOINLINE auto GetKernelRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernel); - } - - static NOINLINE auto GetKernelCodeRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelCode); - } - - static NOINLINE auto GetKernelSlabRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelSlab); - } - - static NOINLINE auto GetKernelPageTableHeapRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelPtHeap); - } - - static NOINLINE auto GetKernelInitPageTableRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramKernelInitPt); - } - - static NOINLINE auto GetKernelPoolPartitionRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramPoolPartition); - } - - static NOINLINE auto GetKernelMetadataPoolRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramMetadataPool); - } - - static NOINLINE auto GetKernelSystemPoolRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemPool); - } - - static NOINLINE auto GetKernelSystemNonSecurePoolRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramSystemNonSecurePool); - } - - static NOINLINE auto GetKernelAppletPoolRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramAppletPool); - } - - static NOINLINE auto GetKernelApplicationPoolRegionPhysicalExtents() { - return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_DramApplicationPool); - } + static NOINLINE auto GetKernelTraceBufferRegionPhysicalExtents() { return GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_KernelTraceBuffer); } }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp index 4193633b2..8bca9c4bd 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_manager.hpp @@ -38,6 +38,7 @@ namespace ams::kern { /* Aliases. */ Pool_Unsafe = Pool_Application, + Pool_Secure = Pool_System, }; enum Direction { @@ -54,7 +55,7 @@ namespace ams::kern { private: using RefCount = u16; public: - static size_t CalculateMetadataOverheadSize(size_t region_size); + static size_t CalculateManagementOverheadSize(size_t region_size); static constexpr size_t CalculateOptimizedProcessOverheadSize(size_t region_size) { return (util::AlignUp((region_size / PageSize), BITSIZEOF(u64)) / BITSIZEOF(u64)) * sizeof(u64); @@ -62,24 +63,24 @@ namespace ams::kern { private: KPageHeap heap; RefCount *page_reference_counts; - KVirtualAddress metadata_region; + KVirtualAddress management_region; Pool pool; Impl *next; Impl *prev; public: - Impl() : heap(), page_reference_counts(), metadata_region(), pool(), next(), prev() { /* ... */ } + Impl() : heap(), page_reference_counts(), management_region(), pool(), next(), prev() { /* ... */ } - size_t Initialize(const KMemoryRegion *region, Pool pool, KVirtualAddress metadata_region, KVirtualAddress metadata_region_end); + size_t Initialize(const KMemoryRegion *region, Pool pool, KVirtualAddress management_region, KVirtualAddress management_region_end); KVirtualAddress AllocateBlock(s32 index, bool random) { return this->heap.AllocateBlock(index, random); } void Free(KVirtualAddress addr, size_t num_pages) { this->heap.Free(addr, num_pages); } - void InitializeOptimizedMemory() { std::memset(GetVoidPointer(this->metadata_region), 0, CalculateOptimizedProcessOverheadSize(this->heap.GetSize())); } + void InitializeOptimizedMemory() { std::memset(GetVoidPointer(this->management_region), 0, CalculateOptimizedProcessOverheadSize(this->heap.GetSize())); } void TrackUnoptimizedAllocation(KVirtualAddress block, size_t num_pages); - size_t TrackOptimizedAllocation(KVirtualAddress block, size_t num_pages); + void TrackOptimizedAllocation(KVirtualAddress block, size_t num_pages); - size_t ProcessOptimizedAllocation(bool *out_any_new, KVirtualAddress block, size_t num_pages, u8 fill_pattern); + bool ProcessOptimizedAllocation(KVirtualAddress block, size_t num_pages, u8 fill_pattern); constexpr Pool GetPool() const { return this->pool; } constexpr size_t GetSize() const { return this->heap.GetSize(); } @@ -87,15 +88,16 @@ namespace ams::kern { size_t GetFreeSize() const { return this->heap.GetFreeSize(); } + constexpr size_t GetPageOffset(KVirtualAddress address) const { return this->heap.GetPageOffset(address); } + constexpr size_t GetPageOffsetToEnd(KVirtualAddress address) const { return this->heap.GetPageOffsetToEnd(address); } + constexpr void SetNext(Impl *n) { this->next = n; } constexpr void SetPrev(Impl *n) { this->prev = n; } constexpr Impl *GetNext() const { return this->next; } constexpr Impl *GetPrev() const { return this->prev; } - void Open(KLightLock *pool_locks, KVirtualAddress address, size_t num_pages) { - KScopedLightLock lk(pool_locks[this->pool]); - - size_t index = this->heap.GetPageOffset(address); + void Open(KVirtualAddress address, size_t num_pages) { + size_t index = this->GetPageOffset(address); const size_t end = index + num_pages; while (index < end) { const RefCount ref_count = (++this->page_reference_counts[index]); @@ -105,10 +107,8 @@ namespace ams::kern { } } - void Close(KLightLock *pool_locks, KVirtualAddress address, size_t num_pages) { - KScopedLightLock lk(pool_locks[this->pool]); - - size_t index = this->heap.GetPageOffset(address); + void Close(KVirtualAddress address, size_t num_pages) { + size_t index = this->GetPageOffset(address); const size_t end = index + num_pages; size_t free_start = 0; @@ -173,7 +173,7 @@ namespace ams::kern { /* ... */ } - NOINLINE void Initialize(KVirtualAddress metadata_region, size_t metadata_region_size); + NOINLINE void Initialize(KVirtualAddress management_region, size_t management_region_size); NOINLINE Result InitializeOptimizedMemory(u64 process_id, Pool pool); NOINLINE void FinalizeOptimizedMemory(u64 process_id, Pool pool); @@ -186,8 +186,13 @@ namespace ams::kern { /* Repeatedly open references until we've done so for all pages. */ while (num_pages) { auto &manager = this->GetManager(address); - const size_t cur_pages = std::min(num_pages, (manager.GetEndAddress() - address) / PageSize); - manager.Open(this->pool_locks, address, cur_pages); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); + + { + KScopedLightLock lk(this->pool_locks[manager.GetPool()]); + manager.Open(address, cur_pages); + } + num_pages -= cur_pages; address += cur_pages * PageSize; } @@ -197,8 +202,13 @@ namespace ams::kern { /* Repeatedly close references until we've done so for all pages. */ while (num_pages) { auto &manager = this->GetManager(address); - const size_t cur_pages = std::min(num_pages, (manager.GetEndAddress() - address) / PageSize); - manager.Close(this->pool_locks, address, cur_pages); + const size_t cur_pages = std::min(num_pages, manager.GetPageOffsetToEnd(address)); + + { + KScopedLightLock lk(this->pool_locks[manager.GetPool()]); + manager.Close(address, cur_pages); + } + num_pages -= cur_pages; address += cur_pages * PageSize; } @@ -238,8 +248,8 @@ namespace ams::kern { return total; } public: - static size_t CalculateMetadataOverheadSize(size_t region_size) { - return Impl::CalculateMetadataOverheadSize(region_size); + static size_t CalculateManagementOverheadSize(size_t region_size) { + return Impl::CalculateManagementOverheadSize(region_size); } static constexpr ALWAYS_INLINE u32 EncodeOption(Pool pool, Direction dir) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_region.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_region.hpp new file mode 100644 index 000000000..a04c32d11 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_region.hpp @@ -0,0 +1,318 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::kern { + + class KMemoryRegionTree; + + class KMemoryRegion : public util::IntrusiveRedBlackTreeBaseNode { + NON_COPYABLE(KMemoryRegion); + NON_MOVEABLE(KMemoryRegion); + private: + friend class KMemoryRegionTree; + private: + uintptr_t address; + uintptr_t pair_address; + size_t region_size; + u32 attributes; + u32 type_id; + public: + static constexpr ALWAYS_INLINE int Compare(const KMemoryRegion &lhs, const KMemoryRegion &rhs) { + if (lhs.GetAddress() < rhs.GetAddress()) { + return -1; + } else if (lhs.GetAddress() <= rhs.GetLastAddress()) { + return 0; + } else { + return 1; + } + } + public: + constexpr ALWAYS_INLINE KMemoryRegion() : address(0), pair_address(0), region_size(0), attributes(0), type_id(0) { /* ... */ } + constexpr ALWAYS_INLINE KMemoryRegion(uintptr_t a, size_t rs, uintptr_t p, u32 r, u32 t) : + address(a), pair_address(p), region_size(rs), attributes(r), type_id(t) + { + /* ... */ + } + constexpr ALWAYS_INLINE KMemoryRegion(uintptr_t a, size_t rs, u32 r, u32 t) : KMemoryRegion(a, rs, std::numeric_limits::max(), r, t) { /* ... */ } + private: + constexpr ALWAYS_INLINE void Reset(uintptr_t a, uintptr_t rs, uintptr_t p, u32 r, u32 t) { + this->address = a; + this->pair_address = p; + this->region_size = rs; + this->attributes = r; + this->type_id = t; + } + public: + constexpr ALWAYS_INLINE uintptr_t GetAddress() const { + return this->address; + } + + constexpr ALWAYS_INLINE uintptr_t GetPairAddress() const { + return this->pair_address; + } + + constexpr ALWAYS_INLINE size_t GetSize() const { + return this->region_size; + } + + constexpr ALWAYS_INLINE uintptr_t GetEndAddress() const { + return this->GetAddress() + this->GetSize(); + } + + constexpr ALWAYS_INLINE uintptr_t GetLastAddress() const { + return this->GetEndAddress() - 1; + } + + constexpr ALWAYS_INLINE u32 GetAttributes() const { + return this->attributes; + } + + constexpr ALWAYS_INLINE u32 GetType() const { + return this->type_id; + } + + constexpr ALWAYS_INLINE void SetType(u32 type) { + MESOSPHERE_INIT_ABORT_UNLESS(this->CanDerive(type)); + this->type_id = type; + } + + constexpr ALWAYS_INLINE bool Contains(uintptr_t address) const { + return this->GetAddress() <= address && address <= this->GetLastAddress(); + } + + constexpr ALWAYS_INLINE bool IsDerivedFrom(u32 type) const { + return (this->GetType() | type) == this->GetType(); + } + + constexpr ALWAYS_INLINE bool HasTypeAttribute(KMemoryRegionAttr attr) const { + return (this->GetType() | attr) == this->GetType(); + } + + constexpr ALWAYS_INLINE bool CanDerive(u32 type) const { + return (this->GetType() | type) == type; + } + + constexpr ALWAYS_INLINE void SetPairAddress(uintptr_t a) { + this->pair_address = a; + } + + constexpr ALWAYS_INLINE void SetTypeAttribute(KMemoryRegionAttr attr) { + this->type_id |= attr; + } + }; + static_assert(std::is_trivially_destructible::value); + + class KMemoryRegionTree { + public: + struct DerivedRegionExtents { + const KMemoryRegion *first_region; + const KMemoryRegion *last_region; + + constexpr DerivedRegionExtents() : first_region(nullptr), last_region(nullptr) { /* ... */ } + + constexpr ALWAYS_INLINE uintptr_t GetAddress() const { + return this->first_region->GetAddress(); + } + + constexpr ALWAYS_INLINE uintptr_t GetEndAddress() const { + return this->last_region->GetEndAddress(); + } + + constexpr ALWAYS_INLINE size_t GetSize() const { + return this->GetEndAddress() - this->GetAddress(); + } + + constexpr ALWAYS_INLINE uintptr_t GetLastAddress() const { + return this->GetEndAddress() - 1; + } + }; + private: + using TreeType = util::IntrusiveRedBlackTreeBaseTraits::TreeType; + public: + using value_type = TreeType::value_type; + using size_type = TreeType::size_type; + using difference_type = TreeType::difference_type; + using pointer = TreeType::pointer; + using const_pointer = TreeType::const_pointer; + using reference = TreeType::reference; + using const_reference = TreeType::const_reference; + using iterator = TreeType::iterator; + using const_iterator = TreeType::const_iterator; + private: + TreeType tree; + public: + constexpr ALWAYS_INLINE KMemoryRegionTree() : tree() { /* ... */ } + public: + KMemoryRegion *FindModifiable(uintptr_t address) { + if (auto it = this->find(KMemoryRegion(address, 1, 0, 0)); it != this->end()) { + return std::addressof(*it); + } else { + return nullptr; + } + } + + const KMemoryRegion *Find(uintptr_t address) const { + if (auto it = this->find(KMemoryRegion(address, 1, 0, 0)); it != this->cend()) { + return std::addressof(*it); + } else { + return nullptr; + } + } + + const KMemoryRegion *FindByType(u32 type_id) const { + for (auto it = this->cbegin(); it != this->cend(); ++it) { + if (it->GetType() == type_id) { + return std::addressof(*it); + } + } + return nullptr; + } + + const KMemoryRegion *FindByTypeAndAttribute(u32 type_id, u32 attr) const { + for (auto it = this->cbegin(); it != this->cend(); ++it) { + if (it->GetType() == type_id && it->GetAttributes() == attr) { + return std::addressof(*it); + } + } + return nullptr; + } + + const KMemoryRegion *FindFirstDerived(u32 type_id) const { + for (auto it = this->cbegin(); it != this->cend(); it++) { + if (it->IsDerivedFrom(type_id)) { + return std::addressof(*it); + } + } + return nullptr; + } + + const KMemoryRegion *FindLastDerived(u32 type_id) const { + const KMemoryRegion *region = nullptr; + for (auto it = this->begin(); it != this->end(); it++) { + if (it->IsDerivedFrom(type_id)) { + region = std::addressof(*it); + } + } + return region; + } + + + DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const { + DerivedRegionExtents extents; + + MESOSPHERE_INIT_ABORT_UNLESS(extents.first_region == nullptr); + MESOSPHERE_INIT_ABORT_UNLESS(extents.last_region == nullptr); + + for (auto it = this->cbegin(); it != this->cend(); it++) { + if (it->IsDerivedFrom(type_id)) { + if (extents.first_region == nullptr) { + extents.first_region = std::addressof(*it); + } + extents.last_region = std::addressof(*it); + } + } + + MESOSPHERE_INIT_ABORT_UNLESS(extents.first_region != nullptr); + MESOSPHERE_INIT_ABORT_UNLESS(extents.last_region != nullptr); + + return extents; + } + public: + NOINLINE void InsertDirectly(uintptr_t address, size_t size, u32 attr = 0, u32 type_id = 0); + NOINLINE bool Insert(uintptr_t address, size_t size, u32 type_id, u32 new_attr = 0, u32 old_attr = 0); + + NOINLINE KVirtualAddress GetRandomAlignedRegion(size_t size, size_t alignment, u32 type_id); + + ALWAYS_INLINE KVirtualAddress GetRandomAlignedRegionWithGuard(size_t size, size_t alignment, u32 type_id, size_t guard_size) { + return this->GetRandomAlignedRegion(size + 2 * guard_size, alignment, type_id) + guard_size; + } + public: + /* Iterator accessors. */ + iterator begin() { + return this->tree.begin(); + } + + const_iterator begin() const { + return this->tree.begin(); + } + + iterator end() { + return this->tree.end(); + } + + const_iterator end() const { + return this->tree.end(); + } + + const_iterator cbegin() const { + return this->begin(); + } + + const_iterator cend() const { + return this->end(); + } + + iterator iterator_to(reference ref) { + return this->tree.iterator_to(ref); + } + + const_iterator iterator_to(const_reference ref) const { + return this->tree.iterator_to(ref); + } + + /* Content management. */ + bool empty() const { + return this->tree.empty(); + } + + reference back() { + return this->tree.back(); + } + + const_reference back() const { + return this->tree.back(); + } + + reference front() { + return this->tree.front(); + } + + const_reference front() const { + return this->tree.front(); + } + + /* GCC over-eagerly inlines this operation. */ + NOINLINE iterator insert(reference ref) { + return this->tree.insert(ref); + } + + NOINLINE iterator erase(iterator it) { + return this->tree.erase(it); + } + + iterator find(const_reference ref) const { + return this->tree.find(ref); + } + + iterator nfind(const_reference ref) const { + return this->tree.nfind(ref); + } + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp new file mode 100644 index 000000000..b27943287 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_region_type.hpp @@ -0,0 +1,300 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::kern { + + enum KMemoryRegionType : u32 {}; + + enum KMemoryRegionAttr : typename std::underlying_type::type { + KMemoryRegionAttr_CarveoutProtected = 0x04000000, + KMemoryRegionAttr_DidKernelMap = 0x08000000, + KMemoryRegionAttr_ShouldKernelMap = 0x10000000, + KMemoryRegionAttr_UserReadOnly = 0x20000000, + KMemoryRegionAttr_NoUserMap = 0x40000000, + KMemoryRegionAttr_LinearMapped = 0x80000000, + }; + + namespace impl { + + consteval size_t BitsForDeriveSparse(size_t n) { + return n + 1; + } + + consteval size_t BitsForDeriveDense(size_t n) { + AMS_ASSUME(n > 0); + + size_t low = 0, high = 1; + for (size_t i = 0; i < n - 1; ++i) { + if ((++low) == high) { + ++high; + low = 0; + } + } + return high + 1; + } + + class KMemoryRegionTypeValue { + private: + using ValueType = typename std::underlying_type::type; + private: + ValueType value; + size_t next_bit; + bool finalized; + bool sparse_only; + bool dense_only; + private: + consteval KMemoryRegionTypeValue(ValueType v) : value(v), next_bit(0), finalized(false), sparse_only(false), dense_only(false) { /* ... */ } + public: + consteval KMemoryRegionTypeValue() : KMemoryRegionTypeValue(0) { /* ... */ } + + consteval operator KMemoryRegionType() const { return static_cast(this->value); } + consteval ValueType GetValue() const { return this->value; } + + consteval const KMemoryRegionTypeValue &Finalize() { this->finalized = true; return *this; } + consteval const KMemoryRegionTypeValue &SetSparseOnly() { this->sparse_only = true; return *this; } + consteval const KMemoryRegionTypeValue &SetDenseOnly() { this->dense_only = true; return *this; } + + consteval KMemoryRegionTypeValue &SetAttribute(KMemoryRegionAttr attr) { AMS_ASSUME(!this->finalized); this->value |= attr; return *this; } + + consteval KMemoryRegionTypeValue DeriveInitial(size_t i, size_t next = BITSIZEOF(ValueType)) const { + AMS_ASSUME(!this->finalized); + AMS_ASSUME(!this->value); + AMS_ASSUME(!this->next_bit); + AMS_ASSUME(next > i); + + KMemoryRegionTypeValue new_type = *this; + new_type.value = (ValueType{1} << i); + new_type.next_bit = next; + return new_type; + } + + consteval KMemoryRegionTypeValue DeriveAttribute(KMemoryRegionAttr attr) const { + AMS_ASSUME(!this->finalized); + + KMemoryRegionTypeValue new_type = *this; + new_type.value |= attr; + return new_type; + } + + consteval KMemoryRegionTypeValue DeriveTransition(size_t ofs = 0, size_t adv = 1) const { + AMS_ASSUME(!this->finalized); + AMS_ASSUME(ofs < adv); + AMS_ASSUME(this->next_bit + adv <= BITSIZEOF(ValueType)); + + KMemoryRegionTypeValue new_type = *this; + new_type.value |= (ValueType{1} << (this->next_bit + ofs)); + new_type.next_bit += adv; + return new_type; + } + + consteval KMemoryRegionTypeValue DeriveSparse(size_t ofs, size_t n, size_t i) const { + AMS_ASSUME(!this->finalized); + AMS_ASSUME(!this->dense_only); + AMS_ASSUME(this->next_bit + ofs + n + 1 <= BITSIZEOF(ValueType)); + AMS_ASSUME(i < n); + + KMemoryRegionTypeValue new_type = *this; + new_type.value |= (ValueType{1} << (this->next_bit + ofs)); + new_type.value |= (ValueType{1} << (this->next_bit + ofs + 1 + i)); + new_type.next_bit += ofs + n + 1; + return new_type; + } + + consteval KMemoryRegionTypeValue Derive(size_t n, size_t i) const { + AMS_ASSUME(!this->finalized); + AMS_ASSUME(!this->sparse_only); + AMS_ASSUME(this->next_bit + BitsForDeriveDense(n) <= BITSIZEOF(ValueType)); + AMS_ASSUME(i < n); + + size_t low = 0, high = 1; + for (size_t j = 0; j < i; ++j) { + if ((++low) == high) { + ++high; + low = 0; + } + } + AMS_ASSUME(high < BitsForDeriveDense(n)); + + + KMemoryRegionTypeValue new_type = *this; + new_type.value |= (ValueType{1} << (this->next_bit + low)); + new_type.value |= (ValueType{1} << (this->next_bit + high)); + new_type.next_bit += BitsForDeriveDense(n); + return new_type; + } + + consteval KMemoryRegionTypeValue Advance(size_t n) const { + AMS_ASSUME(!this->finalized); + AMS_ASSUME(this->next_bit + n <= BITSIZEOF(ValueType)); + + KMemoryRegionTypeValue new_type = *this; + new_type.next_bit += n; + return new_type; + } + + constexpr ALWAYS_INLINE bool IsAncestorOf(ValueType v) const { + return (this->value | v) == v; + } + }; + + } + + constexpr inline const auto KMemoryRegionType_None = impl::KMemoryRegionTypeValue(); + + constexpr inline const auto KMemoryRegionType_Kernel = KMemoryRegionType_None.DeriveInitial(0, 2); + constexpr inline const auto KMemoryRegionType_Dram = KMemoryRegionType_None.DeriveInitial(1, 2); + constexpr inline const auto KMemoryRegionType_CoreLocalRegion = KMemoryRegionType_None.DeriveInitial(2).Finalize(); + static_assert(KMemoryRegionType_Kernel .GetValue() == 0x1); + static_assert(KMemoryRegionType_Dram .GetValue() == 0x2); + static_assert(KMemoryRegionType_CoreLocalRegion.GetValue() == 0x4); + + constexpr inline const auto KMemoryRegionType_DramKernelBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 0).SetAttribute(KMemoryRegionAttr_NoUserMap).SetAttribute(KMemoryRegionAttr_CarveoutProtected); + constexpr inline const auto KMemoryRegionType_DramReservedBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 1); + constexpr inline const auto KMemoryRegionType_DramHeapBase = KMemoryRegionType_Dram.DeriveSparse(0, 3, 2).SetAttribute(KMemoryRegionAttr_LinearMapped); + static_assert(KMemoryRegionType_DramKernelBase .GetValue() == (0xE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap)); + static_assert(KMemoryRegionType_DramReservedBase.GetValue() == (0x16)); + static_assert(KMemoryRegionType_DramHeapBase .GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped)); + + constexpr inline const auto KMemoryRegionType_DramKernelCode = KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 0); + constexpr inline const auto KMemoryRegionType_DramKernelSlab = KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 1); + constexpr inline const auto KMemoryRegionType_DramKernelPtHeap = KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 2).SetAttribute(KMemoryRegionAttr_LinearMapped); + constexpr inline const auto KMemoryRegionType_DramKernelInitPt = KMemoryRegionType_DramKernelBase.DeriveSparse(0, 4, 3).SetAttribute(KMemoryRegionAttr_LinearMapped); + static_assert(KMemoryRegionType_DramKernelCode .GetValue() == (0xCE | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap)); + static_assert(KMemoryRegionType_DramKernelSlab .GetValue() == (0x14E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap)); + static_assert(KMemoryRegionType_DramKernelPtHeap.GetValue() == (0x24E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped)); + static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() == (0x44E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_LinearMapped)); + + + constexpr inline const auto KMemoryRegionType_DramReservedEarly = KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); + static_assert(KMemoryRegionType_DramReservedEarly.GetValue() == (0x16 | KMemoryRegionAttr_NoUserMap)); + + /* UNUSED: DeriveSparse(0, 3, 0); */ + constexpr inline const auto KMemoryRegionType_OnMemoryBootImage = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 1); + constexpr inline const auto KMemoryRegionType_DTB = KMemoryRegionType_DramReservedBase.DeriveSparse(0, 3, 2); + static_assert(KMemoryRegionType_OnMemoryBootImage.GetValue() == 0x156); + static_assert(KMemoryRegionType_DTB.GetValue() == 0x256); + + constexpr inline const auto KMemoryRegionType_KernelTraceBuffer = KMemoryRegionType_DramHeapBase.DeriveTransition(1, 3).SetAttribute(KMemoryRegionAttr_UserReadOnly); + static_assert(KMemoryRegionType_KernelTraceBuffer.GetValue() == (0xA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_UserReadOnly)); + + constexpr inline const auto KMemoryRegionType_DramPoolPartition = KMemoryRegionType_DramHeapBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap); + static_assert(KMemoryRegionType_DramPoolPartition.GetValue() == (0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); + + constexpr inline const auto KMemoryRegionType_DramPoolManagement = KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(KMemoryRegionAttr_CarveoutProtected); + constexpr inline const auto KMemoryRegionType_DramUserPool = KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition(); + static_assert(KMemoryRegionType_DramPoolManagement.GetValue() == (0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected)); + static_assert(KMemoryRegionType_DramUserPool.GetValue() == (0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); + + constexpr inline const auto KMemoryRegionType_DramApplicationPool = KMemoryRegionType_DramUserPool.Derive(4, 0); + constexpr inline const auto KMemoryRegionType_DramAppletPool = KMemoryRegionType_DramUserPool.Derive(4, 1); + constexpr inline const auto KMemoryRegionType_DramSystemNonSecurePool = KMemoryRegionType_DramUserPool.Derive(4, 2); + constexpr inline const auto KMemoryRegionType_DramSystemPool = KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected); + static_assert(KMemoryRegionType_DramApplicationPool .GetValue() == (0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); + static_assert(KMemoryRegionType_DramAppletPool .GetValue() == (0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); + static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() == (0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap)); + static_assert(KMemoryRegionType_DramSystemPool .GetValue() == (0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap | KMemoryRegionAttr_CarveoutProtected)); + + constexpr inline const auto KMemoryRegionType_VirtualDramHeapBase = KMemoryRegionType_Dram.DeriveSparse(1, 3, 0); + constexpr inline const auto KMemoryRegionType_VirtualDramKernelPtHeap = KMemoryRegionType_Dram.DeriveSparse(1, 3, 1); + constexpr inline const auto KMemoryRegionType_VirtualDramKernelTraceBuffer = KMemoryRegionType_Dram.DeriveSparse(1, 3, 2); + static_assert(KMemoryRegionType_VirtualDramHeapBase .GetValue() == 0x1A); + static_assert(KMemoryRegionType_VirtualDramKernelPtHeap .GetValue() == 0x2A); + static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A); + + constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0); + constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1); + constexpr inline const auto KMemoryRegionType_VirtualDramUserPool = KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2); + static_assert(KMemoryRegionType_VirtualDramKernelInitPt .GetValue() == 0x19A); + static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A); + static_assert(KMemoryRegionType_VirtualDramUserPool .GetValue() == 0x31A); + + /* NOTE: For unknown reason, the pools are derived out-of-order here. */ + /* It's worth eventually trying to understand why Nintendo made this choice. */ + /* UNUSED: .Derive(6, 0); */ + /* UNUSED: .Derive(6, 1); */ + constexpr inline const auto KMemoryRegionType_VirtualDramAppletPool = KMemoryRegionType_VirtualDramUserPool.Derive(6, 2); + constexpr inline const auto KMemoryRegionType_VirtualDramApplicationPool = KMemoryRegionType_VirtualDramUserPool.Derive(6, 3); + constexpr inline const auto KMemoryRegionType_VirtualDramSystemNonSecurePool = KMemoryRegionType_VirtualDramUserPool.Derive(6, 4); + constexpr inline const auto KMemoryRegionType_VirtualDramSystemPool = KMemoryRegionType_VirtualDramUserPool.Derive(6, 5); + static_assert(KMemoryRegionType_VirtualDramAppletPool .GetValue() == 0x1B1A); + static_assert(KMemoryRegionType_VirtualDramApplicationPool .GetValue() == 0x271A); + static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A); + static_assert(KMemoryRegionType_VirtualDramSystemPool .GetValue() == 0x331A); + + constexpr inline const auto KMemoryRegionType_ArchDeviceBase = KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly(); + constexpr inline const auto KMemoryRegionType_BoardDeviceBase = KMemoryRegionType_Kernel.DeriveTransition(0, 2).SetDenseOnly(); + static_assert(KMemoryRegionType_ArchDeviceBase .GetValue() == 0x5); + static_assert(KMemoryRegionType_BoardDeviceBase.GetValue() == 0x5); + + #if defined(ATMOSPHERE_ARCH_ARM64) + #include + #elif defined(ATMOSPHERE_ARCH_ARM) + #include + #else + /* Default to no architecture devices. */ + constexpr inline const auto NumArchitectureDeviceRegions = 0; + #endif + static_assert(NumArchitectureDeviceRegions >= 0); + + #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + #include + #else + /* Default to no board devices. */ + constexpr inline const auto NumBoardDeviceRegions = 0; + #endif + static_assert(NumBoardDeviceRegions >= 0); + + constexpr inline const auto KMemoryRegionType_KernelCode = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 0); + constexpr inline const auto KMemoryRegionType_KernelStack = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 1); + constexpr inline const auto KMemoryRegionType_KernelMisc = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 2); + constexpr inline const auto KMemoryRegionType_KernelSlab = KMemoryRegionType_Kernel.DeriveSparse(1, 4, 3); + static_assert(KMemoryRegionType_KernelCode .GetValue() == 0x19); + static_assert(KMemoryRegionType_KernelStack.GetValue() == 0x29); + static_assert(KMemoryRegionType_KernelMisc .GetValue() == 0x49); + static_assert(KMemoryRegionType_KernelSlab .GetValue() == 0x89); + + constexpr inline const auto KMemoryRegionType_KernelMiscDerivedBase = KMemoryRegionType_KernelMisc.DeriveTransition(); + static_assert(KMemoryRegionType_KernelMiscDerivedBase.GetValue() == 0x149); + + /* UNUSED: .Derive(7, 0); */ + constexpr inline const auto KMemoryRegionType_KernelMiscMainStack = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 1); + constexpr inline const auto KMemoryRegionType_KernelMiscMappedDevice = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 2); + constexpr inline const auto KMemoryRegionType_KernelMiscIdleStack = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 3); + constexpr inline const auto KMemoryRegionType_KernelMiscUnknownDebug = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 4); + /* UNUSED: .Derive(7, 5); */ + constexpr inline const auto KMemoryRegionType_KernelMiscExceptionStack = KMemoryRegionType_KernelMiscDerivedBase.Derive(7, 6); + static_assert(KMemoryRegionType_KernelMiscMainStack .GetValue() == 0xB49); + static_assert(KMemoryRegionType_KernelMiscMappedDevice .GetValue() == 0xD49); + static_assert(KMemoryRegionType_KernelMiscIdleStack .GetValue() == 0x1349); + static_assert(KMemoryRegionType_KernelMiscUnknownDebug .GetValue() == 0x1549); + static_assert(KMemoryRegionType_KernelMiscExceptionStack.GetValue() == 0x2349); + + constexpr inline const auto KMemoryRegionType_KernelTemp = KMemoryRegionType_Kernel.Advance(2).Derive(2, 0); + static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31); + + constexpr ALWAYS_INLINE KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) { + if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) { + return KMemoryRegionType_VirtualDramKernelTraceBuffer; + } else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) { + return KMemoryRegionType_VirtualDramKernelPtHeap; + } else { + return KMemoryRegionType_Dram; + } + } + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_bitmap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_bitmap.hpp index 56ef731e1..33d21334c 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_bitmap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_bitmap.hpp @@ -254,7 +254,7 @@ namespace ams::kern { } } public: - static constexpr size_t CalculateMetadataOverheadSize(size_t region_size) { + static constexpr size_t CalculateManagementOverheadSize(size_t region_size) { size_t overhead_bits = 0; for (s32 depth = GetRequiredDepth(region_size) - 1; depth >= 0; depth--) { region_size = util::AlignUp(region_size, BITSIZEOF(u64)) / BITSIZEOF(u64); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp index 76f527991..3f11d6e68 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_heap.hpp @@ -115,11 +115,11 @@ namespace ams::kern { return this->heap_address + (offset << this->GetShift()); } public: - static constexpr size_t CalculateMetadataOverheadSize(size_t region_size, size_t cur_block_shift, size_t next_block_shift) { + static constexpr size_t CalculateManagementOverheadSize(size_t region_size, size_t cur_block_shift, size_t next_block_shift) { const size_t cur_block_size = (u64(1) << cur_block_shift); const size_t next_block_size = (u64(1) << next_block_shift); const size_t align = (next_block_shift != 0) ? next_block_size : cur_block_size; - return KPageBitmap::CalculateMetadataOverheadSize((align * 2 + util::AlignUp(region_size, align)) / cur_block_size); + return KPageBitmap::CalculateManagementOverheadSize((align * 2 + util::AlignUp(region_size, align)) / cur_block_size); } }; private: @@ -129,7 +129,7 @@ namespace ams::kern { size_t num_blocks; Block blocks[NumMemoryBlockPageShifts]; private: - void Initialize(KVirtualAddress heap_address, size_t heap_size, KVirtualAddress metadata_address, size_t metadata_size, const size_t *block_shifts, size_t num_block_shifts); + void Initialize(KVirtualAddress heap_address, size_t heap_size, KVirtualAddress management_address, size_t management_size, const size_t *block_shifts, size_t num_block_shifts); size_t GetNumFreePages() const; void FreeBlock(KVirtualAddress block, s32 index); @@ -142,8 +142,8 @@ namespace ams::kern { constexpr size_t GetPageOffset(KVirtualAddress block) const { return (block - this->GetAddress()) / PageSize; } constexpr size_t GetPageOffsetToEnd(KVirtualAddress block) const { return (this->GetEndAddress() - block) / PageSize; } - void Initialize(KVirtualAddress heap_address, size_t heap_size, KVirtualAddress metadata_address, size_t metadata_size) { - return Initialize(heap_address, heap_size, metadata_address, metadata_size, MemoryBlockPageShifts, NumMemoryBlockPageShifts); + void Initialize(KVirtualAddress heap_address, size_t heap_size, KVirtualAddress management_address, size_t management_size) { + return Initialize(heap_address, heap_size, management_address, management_size, MemoryBlockPageShifts, NumMemoryBlockPageShifts); } size_t GetFreeSize() const { return this->GetNumFreePages() * PageSize; } @@ -155,10 +155,10 @@ namespace ams::kern { KVirtualAddress AllocateBlock(s32 index, bool random); void Free(KVirtualAddress addr, size_t num_pages); private: - static size_t CalculateMetadataOverheadSize(size_t region_size, const size_t *block_shifts, size_t num_block_shifts); + static size_t CalculateManagementOverheadSize(size_t region_size, const size_t *block_shifts, size_t num_block_shifts); public: - static size_t CalculateMetadataOverheadSize(size_t region_size) { - return CalculateMetadataOverheadSize(region_size, MemoryBlockPageShifts, NumMemoryBlockPageShifts); + static size_t CalculateManagementOverheadSize(size_t region_size) { + return CalculateManagementOverheadSize(region_size, MemoryBlockPageShifts, NumMemoryBlockPageShifts); } }; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index f0a43b4bb..1fd477093 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -205,43 +205,43 @@ namespace ams::kern { bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr) { MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); - return KMemoryLayout::IsLinearMappedPhysicalAddress(std::addressof(this->cached_physical_linear_region), phys_addr, this->cached_physical_linear_region); + return KMemoryLayout::IsLinearMappedPhysicalAddress(this->cached_physical_linear_region, phys_addr); } bool IsLinearMappedPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); - return KMemoryLayout::IsLinearMappedPhysicalAddress(std::addressof(this->cached_physical_linear_region), phys_addr, size, this->cached_physical_linear_region); + return KMemoryLayout::IsLinearMappedPhysicalAddress(this->cached_physical_linear_region, phys_addr, size); } bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) { MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); - return KMemoryLayout::IsHeapPhysicalAddress(std::addressof(this->cached_physical_heap_region), phys_addr, this->cached_physical_heap_region); - } - - bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) { - MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread()); - - return KMemoryLayout::IsHeapPhysicalAddress(std::addressof(this->cached_physical_heap_region), phys_addr, this->cached_physical_heap_region); + return KMemoryLayout::IsHeapPhysicalAddress(this->cached_physical_heap_region, phys_addr); } bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr, size_t size) { MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); - return KMemoryLayout::IsHeapPhysicalAddress(std::addressof(this->cached_physical_heap_region), phys_addr, size, this->cached_physical_heap_region); + return KMemoryLayout::IsHeapPhysicalAddress(this->cached_physical_heap_region, phys_addr, size); + } + + bool IsHeapPhysicalAddressForFinalize(KPhysicalAddress phys_addr) { + MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread()); + + return KMemoryLayout::IsHeapPhysicalAddress(this->cached_physical_heap_region, phys_addr); } bool IsHeapVirtualAddress(KVirtualAddress virt_addr) { MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); - return KMemoryLayout::IsHeapVirtualAddress(std::addressof(this->cached_virtual_heap_region), virt_addr, this->cached_virtual_heap_region); + return KMemoryLayout::IsHeapVirtualAddress(this->cached_virtual_heap_region, virt_addr); } bool IsHeapVirtualAddress(KVirtualAddress virt_addr, size_t size) { MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); - return KMemoryLayout::IsHeapVirtualAddress(std::addressof(this->cached_virtual_heap_region), virt_addr, size, this->cached_virtual_heap_region); + return KMemoryLayout::IsHeapVirtualAddress(this->cached_virtual_heap_region, virt_addr, size); } bool ContainsPages(KProcessAddress addr, size_t num_pages) const { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp index 36406d405..1c7d5da02 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_port.hpp @@ -44,7 +44,7 @@ namespace ams::kern { constexpr KPort() : server(), client(), name(), state(State::Invalid), is_light() { /* ... */ } virtual ~KPort() { /* ... */ } - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } void Initialize(s32 max_sessions, bool is_light, uintptr_t name); void OnClientClosed(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp index 48e6bfe46..8eb427e19 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_process.hpp @@ -123,6 +123,20 @@ namespace ams::kern { void StartTermination(); void FinishTermination(); + + void PinThread(s32 core_id, KThread *thread) { + MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); + MESOSPHERE_ASSERT(thread != nullptr); + MESOSPHERE_ASSERT(this->pinned_threads[core_id] == nullptr); + this->pinned_threads[core_id] = thread; + } + + void UnpinThread(s32 core_id, KThread *thread) { + MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); + MESOSPHERE_ASSERT(thread != nullptr); + MESOSPHERE_ASSERT(this->pinned_threads[core_id] == thread); + this->pinned_threads[core_id] = nullptr; + } public: KProcess() { /* ... */ } virtual ~KProcess() { /* ... */ } @@ -207,20 +221,6 @@ namespace ams::kern { return this->pinned_threads[core_id]; } - void PinThread(s32 core_id, KThread *thread) { - MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); - MESOSPHERE_ASSERT(thread != nullptr); - MESOSPHERE_ASSERT(this->pinned_threads[core_id] == nullptr); - this->pinned_threads[core_id] = thread; - } - - void UnpinThread(s32 core_id, KThread *thread) { - MESOSPHERE_ASSERT(0 <= core_id && core_id < static_cast(cpu::NumCores)); - MESOSPHERE_ASSERT(thread != nullptr); - MESOSPHERE_ASSERT(this->pinned_threads[core_id] == thread); - this->pinned_threads[core_id] = nullptr; - } - void CopySvcPermissionsTo(KThread::StackParameters &sp) { this->capabilities.CopySvcPermissionsTo(sp); } @@ -327,6 +327,7 @@ namespace ams::kern { Result SetActivity(ams::svc::ProcessActivity activity); void PinCurrentThread(); + void UnpinCurrentThread(); Result SignalToAddress(KProcessAddress address) { return this->cond_var.SignalToAddress(address); @@ -358,6 +359,8 @@ namespace ams::kern { static Result GetProcessList(s32 *out_num_processes, ams::kern::svc::KUserPointer out_process_ids, s32 max_out_count); static void Switch(KProcess *cur_process, KProcess *next_process) { + MESOSPHERE_UNUSED(cur_process); + /* Set the current process pointer. */ SetCurrentProcess(next_process); @@ -372,11 +375,11 @@ namespace ams::kern { /* Overridden parent functions. */ virtual bool IsInitialized() const override { return this->is_initialized; } - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } virtual void Finalize() override; - virtual u64 GetId() const override { return this->GetProcessId(); } + virtual u64 GetId() const override final { return this->GetProcessId(); } virtual bool IsSignaled() const override { MESOSPHERE_ASSERT_THIS(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_resource_limit.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_resource_limit.hpp index bfcfcb4fb..286992183 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_resource_limit.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_resource_limit.hpp @@ -35,7 +35,7 @@ namespace ams::kern { constexpr ALWAYS_INLINE KResourceLimit() : limit_values(), current_values(), current_hints(), lock(), waiter_count(), cond_var() { /* ... */ } virtual ~KResourceLimit() { /* ... */ } - static ALWAYS_INLINE void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } void Initialize(); virtual void Finalize() override; diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp index 27ea0a1ef..885c1d07e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_scheduler.hpp @@ -48,6 +48,7 @@ namespace ams::kern { private: friend class KScopedSchedulerLock; friend class KScopedSchedulerLockAndSleep; + friend class KScopedDisableDispatch; private: SchedulingState state; bool is_active; @@ -76,7 +77,7 @@ namespace ams::kern { } ALWAYS_INLINE void RequestScheduleOnInterrupt() { - SetSchedulerUpdateNeeded(); + this->state.needs_scheduling = true; if (CanSchedule()) { this->ScheduleOnInterrupt(); @@ -100,11 +101,7 @@ namespace ams::kern { } private: /* Static private API. */ - static ALWAYS_INLINE bool IsSchedulerUpdateNeeded() { return s_scheduler_update_needed; } - static ALWAYS_INLINE void SetSchedulerUpdateNeeded() { s_scheduler_update_needed = true; } - static ALWAYS_INLINE void ClearSchedulerUpdateNeeded() { s_scheduler_update_needed = false; } static ALWAYS_INLINE KSchedulerPriorityQueue &GetPriorityQueue() { return s_priority_queue; } - static NOINLINE u64 UpdateHighestPriorityThreadsImpl(); static NOINLINE void InterruptTaskThreadToRunnable(); @@ -113,6 +110,10 @@ namespace ams::kern { static ALWAYS_INLINE bool CanSchedule() { return GetCurrentThread().GetDisableDispatchCount() == 0; } static ALWAYS_INLINE bool IsSchedulerLockedByCurrentThread() { return s_scheduler_lock.IsLockedByCurrentThread(); } + static ALWAYS_INLINE bool IsSchedulerUpdateNeeded() { return s_scheduler_update_needed; } + static ALWAYS_INLINE void SetSchedulerUpdateNeeded() { s_scheduler_update_needed = true; } + static ALWAYS_INLINE void ClearSchedulerUpdateNeeded() { s_scheduler_update_needed = false; } + static ALWAYS_INLINE void DisableScheduling() { MESOSPHERE_ASSERT(GetCurrentThread().GetDisableDispatchCount() >= 0); GetCurrentThread().DisableDispatch(); @@ -139,9 +140,6 @@ namespace ams::kern { static NOINLINE void ClearPreviousThread(KThread *thread); - static NOINLINE void PinCurrentThread(KProcess *cur_process); - static NOINLINE void UnpinCurrentThread(KProcess *cur_process); - static NOINLINE void OnThreadStateChanged(KThread *thread, KThread::ThreadState old_state); static NOINLINE void OnThreadPriorityChanged(KThread *thread, s32 old_priority); static NOINLINE void OnThreadAffinityMaskChanged(KThread *thread, const KAffinityMask &old_affinity, s32 old_core); @@ -164,8 +162,9 @@ namespace ams::kern { } ALWAYS_INLINE void ScheduleOnInterrupt() { - KScopedDisableDispatch dd; + GetCurrentThread().DisableDispatch(); this->Schedule(); + GetCurrentThread().EnableDispatch(); } void RescheduleOtherCores(u64 cores_needing_scheduling); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_session_request.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_session_request.hpp index 03f1d6ef6..4d5e232d1 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_session_request.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_session_request.hpp @@ -174,7 +174,7 @@ namespace ams::kern { } } - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } constexpr KThread *GetThread() const { return this->thread; } constexpr KWritableEvent *GetEvent() const { return this->event; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_shared_memory.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_shared_memory.hpp index 82fb2432e..9bbd33a83 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_shared_memory.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_shared_memory.hpp @@ -47,7 +47,7 @@ namespace ams::kern { virtual void Finalize() override; virtual bool IsInitialized() const override { return this->is_initialized; } - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } Result Map(KProcessPageTable *table, KProcessAddress address, size_t size, KProcess *process, ams::svc::MemoryPermission map_perm); Result Unmap(KProcessPageTable *table, KProcessAddress address, size_t size, KProcess *process); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index 5ffb5b434..357e8d085 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -472,6 +472,7 @@ namespace ams::kern { void AddCpuTime(s32 core_id, s64 amount) { this->cpu_time += amount; /* TODO: Debug kernels track per-core tick counts. Should we? */ + MESOSPHERE_UNUSED(core_id); } s64 GetCpuTime() const { return this->cpu_time; } @@ -523,7 +524,7 @@ namespace ams::kern { public: /* Overridden parent functions. */ - virtual u64 GetId() const override { return this->GetThreadId(); } + virtual u64 GetId() const override final { return this->GetThreadId(); } virtual bool IsInitialized() const override { return this->initialized; } virtual uintptr_t GetPostDestroyArgument() const override { return reinterpret_cast(this->parent) | (this->resource_limit_release_hint ? 1 : 0); } @@ -558,20 +559,7 @@ namespace ams::kern { GetCurrentThread().DisableDispatch(); } - ALWAYS_INLINE ~KScopedDisableDispatch() { - GetCurrentThread().EnableDispatch(); - } - }; - - class KScopedEnableDispatch { - public: - explicit ALWAYS_INLINE KScopedEnableDispatch() { - GetCurrentThread().EnableDispatch(); - } - - ALWAYS_INLINE ~KScopedEnableDispatch() { - GetCurrentThread().DisableDispatch(); - } + ~KScopedDisableDispatch(); }; ALWAYS_INLINE KExceptionContext *GetExceptionContext(KThread *thread) { diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_trace.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_trace.hpp new file mode 100644 index 000000000..8fe1b836f --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_trace.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::kern { + + #if defined(MESOSPHERE_BUILD_FOR_TRACING) + constexpr inline bool IsKTraceEnabled = true; + #else + constexpr inline bool IsKTraceEnabled = false; + #endif + + constexpr inline size_t KTraceBufferSize = IsKTraceEnabled ? 16_MB : 0; + + static_assert(IsKTraceEnabled || !IsKTraceEnabled); + + class KTrace { + public: + enum Type { + Type_ThreadSwitch = 1, + + Type_SvcEntry0 = 3, + Type_SvcEntry1 = 4, + Type_SvcExit0 = 5, + Type_SvcExit1 = 6, + Type_Interrupt = 7, + + Type_ScheduleUpdate = 11, + + Type_CoreMigration = 14, + }; + private: + static bool s_is_active; + public: + static void Initialize(KVirtualAddress address, size_t size); + static void Start(); + static void Stop(); + + static void PushRecord(u8 type, u64 param0 = 0, u64 param1 = 0, u64 param2 = 0, u64 param3 = 0, u64 param4 = 0, u64 param5 = 0); + + static ALWAYS_INLINE bool IsActive() { return s_is_active; } + }; + +} + +#define MESOSPHERE_KTRACE_RESUME() \ + ({ \ + if constexpr (::ams::kern::IsKTraceEnabled) { \ + ::ams::kern::KTrace::Start(); \ + } \ + }) + +#define MESOSPHERE_KTRACE_PAUSE() \ + ({ \ + if constexpr (::ams::kern::IsKTraceEnabled) { \ + ::ams::kern::KTrace::Stop(); \ + } \ + }) + +#define MESOSPHERE_KTRACE_PUSH_RECORD(TYPE, ...) \ + ({ \ + if constexpr (::ams::kern::IsKTraceEnabled) { \ + if (::ams::kern::KTrace::IsActive()) { \ + ::ams::kern::KTrace::PushRecord(TYPE, ## __VA_ARGS__); \ + } \ + } \ + }) + +#define MESOSPHERE_KTRACE_THREAD_SWITCH(NEXT) \ + MESOSPHERE_KTRACE_PUSH_RECORD(::ams::kern::KTrace::Type_ThreadSwitch, (NEXT)->GetId()) + +#define MESOSPHERE_KTRACE_SVC_ENTRY(SVC_ID, PARAM0, PARAM1, PARAM2, PARAM3, PARAM4, PARAM5, PARAM6, PARAM7) \ + ({ \ + if constexpr (::ams::kern::IsKTraceEnabled) { \ + if (::ams::kern::KTrace::IsActive()) { \ + ::ams::kern::KTrace::PushRecord(::ams::kern::KTrace::Type_SvcEntry0, SVC_ID, PARAM0, PARAM1, PARAM2, PARAM3, PARAM4); \ + ::ams::kern::KTrace::PushRecord(::ams::kern::KTrace::Type_SvcEntry1, PARAM5, PARAM6, PARAM7); \ + } \ + } \ + }) + +#define MESOSPHERE_KTRACE_SVC_EXIT(SVC_ID, PARAM0, PARAM1, PARAM2, PARAM3, PARAM4, PARAM5, PARAM6, PARAM7) \ + ({ \ + if constexpr (::ams::kern::IsKTraceEnabled) { \ + if (::ams::kern::KTrace::IsActive()) { \ + ::ams::kern::KTrace::PushRecord(::ams::kern::KTrace::Type_SvcExit0, SVC_ID, PARAM0, PARAM1, PARAM2, PARAM3, PARAM4); \ + ::ams::kern::KTrace::PushRecord(::ams::kern::KTrace::Type_SvcExit1, PARAM5, PARAM6, PARAM7); \ + } \ + } \ + }) + +#define MESOSPHERE_KTRACE_INTERRUPT(ID) \ + MESOSPHERE_KTRACE_PUSH_RECORD(::ams::kern::KTrace::Type_Interrupt, ID) + +#define MESOSPHERE_KTRACE_SCHEDULE_UPDATE(CORE, PREV, NEXT) \ + MESOSPHERE_KTRACE_PUSH_RECORD(::ams::kern::KTrace::Type_ScheduleUpdate, CORE, (PREV)->GetId(), (NEXT)->GetId()) + +#define MESOSPHERE_KTRACE_CORE_MIGRATION(THREAD_ID, PREV, NEXT, REASON) \ + MESOSPHERE_KTRACE_PUSH_RECORD(::ams::kern::KTrace::Type_CoreMigration, THREAD_ID, PREV, NEXT, REASON) diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_typed_address.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_typed_address.hpp index bc291e0bd..7144bf5b4 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_typed_address.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_typed_address.hpp @@ -31,11 +31,11 @@ namespace ams::kern { template constexpr ALWAYS_INLINE explicit KTypedAddress(U *ptr) : address(reinterpret_cast(ptr)) { /* ... */ } + /* Copy constructor. */ + constexpr ALWAYS_INLINE KTypedAddress(const KTypedAddress &rhs) = default; + /* Assignment operator. */ - constexpr ALWAYS_INLINE KTypedAddress operator=(KTypedAddress rhs) { - this->address = rhs.address; - return *this; - } + constexpr ALWAYS_INLINE KTypedAddress &operator=(const KTypedAddress &rhs) = default; /* Arithmetic operators. */ template @@ -180,6 +180,9 @@ namespace ams::kern { #endif + template + concept IsKTypedAddress = std::same_as || std::same_as || std::same_as; + template constexpr inline T Null = [] { if constexpr (std::is_same::value) { @@ -197,6 +200,26 @@ namespace ams::kern { static_assert(sizeof(KVirtualAddress) == sizeof(uintptr_t)); static_assert(sizeof(KProcessAddress) == sizeof(uintptr_t)); + static_assert(std::is_trivially_copyable::value); + static_assert(std::is_trivially_copyable::value); + static_assert(std::is_trivially_copyable::value); + + static_assert(std::is_trivially_copy_constructible::value); + static_assert(std::is_trivially_copy_constructible::value); + static_assert(std::is_trivially_copy_constructible::value); + + static_assert(std::is_trivially_move_constructible::value); + static_assert(std::is_trivially_move_constructible::value); + static_assert(std::is_trivially_move_constructible::value); + + static_assert(std::is_trivially_copy_assignable::value); + static_assert(std::is_trivially_copy_assignable::value); + static_assert(std::is_trivially_copy_assignable::value); + + static_assert(std::is_trivially_move_assignable::value); + static_assert(std::is_trivially_move_assignable::value); + static_assert(std::is_trivially_move_assignable::value); + static_assert(std::is_trivially_destructible::value); static_assert(std::is_trivially_destructible::value); static_assert(std::is_trivially_destructible::value); @@ -206,6 +229,10 @@ namespace ams::kern { static_assert(Null == Null); static_assert(Null == Null); + /* Constructor/assignment validations. */ + static_assert([]{ const KPhysicalAddress a(5); KPhysicalAddress b(a); return b; }() == KPhysicalAddress(5)); + static_assert([]{ const KPhysicalAddress a(5); KPhysicalAddress b(10); b = a; return b; }() == KPhysicalAddress(5)); + /* Arithmetic validations. */ static_assert(KPhysicalAddress(10) + 5 == KPhysicalAddress(15)); static_assert(KPhysicalAddress(10) - 5 == KPhysicalAddress(5)); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_writable_event.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_writable_event.hpp index f67c43979..f3ee813d6 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_writable_event.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_writable_event.hpp @@ -32,7 +32,7 @@ namespace ams::kern { virtual void Destroy() override; - static void PostDestroy(uintptr_t arg) { /* ... */ } + static void PostDestroy(uintptr_t arg) { MESOSPHERE_UNUSED(arg); /* ... */ } void Initialize(KEvent *p); Result Signal(); diff --git a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp index 3e7eaf72b..9382de11e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_panic.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_panic.hpp @@ -19,17 +19,12 @@ namespace ams::kern { - template - ALWAYS_INLINE void UnusedImpl(ArgTypes &&... args) { - (static_cast(args), ...); - } - NORETURN NOINLINE void Panic(const char *file, int line, const char *format, ...) __attribute__((format(printf, 3, 4))); NORETURN NOINLINE void Panic(); } -#define MESOSPHERE_UNUSED(...) ::ams::kern::UnusedImpl(__VA_ARGS__) +#define MESOSPHERE_UNUSED(...) AMS_UNUSED(__VA_ARGS__) #ifdef MESOSPHERE_ENABLE_DEBUG_PRINT #define MESOSPHERE_PANIC(...) do { ::ams::kern::Panic(__FILE__, __LINE__, ## __VA_ARGS__); } while(0) @@ -69,7 +64,7 @@ namespace ams::kern { #define MESOSPHERE_UNIMPLEMENTED() MESOSPHERE_PANIC("%s: Unimplemented\n", __PRETTY_FUNCTION__) #define MESOSPHERE_ABORT() MESOSPHERE_PANIC("Abort()\n"); -#define MESOSPHERE_INIT_ABORT() do { /* ... */ } while (true) +#define MESOSPHERE_INIT_ABORT() AMS_INFINITE_LOOP() #define MESOSPHERE_ABORT_UNLESS(expr) \ ({ \ diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_device_page_table.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_device_page_table.hpp index 51e4db302..6557c991e 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_select_device_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_select_device_page_table.hpp @@ -24,5 +24,10 @@ } #else - #error "Unknown board for KDevicePageTable" + #include + + namespace ams::kern { + using ams::kern::board::generic::KDevicePageTable; + } + #endif diff --git a/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_controller.hpp b/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_controller.hpp index 605fa3fcd..7225dc09c 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_controller.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_select_interrupt_controller.hpp @@ -24,6 +24,13 @@ using ams::kern::arch::arm64::KInterruptController; } +#elif defined(ATMOSPHERE_ARCH_ARM) + + #include + namespace ams::kern { + using ams::kern::arch::arm::KInterruptController; + } + #else #error "Unknown architecture for KInterruptController" diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_controller.cpp b/libraries/libmesosphere/source/arch/arm/kern_generic_interrupt_controller.inc similarity index 96% rename from libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_controller.cpp rename to libraries/libmesosphere/source/arch/arm/kern_generic_interrupt_controller.inc index 33fef41f7..12fc57c2f 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_controller.cpp +++ b/libraries/libmesosphere/source/arch/arm/kern_generic_interrupt_controller.inc @@ -15,7 +15,7 @@ */ #include -namespace ams::kern::arch::arm64 { +namespace ams::kern::arch::arm { void KInterruptController::SetupInterruptLines(s32 core_id) const { const size_t ITLines = (core_id == 0) ? 32 * ((this->gicd->typer & 0x1F) + 1) : NumLocalInterrupts; @@ -39,8 +39,8 @@ namespace ams::kern::arch::arm64 { void KInterruptController::Initialize(s32 core_id) { /* Setup pointers to ARM mmio. */ - this->gicd = GetPointer(KMemoryLayout::GetInterruptDistributorAddress()); - this->gicc = GetPointer(KMemoryLayout::GetInterruptCpuInterfaceAddress()); + this->gicd = GetPointer(KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_InterruptDistributor)); + this->gicc = GetPointer(KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_InterruptCpuInterface)); /* Clear CTLRs. */ this->gicc->ctlr = 0; diff --git a/libraries/libmesosphere/source/arch/arm/kern_k_interrupt_controller.board.generic.cpp b/libraries/libmesosphere/source/arch/arm/kern_k_interrupt_controller.board.generic.cpp new file mode 100644 index 000000000..06c132cfb --- /dev/null +++ b/libraries/libmesosphere/source/arch/arm/kern_k_interrupt_controller.board.generic.cpp @@ -0,0 +1,19 @@ +/* + * 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 . + */ +#include + +/* Include the common implementation. */ +#include "../arm/kern_generic_interrupt_controller.inc" diff --git a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp index 170ae14e0..dd39f3b84 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_cpu.cpp @@ -37,6 +37,7 @@ namespace ams::kern::arch::arm64::cpu { constexpr KThreadTerminationInterruptHandler() : KInterruptHandler() { /* ... */ } virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override { + MESOSPHERE_UNUSED(interrupt_id); return nullptr; } }; @@ -68,6 +69,8 @@ namespace ams::kern::arch::arm64::cpu { /* Nintendo misuses this per their own API, but it's functional. */ virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override { + MESOSPHERE_UNUSED(interrupt_id); + if (this->which < 0) { this->counter = cpu::GetCycleCounter(); } else { @@ -85,7 +88,7 @@ namespace ams::kern::arch::arm64::cpu { public: enum class Operation { Idle, - InvalidateInstructionCache, + InstructionMemoryBarrier, StoreDataCache, FlushDataCache, }; @@ -145,49 +148,63 @@ namespace ams::kern::arch::arm64::cpu { } virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override { + MESOSPHERE_UNUSED(interrupt_id); this->ProcessOperation(); return nullptr; } void RequestOperation(Operation op) { KScopedLightLock lk(this->lock); - MESOSPHERE_ABORT_UNLESS(this->operation == Operation::Idle); - /* Send and wait for acknowledgement of request. */ - { - KScopedLightLock cv_lk(this->cv_lock); + + /* Create core masks for us to use. */ + constexpr u64 AllCoresMask = (1ul << cpu::NumCores) - 1ul; + const u64 other_cores_mask = AllCoresMask & ~(1ul << GetCurrentCoreId()); + + if ((op == Operation::InstructionMemoryBarrier) || (Kernel::GetState() == Kernel::State::Initializing)) { + /* Check that there's no on-going operation. */ + MESOSPHERE_ABORT_UNLESS(this->operation == Operation::Idle); MESOSPHERE_ABORT_UNLESS(this->target_cores == 0); /* Set operation. */ this->operation = op; - /* Create core masks for us to use. */ - constexpr u64 AllCoresMask = (1ul << cpu::NumCores) - 1ul; - const u64 other_cores_mask = AllCoresMask & ~(1ul << GetCurrentCoreId()); + /* For certain operations, we want to send an interrupt. */ + this->target_cores = other_cores_mask; - if ((op == Operation::InvalidateInstructionCache) || (Kernel::GetState() == Kernel::State::Initializing)) { - /* For certain operations, we want to send an interrupt. */ - this->target_cores = other_cores_mask; - DataSynchronizationBarrier(); - const u64 target_mask = this->target_cores; - DataSynchronizationBarrier(); - Kernel::GetInterruptManager().SendInterProcessorInterrupt(KInterruptName_CacheOperation, target_mask); - this->ProcessOperation(); - while (this->target_cores != 0) { - cpu::Yield(); - } - } else { - /* Request all cores. */ - this->target_cores = AllCoresMask; + const u64 target_mask = this->target_cores; + DataSynchronizationBarrier(); + Kernel::GetInterruptManager().SendInterProcessorInterrupt(KInterruptName_CacheOperation, target_mask); - /* Use the condvar. */ - this->cv.Broadcast(); - while (this->target_cores != 0) { - this->cv.Wait(std::addressof(this->cv_lock)); - } + this->ProcessOperation(); + while (this->target_cores != 0) { + cpu::Yield(); } + + /* Go idle again. */ + this->operation = Operation::Idle; + } else { + /* Lock condvar so that we can send and wait for acknowledgement of request. */ + KScopedLightLock cv_lk(this->cv_lock); + + /* Check that there's no on-going operation. */ + MESOSPHERE_ABORT_UNLESS(this->operation == Operation::Idle); + MESOSPHERE_ABORT_UNLESS(this->target_cores == 0); + + /* Set operation. */ + this->operation = op; + + /* Request all cores. */ + this->target_cores = AllCoresMask; + + /* Use the condvar. */ + this->cv.Broadcast(); + while (this->target_cores != 0) { + this->cv.Wait(std::addressof(this->cv_lock)); + } + + /* Go idle again. */ + this->operation = Operation::Idle; } - /* Go idle again. */ - this->operation = Operation::Idle; } }; @@ -269,7 +286,7 @@ namespace ams::kern::arch::arm64::cpu { switch (this->operation) { case Operation::Idle: break; - case Operation::InvalidateInstructionCache: + case Operation::InstructionMemoryBarrier: InstructionMemoryBarrier(); break; case Operation::StoreDataCache: @@ -281,6 +298,8 @@ namespace ams::kern::arch::arm64::cpu { DataSynchronizationBarrier(); break; } + + this->target_cores &= ~(1ul << GetCurrentCoreId()); } ALWAYS_INLINE void SetEventLocally() { @@ -323,6 +342,14 @@ namespace ams::kern::arch::arm64::cpu { return ResultSuccess(); } + ALWAYS_INLINE void InvalidateEntireInstructionCacheLocalImpl() { + __asm__ __volatile__("ic iallu" ::: "memory"); + } + + ALWAYS_INLINE void InvalidateEntireInstructionCacheGlobalImpl() { + __asm__ __volatile__("ic ialluis" ::: "memory"); + } + } void FlushEntireDataCacheSharedForInit() { @@ -333,11 +360,16 @@ namespace ams::kern::arch::arm64::cpu { return PerformCacheOperationBySetWayLocal(FlushDataCacheLineBySetWayImpl); } + void InvalidateEntireInstructionCacheForInit() { + InvalidateEntireInstructionCacheLocalImpl(); + EnsureInstructionConsistency(); + } + void StoreEntireCacheForInit() { PerformCacheOperationBySetWayLocal(StoreDataCacheLineBySetWayImpl); PerformCacheOperationBySetWayShared(StoreDataCacheLineBySetWayImpl); DataSynchronizationBarrierInnerShareable(); - InvalidateEntireInstructionCache(); + InvalidateEntireInstructionCacheForInit(); } void FlushEntireDataCache() { @@ -391,12 +423,23 @@ namespace ams::kern::arch::arm64::cpu { R_TRY(InvalidateInstructionCacheRange(start, end)); - /* Request the interrupt helper to invalidate, too. */ - g_cache_operation_handler.RequestOperation(KCacheHelperInterruptHandler::Operation::InvalidateInstructionCache); + /* Request the interrupt helper to perform an instruction memory barrier. */ + g_cache_operation_handler.RequestOperation(KCacheHelperInterruptHandler::Operation::InstructionMemoryBarrier); return ResultSuccess(); } + void InvalidateEntireInstructionCache() { + KScopedCoreMigrationDisable dm; + + /* Invalidate the instruction cache on all cores. */ + InvalidateEntireInstructionCacheGlobalImpl(); + EnsureInstructionConsistency(); + + /* Request the interrupt helper to perform an instruction memory barrier. */ + g_cache_operation_handler.RequestOperation(KCacheHelperInterruptHandler::Operation::InstructionMemoryBarrier); + } + void InitializeInterruptThreads(s32 core_id) { /* Initialize the cache operation handler. */ g_cache_operation_handler.Initialize(core_id); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index b728ca612..263a304ea 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -511,7 +511,7 @@ namespace ams::kern::arch::arm64 { KScopedSchedulerLock lk; /* Pin the current thread. */ - KScheduler::PinCurrentThread(GetCurrentProcessPointer()); + GetCurrentProcess().PinCurrentThread(); /* Set the interrupt flag for the thread. */ GetCurrentThread().SetInterruptFlag(); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_hardware_timer.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_hardware_timer.cpp index 5a1630758..40420496f 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_hardware_timer.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_hardware_timer.cpp @@ -24,6 +24,7 @@ namespace ams::kern::arch::arm64 { constexpr KHardwareTimerInterruptTask() : KInterruptTask() { /* ... */ } virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override { + MESOSPHERE_UNUSED(interrupt_id); return this; } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_controller.board.generic.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_controller.board.generic.cpp new file mode 100644 index 000000000..06c132cfb --- /dev/null +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_controller.board.generic.cpp @@ -0,0 +1,19 @@ +/* + * 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 . + */ +#include + +/* Include the common implementation. */ +#include "../arm/kern_generic_interrupt_controller.inc" diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp index f1995e103..51b2a4b36 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_interrupt_manager.cpp @@ -111,6 +111,9 @@ namespace ams::kern::arch::arm64 { const u32 raw_irq = this->interrupt_controller.GetIrq(); const s32 irq = KInterruptController::ConvertRawIrq(raw_irq); + /* Trace the interrupt. */ + MESOSPHERE_KTRACE_INTERRUPT(irq); + /* If the IRQ is spurious, we don't need to reschedule. */ if (irq < 0) { return false; @@ -180,7 +183,7 @@ namespace ams::kern::arch::arm64 { KScopedSchedulerLock sl; /* Pin the current thread. */ - KScheduler::PinCurrentThread(GetCurrentProcessPointer()); + GetCurrentProcess().PinCurrentThread(); /* Set the interrupt flag for the thread. */ GetCurrentThread().SetInterruptFlag(); diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp index 18e89bfad..f7722c7dc 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table.cpp @@ -160,6 +160,7 @@ namespace ams::kern::arch::arm64 { void KPageTable::Initialize(s32 core_id) { /* Nothing actually needed here. */ + MESOSPHERE_UNUSED(core_id); } Result KPageTable::InitializeForKernel(void *table, KVirtualAddress start, KVirtualAddress end) { @@ -181,7 +182,8 @@ namespace ams::kern::arch::arm64 { } Result KPageTable::InitializeForProcess(u32 id, ams::svc::CreateProcessFlag as_type, bool enable_aslr, bool from_back, KMemoryManager::Pool pool, KProcessAddress code_address, size_t code_size, KMemoryBlockSlabManager *mem_block_slab_manager, KBlockInfoManager *block_info_manager, KPageTableManager *pt_manager) { - /* Convert the address space type to a width. */ + /* The input ID isn't actually used. */ + MESOSPHERE_UNUSED(id); /* Get an ASID */ this->asid = g_asid_manager.Reserve(); @@ -364,6 +366,9 @@ namespace ams::kern::arch::arm64 { MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), L1BlockSize)); MESOSPHERE_ASSERT(util::IsAligned(num_pages * PageSize, L1BlockSize)); + /* Allocation is never needed for L1 block mapping. */ + MESOSPHERE_UNUSED(page_list, reuse_ll); + auto &impl = this->GetImpl(); /* Iterate, mapping each block. */ diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp index 90ad97c8a..4009dce74 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_thread_context.cpp @@ -281,6 +281,7 @@ namespace ams::kern::arch::arm64 { } void KThreadContext::OnThreadTerminating(const KThread *thread) { + MESOSPHERE_UNUSED(thread); /* ... */ } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s b/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s new file mode 100644 index 000000000..7d317ac60 --- /dev/null +++ b/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s @@ -0,0 +1,88 @@ +/* + * 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 . + */ +#include + +#if defined(MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP) + +#define MESOSPHERE_GENERATE_PANIC_EXCEPTION_CONTEXT \ + /* Save x0/x1 to stack. */ \ + stp x0, x1, [sp, #-16]!; \ + \ + /* Get the exception context for the core. */ \ + adr x0, _ZN3ams4kern26g_panic_exception_contextsE; \ + mrs x1, mpidr_el1; \ + and x1, x1, #0xFF; \ + lsl x1, x1, #0x8; \ + add x0, x0, x1; \ + lsr x1, x1, #0x3; \ + add x0, x0, x1; \ + \ + /* Save x0/x1/sp to the context. */ \ + ldr x1, [sp, #(8 * 0)]; \ + str x1, [x0, #(8 * 0)]; \ + ldr x1, [sp, #(8 * 1)]; \ + str x1, [x0, #(8 * 1)]; \ + \ + /* Save all other registers to the context. */ \ + stp x2, x3, [x0, #(8 * 2)]; \ + stp x4, x5, [x0, #(8 * 4)]; \ + stp x6, x7, [x0, #(8 * 6)]; \ + stp x8, x9, [x0, #(8 * 8)]; \ + stp x10, x11, [x0, #(8 * 10)]; \ + stp x12, x13, [x0, #(8 * 12)]; \ + stp x14, x15, [x0, #(8 * 14)]; \ + stp x16, x17, [x0, #(8 * 16)]; \ + stp x18, x19, [x0, #(8 * 18)]; \ + stp x20, x21, [x0, #(8 * 20)]; \ + stp x22, x23, [x0, #(8 * 22)]; \ + stp x24, x25, [x0, #(8 * 24)]; \ + stp x26, x27, [x0, #(8 * 26)]; \ + stp x28, x29, [x0, #(8 * 28)]; \ + \ + add x1, sp, #16; \ + stp x30, x1, [x0, #(8 * 30)]; \ + \ + /* Restore x0/x1. */ \ + ldp x0, x1, [sp], #16; + +#else + +#define MESOSPHERE_GENERATE_PANIC_EXCEPTION_CONTEXT + +#endif + +/* ams::kern::Panic(const char *file, int line, const char *format, ...) */ +.section .text._ZN3ams4kern5PanicEPKciS2_z, "ax", %progbits +.global _ZN3ams4kern5PanicEPKciS2_z +.type _ZN3ams4kern5PanicEPKciS2_z, %function +_ZN3ams4kern5PanicEPKciS2_z: + /* Generate the exception context. */ + MESOSPHERE_GENERATE_PANIC_EXCEPTION_CONTEXT + + /* Jump to the architecturally-common implementation function. */ + b _ZN3ams4kern9PanicImplEPKciS2_z + + +/* ams::kern::Panic() */ +.section .text._ZN3ams4kern5PanicEv, "ax", %progbits +.global _ZN3ams4kern5PanicEv +.type _ZN3ams4kern5PanicEv, %function +_ZN3ams4kern5PanicEv: + /* Generate the exception context. */ + MESOSPHERE_GENERATE_PANIC_EXCEPTION_CONTEXT + + /* Jump to the architecturally-common implementation function. */ + b _ZN3ams4kern9PanicImplEv diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers.cpp new file mode 100644 index 000000000..72723557f --- /dev/null +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers.cpp @@ -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 . + */ +#include + +namespace ams::kern::svc { + + void TraceSvcEntry(const u64 *data) { + MESOSPHERE_KTRACE_SVC_ENTRY(GetCurrentThread().GetSvcId(), data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + } + + void TraceSvcExit(const u64 *data) { + MESOSPHERE_KTRACE_SVC_EXIT(GetCurrentThread().GetSvcId(), data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + } + +} diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s index 39d8eda7e..db0692c6f 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_handlers_asm.s @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include /* ams::kern::arch::arm64::SvcHandler64() */ .section .text._ZN3ams4kern4arch5arm6412SvcHandler64Ev, "ax", %progbits @@ -83,6 +84,24 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: strb w10, [sp, #(0x120 + 0x12)] strb w8, [sp, #(0x120 + 0x11)] + /* If we should, trace the svc entry. */ +#if defined(MESOSPHERE_BUILD_FOR_TRACING) + sub sp, sp, #0x50 + stp x0, x1, [sp, #(8 * 0)] + stp x2, x3, [sp, #(8 * 2)] + stp x4, x5, [sp, #(8 * 4)] + stp x6, x7, [sp, #(8 * 6)] + str x11, [sp, #(8 * 8)] + mov x0, sp + bl _ZN3ams4kern3svc13TraceSvcEntryEPKm + ldp x0, x1, [sp, #(8 * 0)] + ldp x2, x3, [sp, #(8 * 2)] + ldp x4, x5, [sp, #(8 * 4)] + ldp x6, x7, [sp, #(8 * 6)] + ldr x11, [sp, #(8 * 8)] + add sp, sp, #0x50 +#endif + /* Invoke the SVC handler. */ msr daifclr, #2 blr x11 @@ -163,6 +182,22 @@ _ZN3ams4kern4arch5arm6412SvcHandler64Ev: /* Clear our in-SVC note. */ strb wzr, [sp, #(0x120 + 0x12)] + /* If we should, trace the svc exit. */ +#if defined(MESOSPHERE_BUILD_FOR_TRACING) + sub sp, sp, #0x40 + stp x0, x1, [sp, #(8 * 0)] + stp x2, x3, [sp, #(8 * 2)] + stp x4, x5, [sp, #(8 * 4)] + stp x6, x7, [sp, #(8 * 6)] + mov x0, sp + bl _ZN3ams4kern3svc12TraceSvcExitEPKm + ldp x0, x1, [sp, #(8 * 0)] + ldp x2, x3, [sp, #(8 * 2)] + ldp x4, x5, [sp, #(8 * 4)] + ldp x6, x7, [sp, #(8 * 6)] + add sp, sp, #0x40 +#endif + /* Restore registers. */ ldp x30, x8, [sp, #(8 * 30)] ldp x9, x10, [sp, #(8 * 32)] @@ -259,6 +294,24 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: strb w15, [sp, #(0x120 + 0x12)] strb w16, [sp, #(0x120 + 0x11)] + /* If we should, trace the svc entry. */ +#if defined(MESOSPHERE_BUILD_FOR_TRACING) + sub sp, sp, #0x50 + stp x0, x1, [sp, #(8 * 0)] + stp x2, x3, [sp, #(8 * 2)] + stp x4, x5, [sp, #(8 * 4)] + stp x6, x7, [sp, #(8 * 6)] + str x19, [sp, #(8 * 8)] + mov x0, sp + bl _ZN3ams4kern3svc13TraceSvcEntryEPKm + ldp x0, x1, [sp, #(8 * 0)] + ldp x2, x3, [sp, #(8 * 2)] + ldp x4, x5, [sp, #(8 * 4)] + ldp x6, x7, [sp, #(8 * 6)] + ldr x19, [sp, #(8 * 8)] + add sp, sp, #0x50 +#endif + /* Invoke the SVC handler. */ msr daifclr, #2 blr x19 @@ -327,6 +380,22 @@ _ZN3ams4kern4arch5arm6412SvcHandler32Ev: /* Clear our in-SVC note. */ strb wzr, [sp, #(0x120 + 0x12)] + /* If we should, trace the svc exit. */ +#if defined(MESOSPHERE_BUILD_FOR_TRACING) + sub sp, sp, #0x40 + stp x0, x1, [sp, #(8 * 0)] + stp x2, x3, [sp, #(8 * 2)] + stp x4, x5, [sp, #(8 * 4)] + stp x6, x7, [sp, #(8 * 6)] + mov x0, sp + bl _ZN3ams4kern3svc12TraceSvcExitEPKm + ldp x0, x1, [sp, #(8 * 0)] + ldp x2, x3, [sp, #(8 * 2)] + ldp x4, x5, [sp, #(8 * 4)] + ldp x6, x7, [sp, #(8 * 6)] + add sp, sp, #0x40 +#endif + /* Restore registers. */ ldp x8, x9, [sp, #(8 * 8)] ldp x10, x11, [sp, #(8 * 10)] diff --git a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp index 7f9397d6f..db2842e1b 100644 --- a/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp +++ b/libraries/libmesosphere/source/arch/arm64/svc/kern_svc_tables.cpp @@ -147,6 +147,13 @@ namespace ams::kern::svc { if (table_64_from_32) { ::ams::kern::svc::PatchSvcTableEntry(table_64_from_32, svc::SvcId_QueryIoMapping, LegacyQueryIoMapping::Call64From32); } } + /* 6.0.0 broke the ABI for GetFutureThreadInfo, and renamed it to GetDebugFutureThreadInfo. */ + if (target_fw < TargetFirmware_6_0_0) { + static_assert(svc::SvcId_GetDebugFutureThreadInfo == svc::SvcId_LegacyGetFutureThreadInfo); + if (table_64) { ::ams::kern::svc::PatchSvcTableEntry(table_64, svc::SvcId_GetDebugFutureThreadInfo, LegacyGetFutureThreadInfo::Call64); } + if (table_64_from_32) { ::ams::kern::svc::PatchSvcTableEntry(table_64_from_32, svc::SvcId_GetDebugFutureThreadInfo, LegacyGetFutureThreadInfo::Call64From32); } + } + /* 3.0.0 broke the ABI for ContinueDebugEvent. */ if (target_fw < TargetFirmware_3_0_0) { if (table_64) { ::ams::kern::svc::PatchSvcTableEntry(table_64, svc::SvcId_ContinueDebugEvent, LegacyContinueDebugEvent::Call64); } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_atomics_registers.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_atomics_registers.hpp new file mode 100644 index 000000000..ee6564f59 --- /dev/null +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_atomics_registers.hpp @@ -0,0 +1,21 @@ +/* + * 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 . + */ +#pragma once + +#define ATOMICS_AP0_TRIGGER 0x000 +#define ATOMICS_AP0_RESULT(id) (0xc00 + id * 4) + +#define TRIGGER_CMD_GET 4 diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_bpmp_api.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_bpmp_api.hpp new file mode 100644 index 000000000..d06c35a8a --- /dev/null +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_bpmp_api.hpp @@ -0,0 +1,63 @@ +/* + * 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 . + */ +#pragma once +#include + +/* Message Flags */ +#define BPMP_MSG_DO_ACK (1 << 0) +#define BPMP_MSG_RING_DOORBELL (1 << 1) + +/* Messages */ +#define MRQ_PING 0 +#define MRQ_ENABLE_SUSPEND 17 +#define MRQ_CPU_PMIC_SELECT 28 + +/* BPMP Power states. */ +#define TEGRA_BPMP_PM_CC1 9 +#define TEGRA_BPMP_PM_CC4 12 +#define TEGRA_BPMP_PM_CC6 14 +#define TEGRA_BPMP_PM_CC7 15 +#define TEGRA_BPMP_PM_SC1 17 +#define TEGRA_BPMP_PM_SC2 18 +#define TEGRA_BPMP_PM_SC3 19 +#define TEGRA_BPMP_PM_SC4 20 +#define TEGRA_BPMP_PM_SC7 23 + +/* Channel states. */ +#define CH_MASK(ch) (0x3u << ((ch) * 2)) +#define SL_SIGL(ch) (0x0u << ((ch) * 2)) +#define SL_QUED(ch) (0x1u << ((ch) * 2)) +#define MA_FREE(ch) (0x2u << ((ch) * 2)) +#define MA_ACKD(ch) (0x3u << ((ch) * 2)) + +constexpr inline int MessageSize = 0x80; +constexpr inline int MessageDataSizeMax = 0x78; + +struct MailboxData { + s32 code; + s32 flags; + u8 data[MessageDataSizeMax]; +}; + +static_assert(ams::util::is_pod::value); +static_assert(sizeof(MailboxData) == MessageSize); + +struct ChannelData { + MailboxData *ib; + MailboxData *ob; +}; + + diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_ictlr_registers.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_ictlr_registers.hpp new file mode 100644 index 000000000..3e63d8692 --- /dev/null +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_ictlr_registers.hpp @@ -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 . + */ +#pragma once + +#define ICTLR_REG_BASE(irq) ((((irq) - 32) >> 5) * 0x100) +#define ICTLR_FIR_SET(irq) (ICTLR_REG_BASE(irq) + 0x18) +#define ICTLR_FIR_CLR(irq) (ICTLR_REG_BASE(irq) + 0x1c) +#define FIR_BIT(irq) (1 << ((irq) & 0x1f)) + +#define INT_GIC_BASE (0) +#define INT_PRI_BASE (INT_GIC_BASE + 32) +#define INT_SHR_SEM_OUTBOX_IBF (INT_PRI_BASE + 6) diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp index 4ada48c89..0557bb305 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_device_page_table.cpp @@ -14,7 +14,10 @@ * along with this program. If not, see . */ #include -#include "kern_mc_registers.hpp" + +#if defined(MESOSPHERE_BUILD_FOR_DEBUGGING) || defined(MESOSPHERE_BUILD_FOR_AUDITING) +#define MESOSPHERE_ENABLE_MEMORY_CONTROLLER_INTERRUPT +#endif namespace ams::kern::board::nintendo::nx { @@ -332,13 +335,13 @@ namespace ams::kern::board::nintendo::nx { }; /* Globals. */ - KLightLock g_lock; - u8 g_reserved_asid; - KPhysicalAddress g_memory_controller_address; - KPhysicalAddress g_reserved_table_phys_addr; - KDeviceAsidManager g_asid_manager; - u32 g_saved_page_tables[AsidCount]; - u32 g_saved_asid_registers[ams::svc::DeviceName_Count]; + constinit KLightLock g_lock; + constinit u8 g_reserved_asid; + constinit KPhysicalAddress g_memory_controller_address; + constinit KPhysicalAddress g_reserved_table_phys_addr; + constinit KDeviceAsidManager g_asid_manager; + constinit u32 g_saved_page_tables[AsidCount]; + constinit u32 g_saved_asid_registers[ams::svc::DeviceName_Count]; /* Memory controller access functionality. */ void WriteMcRegister(size_t offset, u32 value) { @@ -349,6 +352,237 @@ namespace ams::kern::board::nintendo::nx { return KSystemControl::ReadRegisterPrivileged(GetInteger(g_memory_controller_address) + offset); } + /* Memory controller interrupt functionality. */ + + constexpr const char * const MemoryControllerClientNames[138] = { + [ 0] = "csr_ptcr (ptc)", + [ 1] = "csr_display0a (dc)", + [ 2] = "csr_display0ab (dcb)", + [ 3] = "csr_display0b (dc)", + [ 4] = "csr_display0bb (dcb)", + [ 5] = "csr_display0c (dc)", + [ 6] = "csr_display0cb (dcb)", + [ 7] = "Unknown Client", + [ 8] = "Unknown Client", + [ 9] = "Unknown Client", + [ 10] = "Unknown Client", + [ 11] = "Unknown Client", + [ 12] = "Unknown Client", + [ 13] = "Unknown Client", + [ 14] = "csr_afir (afi)", + [ 15] = "csr_avpcarm7r (avpc)", + [ 16] = "csr_displayhc (dc)", + [ 17] = "csr_displayhcb (dcb)", + [ 18] = "Unknown Client", + [ 19] = "Unknown Client", + [ 20] = "Unknown Client", + [ 21] = "csr_hdar (hda)", + [ 22] = "csr_host1xdmar (hc)", + [ 23] = "csr_host1xr (hc)", + [ 24] = "Unknown Client", + [ 25] = "Unknown Client", + [ 26] = "Unknown Client", + [ 27] = "Unknown Client", + [ 28] = "csr_nvencsrd (nvenc)", + [ 29] = "csr_ppcsahbdmar (ppcs)", + [ 30] = "csr_ppcsahbslvr (ppcs)", + [ 31] = "csr_satar (sata)", + [ 32] = "Unknown Client", + [ 33] = "Unknown Client", + [ 34] = "Unknown Client", + [ 35] = "Unknown Client", + [ 36] = "Unknown Client", + [ 37] = "Unknown Client", + [ 38] = "Unknown Client", + [ 39] = "csr_mpcorer (cpu)", + [ 40] = "Unknown Client", + [ 41] = "Unknown Client", + [ 42] = "Unknown Client", + [ 43] = "csw_nvencswr (nvenc)", + [ 44] = "Unknown Client", + [ 45] = "Unknown Client", + [ 46] = "Unknown Client", + [ 47] = "Unknown Client", + [ 48] = "Unknown Client", + [ 49] = "csw_afiw (afi)", + [ 50] = "csw_avpcarm7w (avpc)", + [ 51] = "Unknown Client", + [ 52] = "Unknown Client", + [ 53] = "csw_hdaw (hda)", + [ 54] = "csw_host1xw (hc)", + [ 55] = "Unknown Client", + [ 56] = "Unknown Client", + [ 57] = "csw_mpcorew (cpu)", + [ 58] = "Unknown Client", + [ 59] = "csw_ppcsahbdmaw (ppcs)", + [ 60] = "csw_ppcsahbslvw (ppcs)", + [ 61] = "csw_sataw (sata)", + [ 62] = "Unknown Client", + [ 63] = "Unknown Client", + [ 64] = "Unknown Client", + [ 65] = "Unknown Client", + [ 66] = "Unknown Client", + [ 67] = "Unknown Client", + [ 68] = "csr_ispra (isp2)", + [ 69] = "Unknown Client", + [ 70] = "csw_ispwa (isp2)", + [ 71] = "csw_ispwb (isp2)", + [ 72] = "Unknown Client", + [ 73] = "Unknown Client", + [ 74] = "csr_xusb_hostr (xusb_host)", + [ 75] = "csw_xusb_hostw (xusb_host)", + [ 76] = "csr_xusb_devr (xusb_dev)", + [ 77] = "csw_xusb_devw (xusb_dev)", + [ 78] = "csr_isprab (isp2b)", + [ 79] = "Unknown Client", + [ 80] = "csw_ispwab (isp2b)", + [ 81] = "csw_ispwbb (isp2b)", + [ 82] = "Unknown Client", + [ 83] = "Unknown Client", + [ 84] = "csr_tsecsrd (tsec)", + [ 85] = "csw_tsecswr (tsec)", + [ 86] = "csr_a9avpscr (a9avp)", + [ 87] = "csw_a9avpscw (a9avp)", + [ 88] = "csr_gpusrd (gpu)", + [ 89] = "csw_gpuswr (gpu)", + [ 90] = "csr_displayt (dc)", + [ 91] = "Unknown Client", + [ 92] = "Unknown Client", + [ 93] = "Unknown Client", + [ 94] = "Unknown Client", + [ 95] = "Unknown Client", + [ 96] = "csr_sdmmcra (sdmmc1a)", + [ 97] = "csr_sdmmcraa (sdmmc2a)", + [ 98] = "csr_sdmmcr (sdmmc3a)", + [ 99] = "csr_sdmmcrab (sdmmc4a)", + [100] = "csw_sdmmcwa (sdmmc1a)", + [101] = "csw_sdmmcwaa (sdmmc2a)", + [102] = "csw_sdmmcw (sdmmc3a)", + [103] = "csw_sdmmcwab (sdmmc4a)", + [104] = "Unknown Client", + [105] = "Unknown Client", + [106] = "Unknown Client", + [107] = "Unknown Client", + [108] = "csr_vicsrd (vic)", + [109] = "csw_vicswr (vic)", + [110] = "Unknown Client", + [111] = "Unknown Client", + [112] = "Unknown Client", + [113] = "Unknown Client", + [114] = "csw_viw (vi)", + [115] = "csr_displayd (dc)", + [116] = "Unknown Client", + [117] = "Unknown Client", + [118] = "Unknown Client", + [119] = "Unknown Client", + [120] = "csr_nvdecsrd (nvdec)", + [121] = "csw_nvdecswr (nvdec)", + [122] = "csr_aper (ape)", + [123] = "csw_apew (ape)", + [124] = "Unknown Client", + [125] = "Unknown Client", + [126] = "csr_nvjpgsrd (nvjpg)", + [127] = "csw_nvjpgswr (nvjpg)", + [128] = "csr_sesrd (se)", + [129] = "csw_seswr (se)", + [130] = "csr_axiapr (axiap)", + [131] = "csw_axiapw (axiap)", + [132] = "csr_etrr (etr)", + [133] = "csw_etrw (etr)", + [134] = "csr_tsecsrdb (tsecb)", + [135] = "csw_tsecswrb (tsecb)", + [136] = "csr_gpusrd2 (gpu)", + [137] = "csw_gpuswr2 (gpu)", + }; + + constexpr const char * GetMemoryControllerClientName(size_t i) { + if (i < util::size(MemoryControllerClientNames)) { + return MemoryControllerClientNames[i]; + } + return "Unknown Client"; + } + + constexpr const char * const MemoryControllerErrorTypes[8] = { + "RSVD", + "Unknown", + "DECERR_EMEM", + "SECURITY_TRUSTZONE", + "SECURITY_CARVEOUT", + "Unknown", + "INVALID_SMMU_PAGE", + "Unknown", + }; + + class KMemoryControllerInterruptTask : public KInterruptTask { + public: + constexpr KMemoryControllerInterruptTask() : KInterruptTask() { /* ... */ } + + virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override { + MESOSPHERE_UNUSED(interrupt_id); + return this; + } + + virtual void DoTask() override { + #if defined(MESOSPHERE_ENABLE_MEMORY_CONTROLLER_INTERRUPT) + { + /* Clear the interrupt when we're done. */ + ON_SCOPE_EXIT { Kernel::GetInterruptManager().ClearInterrupt(KInterruptName_MemoryController); }; + + /* Get and clear the interrupt status. */ + u32 int_status, err_status, err_adr; + { + int_status = ReadMcRegister(MC_INTSTATUS); + err_status = ReadMcRegister(MC_ERR_STATUS); + err_adr = ReadMcRegister(MC_ERR_ADR); + + WriteMcRegister(MC_INTSTATUS, int_status); + } + + /* Print the interrupt. */ + { + constexpr auto GetBits = [] ALWAYS_INLINE_LAMBDA (u32 value, size_t ofs, size_t count) { + return (value >> ofs) & ((1u << count) - 1); + }; + + constexpr auto GetBit = [GetBits] ALWAYS_INLINE_LAMBDA (u32 value, size_t ofs) { + return (value >> ofs) & 1u; + }; + + MESOSPHERE_RELEASE_LOG("sMMU error interrupt\n"); + MESOSPHERE_RELEASE_LOG(" MC_INTSTATUS=%08x\n", int_status); + MESOSPHERE_RELEASE_LOG(" DECERR_GENERALIZED_CARVEOUT=%d\n", GetBit(int_status, 17)); + MESOSPHERE_RELEASE_LOG(" DECERR_MTS=%d\n", GetBit(int_status, 16)); + MESOSPHERE_RELEASE_LOG(" SECERR_SEC=%d\n", GetBit(int_status, 13)); + MESOSPHERE_RELEASE_LOG(" DECERR_VPR=%d\n", GetBit(int_status, 12)); + MESOSPHERE_RELEASE_LOG(" INVALID_APB_ASID_UPDATE=%d\n", GetBit(int_status, 11)); + MESOSPHERE_RELEASE_LOG(" INVALID_SMMU_PAGE=%d\n", GetBit(int_status, 10)); + MESOSPHERE_RELEASE_LOG(" ARBITRATION_EMEM=%d\n", GetBit(int_status, 9)); + MESOSPHERE_RELEASE_LOG(" SECURITY_VIOLATION=%d\n", GetBit(int_status, 8)); + MESOSPHERE_RELEASE_LOG(" DECERR_EMEM=%d\n", GetBit(int_status, 6)); + MESOSPHERE_RELEASE_LOG(" MC_ERRSTATUS=%08x\n", err_status); + MESOSPHERE_RELEASE_LOG(" ERR_TYPE=%d (%s)\n", GetBits(err_status, 28, 3), MemoryControllerErrorTypes[GetBits(err_status, 28, 3)]); + MESOSPHERE_RELEASE_LOG(" ERR_INVALID_SMMU_PAGE_READABLE=%d\n", GetBit (err_status, 27)); + MESOSPHERE_RELEASE_LOG(" ERR_INVALID_SMMU_PAGE_WRITABLE=%d\n", GetBit (err_status, 26)); + MESOSPHERE_RELEASE_LOG(" ERR_INVALID_SMMU_NONSECURE=%d\n", GetBit (err_status, 25)); + MESOSPHERE_RELEASE_LOG(" ERR_ADR_HI=%x\n", GetBits(err_status, 20, 2)); + MESOSPHERE_RELEASE_LOG(" ERR_SWAP=%d\n", GetBit (err_status, 18)); + MESOSPHERE_RELEASE_LOG(" ERR_SECURITY=%d %s\n", GetBit (err_status, 17), GetBit(err_status, 17) ? "SECURE" : "NONSECURE"); + MESOSPHERE_RELEASE_LOG(" ERR_RW=%d %s\n", GetBit (err_status, 16), GetBit(err_status, 16) ? "WRITE" : "READ"); + MESOSPHERE_RELEASE_LOG(" ERR_ADR1=%x\n", GetBits(err_status, 12, 3)); + MESOSPHERE_RELEASE_LOG(" ERR_ID=%d %s\n", GetBits(err_status, 0, 8), GetMemoryControllerClientName(GetBits(err_status, 0, 8))); + MESOSPHERE_RELEASE_LOG(" MC_ERRADR=%08x\n", err_adr); + MESOSPHERE_RELEASE_LOG(" ERR_ADR=%lx\n", (static_cast(GetBits(err_status, 20, 2)) << 32) | static_cast(err_adr)); + MESOSPHERE_RELEASE_LOG("\n"); + } + } + #endif + } + }; + + /* Interrupt task global. */ + constinit KMemoryControllerInterruptTask g_mc_interrupt_task; + + /* Memory controller utilities. */ void SmmuSynchronizationBarrier() { ReadMcRegister(MC_SMMU_CONFIG); } @@ -405,7 +639,7 @@ namespace ams::kern::board::nintendo::nx { void KDevicePageTable::Initialize() { /* Set the memory controller register address. */ - g_memory_controller_address = KMemoryLayout::GetMemoryControllerRegion().GetAddress(); + g_memory_controller_address = KMemoryLayout::GetDevicePhysicalAddress(KMemoryRegionType_MemoryController); /* Allocate a page to use as a reserved/no device table. */ const KVirtualAddress table_virt_addr = Kernel::GetPageTableManager().Allocate(); @@ -452,11 +686,23 @@ namespace ams::kern::board::nintendo::nx { /* Clear int status. */ WriteMcRegister(MC_INTSTATUS, ReadMcRegister(MC_INTSTATUS)); + /* If we're setting an interrupt handler, unmask all interrupts. */ + #if defined(MESOSPHERE_ENABLE_MEMORY_CONTROLLER_INTERRUPT) + { + WriteMcRegister(MC_INTMASK, 0x33D40); + } + #endif + /* Enable the SMMU */ WriteMcRegister(MC_SMMU_CONFIG, 1); SmmuSynchronizationBarrier(); - /* TODO: Install interrupt handler. */ + /* Install interrupt handler. */ + #if defined(MESOSPHERE_ENABLE_MEMORY_CONTROLLER_INTERRUPT) + { + Kernel::GetInterruptManager().BindHandler(std::addressof(g_mc_interrupt_task), KInterruptName_MemoryController, GetCurrentCoreId(), KInterruptController::PriorityLevel_High, true, true); + } + #endif } void KDevicePageTable::Lock() { diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp index 0e3f4f602..913137d5f 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.cpp @@ -16,6 +16,7 @@ #include #include "kern_k_sleep_manager.hpp" #include "kern_secure_monitor.hpp" +#include "kern_lps_driver.hpp" namespace ams::kern::board::nintendo::nx { @@ -77,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); } @@ -472,7 +471,8 @@ namespace ams::kern::board::nintendo::nx { } void KSleepManager::ProcessRequests(uintptr_t buffer) { - const s32 core_id = GetCurrentCoreId(); + const auto target_fw = GetTargetFirmware(); + const s32 core_id = GetCurrentCoreId(); KPhysicalAddress resume_entry_phys_addr = Null; /* Get the physical addresses we'll need. */ @@ -484,6 +484,8 @@ namespace ams::kern::board::nintendo::nx { const KPhysicalAddress sleep_buffer_phys_addr = g_sleep_buffer_phys_addrs[core_id]; const u64 target_core_mask = (1ul << core_id); + const bool use_legacy_lps_driver = target_fw < TargetFirmware_2_0_0; + /* Loop, processing sleep when requested. */ while (true) { /* Wait for a request. */ @@ -494,6 +496,11 @@ namespace ams::kern::board::nintendo::nx { } } + /* If on core 0, ensure the legacy lps driver is initialized. */ + if (use_legacy_lps_driver && core_id == 0) { + lps::Initialize(); + } + /* Perform Sleep/Wake sequence. */ { /* Disable interrupts. */ @@ -550,6 +557,11 @@ namespace ams::kern::board::nintendo::nx { /* Wait for all other cores to be powered off. */ WaitOtherCpuPowerOff(); + /* If we're using the legacy lps driver, enable suspend. */ + if (use_legacy_lps_driver) { + MESOSPHERE_R_ABORT_UNLESS(lps::EnableSuspend(true)); + } + /* Log that we're about to enter SC7. */ MESOSPHERE_LOG("Entering SC7\n"); @@ -557,7 +569,12 @@ namespace ams::kern::board::nintendo::nx { KDebugLog::Save(); /* Invoke the sleep handler. */ - CpuSleepHandler(GetInteger(sleep_buffer_phys_addr), GetInteger(resume_entry_phys_addr)); + if (!use_legacy_lps_driver) { + /* When not using the legacy driver, invoke directly. */ + CpuSleepHandler(GetInteger(sleep_buffer_phys_addr), GetInteger(resume_entry_phys_addr)); + } else { + lps::InvokeCpuSleepHandler(GetInteger(sleep_buffer_phys_addr), GetInteger(resume_entry_phys_addr)); + } /* Restore the debug log state. */ KDebugLog::Restore(); @@ -586,6 +603,11 @@ namespace ams::kern::board::nintendo::nx { /* If on core 0, wake up the device page tables. */ if (core_id == 0) { KDevicePageTable::Wakeup(); + + /* If we're using the legacy driver, resume the bpmp firmware. */ + if (use_legacy_lps_driver) { + lps::ResumeBpmpFirmware(); + } } /* Ensure that all cores get to this point before continuing. */ diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.hpp index f39ce7b67..64f3ddcb6 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.hpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_sleep_manager.hpp @@ -20,7 +20,6 @@ namespace ams::kern::board::nintendo::nx { class KSleepManager { private: - static void CpuSleepHandler(uintptr_t arg, uintptr_t entry); static void ResumeEntry(uintptr_t arg); static void InvalidateDataCacheForResumeEntry(uintptr_t level); @@ -29,6 +28,8 @@ namespace ams::kern::board::nintendo::nx { public: static void Initialize(); static void SleepSystem(); + public: + static void CpuSleepHandler(uintptr_t arg, uintptr_t entry); }; diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index ca6e0f58c..6f4987ed9 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -179,12 +179,12 @@ namespace ams::kern::board::nintendo::nx { bool IsRegisterAccessibleToPrivileged(ams::svc::PhysicalAddress address) { /* Find the region for the address. */ - KMemoryRegionTree::const_iterator it = KMemoryLayout::FindContainingRegion(KPhysicalAddress(address)); - if (AMS_LIKELY(it != KMemoryLayout::GetPhysicalMemoryRegionTree().end())) { - if (AMS_LIKELY(it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_MemoryController))) { + const KMemoryRegion *region = KMemoryLayout::Find(KPhysicalAddress(address)); + if (AMS_LIKELY(region != nullptr)) { + if (AMS_LIKELY(region->IsDerivedFrom(KMemoryRegionType_MemoryController))) { /* Get the offset within the region. */ - const size_t offset = address - it->GetAddress(); - MESOSPHERE_ABORT_UNLESS(offset < it->GetSize()); + const size_t offset = address - region->GetAddress(); + MESOSPHERE_ABORT_UNLESS(offset < region->GetSize()); /* Check the whitelist. */ if (AMS_LIKELY(CheckRegisterAllowedTable(McKernelRegisterWhitelist, offset))) { @@ -198,21 +198,21 @@ namespace ams::kern::board::nintendo::nx { bool IsRegisterAccessibleToUser(ams::svc::PhysicalAddress address) { /* Find the region for the address. */ - KMemoryRegionTree::const_iterator it = KMemoryLayout::FindContainingRegion(KPhysicalAddress(address)); - if (AMS_LIKELY(it != KMemoryLayout::GetPhysicalMemoryRegionTree().end())) { + const KMemoryRegion *region = KMemoryLayout::Find(KPhysicalAddress(address)); + if (AMS_LIKELY(region != nullptr)) { /* The PMC is always allowed. */ - if (it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_PowerManagementController)) { + if (region->IsDerivedFrom(KMemoryRegionType_PowerManagementController)) { return true; } /* Memory controller is allowed if the register is whitelisted. */ - if (it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_MemoryController ) || - it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_MemoryController0) || - it->IsDerivedFrom(KMemoryRegionAttr_NoUserMap | KMemoryRegionType_MemoryController1)) + if (region->IsDerivedFrom(KMemoryRegionType_MemoryController ) || + region->IsDerivedFrom(KMemoryRegionType_MemoryController0) || + region->IsDerivedFrom(KMemoryRegionType_MemoryController1)) { /* Get the offset within the region. */ - const size_t offset = address - it->GetAddress(); - MESOSPHERE_ABORT_UNLESS(offset < it->GetSize()); + const size_t offset = address - region->GetAddress(); + MESOSPHERE_ABORT_UNLESS(offset < region->GetSize()); /* Check the whitelist. */ if (AMS_LIKELY(CheckRegisterAllowedTable(McUserRegisterWhitelist, offset))) { @@ -308,6 +308,15 @@ namespace ams::kern::board::nintendo::nx { g_secure_applet_memory_used = false; } + u64 GetVersionIdentifier() { + u64 value = kern::GetTargetFirmware(); + value |= static_cast(ATMOSPHERE_RELEASE_VERSION_MICRO) << 32; + value |= static_cast(ATMOSPHERE_RELEASE_VERSION_MINOR) << 40; + value |= static_cast(ATMOSPHERE_RELEASE_VERSION_MAJOR) << 48; + value |= static_cast('M') << 56; + return value; + } + } /* Initialization. */ @@ -338,44 +347,60 @@ namespace ams::kern::board::nintendo::nx { } size_t KSystemControl::Init::GetApplicationPoolSize() { - switch (GetMemoryArrangeForInit()) { - case smc::MemoryArrangement_4GB: - default: - return 3285_MB; - case smc::MemoryArrangement_4GBForAppletDev: - return 2048_MB; - case smc::MemoryArrangement_4GBForSystemDev: - return 3285_MB; - case smc::MemoryArrangement_6GB: - return 4916_MB; - case smc::MemoryArrangement_6GBForAppletDev: - return 3285_MB; - case smc::MemoryArrangement_8GB: - return 4916_MB; - } + /* Get the base pool size. */ + const size_t base_pool_size = [] ALWAYS_INLINE_LAMBDA () -> size_t { + switch (GetMemoryArrangeForInit()) { + case smc::MemoryArrangement_4GB: + default: + return 3285_MB; + case smc::MemoryArrangement_4GBForAppletDev: + return 2048_MB; + case smc::MemoryArrangement_4GBForSystemDev: + return 3285_MB; + case smc::MemoryArrangement_6GB: + return 4916_MB; + case smc::MemoryArrangement_6GBForAppletDev: + return 3285_MB; + case smc::MemoryArrangement_8GB: + return 4916_MB; + } + }(); + + /* Return (possibly) adjusted size. */ + return base_pool_size; } size_t KSystemControl::Init::GetAppletPoolSize() { - switch (GetMemoryArrangeForInit()) { - case smc::MemoryArrangement_4GB: - default: - return 507_MB; - case smc::MemoryArrangement_4GBForAppletDev: - return 1554_MB; - case smc::MemoryArrangement_4GBForSystemDev: - return 448_MB; - case smc::MemoryArrangement_6GB: - return 562_MB; - case smc::MemoryArrangement_6GBForAppletDev: - return 2193_MB; - case smc::MemoryArrangement_8GB: - return 2193_MB; - } + /* Get the base pool size. */ + const size_t base_pool_size = [] ALWAYS_INLINE_LAMBDA () -> size_t { + switch (GetMemoryArrangeForInit()) { + case smc::MemoryArrangement_4GB: + default: + return 507_MB; + case smc::MemoryArrangement_4GBForAppletDev: + return 1554_MB; + case smc::MemoryArrangement_4GBForSystemDev: + return 448_MB; + case smc::MemoryArrangement_6GB: + return 562_MB; + case smc::MemoryArrangement_6GBForAppletDev: + return 2193_MB; + case smc::MemoryArrangement_8GB: + return 2193_MB; + } + }(); + + /* Return (possibly) adjusted size. */ + constexpr size_t ExtraSystemMemoryForAtmosphere = 33_MB; + return base_pool_size - ExtraSystemMemoryForAtmosphere - KTraceBufferSize; } size_t KSystemControl::Init::GetMinimumNonSecureSystemPoolSize() { - /* TODO: Where does this constant actually come from? */ - return 0x29C8000; + /* Verify that our minimum is at least as large as Nintendo's. */ + constexpr size_t MinimumSize = ::ams::svc::RequiredNonSecureSystemMemorySize; + static_assert(MinimumSize >= 0x29C8000); + + return MinimumSize; } void KSystemControl::Init::CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg) { @@ -453,7 +478,7 @@ namespace ams::kern::board::nintendo::nx { KSleepManager::Initialize(); /* Reserve secure applet memory. */ - { + if (GetTargetFirmware() >= TargetFirmware_5_0_0) { MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address == Null); MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, SecureAppletMemorySize)); @@ -461,10 +486,16 @@ namespace ams::kern::board::nintendo::nx { g_secure_applet_memory_address = Kernel::GetMemoryManager().AllocateContinuous(SecureAppletMemorySize / PageSize, 1, SecureAppletAllocateOption); MESOSPHERE_ABORT_UNLESS(g_secure_applet_memory_address != Null); } + + /* Initialize KTrace. */ + if constexpr (IsKTraceEnabled) { + const auto &ktrace = KMemoryLayout::GetKernelTraceBufferRegion(); + KTrace::Initialize(ktrace.GetAddress(), ktrace.GetSize()); + } } - u32 KSystemControl::GetInitialProcessBinaryPool() { - return KMemoryManager::Pool_Application; + u32 KSystemControl::GetCreateProcessMemoryPool() { + return KMemoryManager::Pool_Unsafe; } /* Privileged Access. */ @@ -510,14 +541,91 @@ namespace ams::kern::board::nintendo::nx { KSleepManager::SleepSystem(); } - void KSystemControl::StopSystem() { + void KSystemControl::StopSystem(void *arg) { + if (arg != nullptr) { + /* Get the address of the legacy IRAM region. */ + const KVirtualAddress iram_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsIram) + 64_KB; + constexpr size_t RebootPayloadSize = 0x2E000; + + /* NOTE: Atmosphere extension; if we received an exception context from Panic(), */ + /* generate a fatal error report using it. */ + const KExceptionContext *e_ctx = static_cast(arg); + auto *f_ctx = GetPointer<::ams::impl::FatalErrorContext>(iram_address + RebootPayloadSize); + + /* Clear the fatal context. */ + std::memset(f_ctx, 0xCC, sizeof(*f_ctx)); + + /* Set metadata. */ + f_ctx->magic = ::ams::impl::FatalErrorContext::Magic; + f_ctx->error_desc = ::ams::impl::FatalErrorContext::KernelPanicDesc; + f_ctx->program_id = (static_cast(util::FourCC<'M', 'E', 'S', 'O'>::Code) << 0) | (static_cast(util::FourCC<'S', 'P', 'H', 'R'>::Code) << 32); + + /* Set identifier. */ + f_ctx->report_identifier = KHardwareTimer::GetTick(); + + /* Set module base. */ + f_ctx->module_base = KMemoryLayout::GetKernelCodeRegionExtents().GetAddress(); + + /* Set afsr1. */ + f_ctx->afsr0 = 0; + f_ctx->afsr1 = GetVersionIdentifier(); + + /* Copy registers. */ + for (size_t i = 0; i < util::size(e_ctx->x); ++i) { + f_ctx->gprs[i] = e_ctx->x[i]; + } + f_ctx->sp = e_ctx->sp; + + /* Dump stack trace. */ + { + uintptr_t fp = e_ctx->x[29]; + for (f_ctx->stack_trace_size = 0; f_ctx->stack_trace_size < ::ams::impl::FatalErrorContext::MaxStackTrace && fp != 0 && util::IsAligned(fp, 0x10) && cpu::GetPhysicalAddressWritable(nullptr, fp, true); ++(f_ctx->stack_trace_size)) { + struct { + uintptr_t fp; + uintptr_t lr; + } *stack_frame = reinterpret_cast(fp); + + f_ctx->stack_trace[f_ctx->stack_trace_size] = stack_frame->lr; + fp = stack_frame->fp; + } + } + + /* Dump stack. */ + { + uintptr_t sp = e_ctx->sp; + for (f_ctx->stack_dump_size = 0; f_ctx->stack_dump_size < ::ams::impl::FatalErrorContext::MaxStackDumpSize && cpu::GetPhysicalAddressWritable(nullptr, sp + f_ctx->stack_dump_size, true); f_ctx->stack_dump_size += sizeof(u64)) { + *reinterpret_cast(f_ctx->stack_dump + f_ctx->stack_dump_size) = *reinterpret_cast(sp + f_ctx->stack_dump_size); + } + } + + /* Try to get a payload address. */ + const KMemoryRegion *cached_region = nullptr; + u64 reboot_payload_paddr = 0; + if (smc::TryGetConfig(std::addressof(reboot_payload_paddr), 1, smc::ConfigItem::ExospherePayloadAddress) && KMemoryLayout::IsLinearMappedPhysicalAddress(cached_region, reboot_payload_paddr, RebootPayloadSize)) { + /* If we have a payload, reboot to it. */ + const KVirtualAddress reboot_payload = KMemoryLayout::GetLinearVirtualAddress(KPhysicalAddress(reboot_payload_paddr)); + + /* Clear IRAM. */ + std::memset(GetVoidPointer(iram_address), 0xCC, RebootPayloadSize); + + /* Copy the payload to iram. */ + for (size_t i = 0; i < RebootPayloadSize / sizeof(u32); ++i) { + GetPointer(iram_address)[i] = GetPointer(reboot_payload)[i]; + } + + /* Reboot. */ + smc::SetConfig(smc::ConfigItem::ExosphereNeedsReboot, smc::UserRebootType_ToPayload); + } else { + /* If we don't have a payload, reboot to rcm. */ + smc::SetConfig(smc::ConfigItem::ExosphereNeedsReboot, smc::UserRebootType_ToRcm); + } + } + if (g_call_smc_on_panic) { - /* Display a panic screen via secure monitor. */ + /* If we should, instruct the secure monitor to display a panic screen. */ smc::Panic(0xF00); } - u32 dummy; - smc::init::ReadWriteRegister(std::addressof(dummy), 0x7000E400, 0x10, 0x10); - while (true) { /* ... */ } + AMS_INFINITE_LOOP(); } /* User access. */ diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.cpp new file mode 100644 index 000000000..e3f8755c9 --- /dev/null +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.cpp @@ -0,0 +1,463 @@ +/* + * 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 . + */ +#include +#include "kern_lps_driver.hpp" +#include "kern_k_sleep_manager.hpp" + +#include "kern_bpmp_api.hpp" +#include "kern_atomics_registers.hpp" +#include "kern_ictlr_registers.hpp" +#include "kern_sema_registers.hpp" + +namespace ams::kern::board::nintendo::nx::lps { + + namespace { + + constexpr inline int ChannelCount = 12; + + constexpr inline TimeSpan ChannelTimeout = TimeSpan::FromSeconds(1); + + constinit bool g_lps_init_done = false; + constinit bool g_bpmp_connected = false; + constinit bool g_bpmp_mail_initialized = false; + + constinit KSpinLock g_bpmp_mrq_lock; + + constinit KVirtualAddress g_evp_address = Null; + constinit KVirtualAddress g_flow_address = Null; + constinit KVirtualAddress g_prictlr_address = Null; + constinit KVirtualAddress g_sema_address = Null; + constinit KVirtualAddress g_atomics_address = Null; + constinit KVirtualAddress g_clkrst_address = Null; + constinit KVirtualAddress g_pmc_address = Null; + + constinit ChannelData g_channel_area[ChannelCount] = {}; + + constinit u32 g_csite_clk_source = 0; + + ALWAYS_INLINE u32 Read(KVirtualAddress address) { + return *GetPointer(address); + } + + ALWAYS_INLINE void Write(KVirtualAddress address, u32 value) { + *GetPointer(address) = value; + } + + void InitializeDeviceVirtualAddresses() { + /* Retrieve randomized mappings. */ + g_evp_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsExceptionVectors); + g_flow_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsFlowController); + g_prictlr_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsPrimaryICtlr); + g_sema_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsSemaphore); + g_atomics_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsAtomics); + g_clkrst_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsClkRst); + g_pmc_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_PowerManagementController); + } + + /* NOTE: linux "do_cc4_init" */ + void ConfigureCc3AndCc4() { + /* Configure CC4/CC3 as enabled with time threshold as 2 microseconds. */ + Write(g_flow_address + FLOW_CTLR_CC4_HVC_CONTROL, (0x2 << 3) | 0x1); + + /* Configure Retention with threshold 2 microseconds. */ + Write(g_flow_address + FLOW_CTLR_CC4_RETENTION_CONTROL, (0x2 << 3)); + + /* Configure CC3/CC3 retry threshold as 2 microseconds. */ + Write(g_flow_address + FLOW_CTLR_CC4_HVC_RETRY, (0x2 << 3)); + + /* Read the retry register to ensure writes take. */ + Read(g_flow_address + FLOW_CTLR_CC4_HVC_RETRY); + } + + constexpr bool IsValidMessageDataSize(int size) { + return 0 <= size && size < MessageDataSizeMax; + } + + /* NOTE: linux "bpmp_valid_txfer" */ + constexpr bool IsTransferValid(const void *ob, int ob_size, void *ib, int ib_size) { + return IsValidMessageDataSize(ob_size) && IsValidMessageDataSize(ib_size) && (ob_size == 0 || ob != nullptr) && (ib_size == 0 || ib != nullptr); + } + + /* NOTE: linux "bpmp_ob_channel" */ + int BpmpGetOutboundChannel() { + return GetCurrentCoreId(); + } + + /* NOTE: linux "bpmp_ch_sta" */ + u32 BpmpGetChannelState(int channel) { + cpu::DataSynchronizationBarrier(); + return Read(g_sema_address + RES_SEMA_SHRD_SMP_STA) & CH_MASK(channel); + } + + /* NOTE: linux "bpmp_master_free" */ + bool BpmpIsMasterFree(int channel) { + return BpmpGetChannelState(channel) == MA_FREE(channel); + } + + /* NOTE: linux "bpmp_master_acked" */ + bool BpmpIsMasterAcked(int channel) { + return BpmpGetChannelState(channel) == MA_ACKD(channel); + } + + /* NOTE: linux "bpmp_signal_slave" */ + void BpmpSignalSlave(int channel) { + Write(g_sema_address + RES_SEMA_SHRD_SMP_CLR, CH_MASK(channel)); + cpu::DataSynchronizationBarrier(); + } + + /* NOTE: linux "bpmp_free_master" */ + void BpmpFreeMaster(int channel) { + /* Transition state from ack'd to free. */ + Write(g_sema_address + RES_SEMA_SHRD_SMP_CLR, ((MA_ACKD(channel)) ^ (MA_FREE(channel)))); + cpu::DataSynchronizationBarrier(); + } + + /* NOTE: linux "bpmp_ring_doorbell" */ + void BpmpRingDoorbell() { + Write(g_prictlr_address + ICTLR_FIR_SET(INT_SHR_SEM_OUTBOX_IBF), FIR_BIT(INT_SHR_SEM_OUTBOX_IBF)); + cpu::DataSynchronizationBarrier(); + } + + /* NOTE: linux "bpmp_wait_master_free" */ + int BpmpWaitMasterFree(int channel) { + /* Check if the master is already freed. */ + if (BpmpIsMasterFree(channel)) { + return 0; + } + + /* Spin-poll for the master to be freed until timeout occurs. */ + const auto start_tick = KHardwareTimer::GetTick(); + const auto timeout = ams::svc::Tick(ChannelTimeout); + do { + if (BpmpIsMasterFree(channel)) { + return 0; + } + } while ((KHardwareTimer::GetTick() - start_tick) < timeout); + + /* The master didn't become free. */ + return -1; + } + + /* NOTE: linux "bpmp_wait_ack" */ + int BpmpWaitAck(int channel) { + /* Check if the master is already ACK'd. */ + if (BpmpIsMasterAcked(channel)) { + return 0; + } + + /* Spin-poll for the master to be ACK'd until timeout occurs. */ + const auto start_tick = KHardwareTimer::GetTick(); + const auto timeout = ams::svc::Tick(ChannelTimeout); + do { + if (BpmpIsMasterAcked(channel)) { + return 0; + } + } while ((KHardwareTimer::GetTick() - start_tick) < timeout); + + /* The master didn't get ACK'd. */ + return -1; + } + + /* NOTE: linux "bpmp_write_ch" */ + int BpmpWriteChannel(int channel, int mrq, int flags, const void *data, size_t data_size) { + /* Wait to be able to master the mailbox. */ + if (int res = BpmpWaitMasterFree(channel); res != 0) { + return res; + } + + /* Prepare the message. */ + MailboxData *mb = g_channel_area[channel].ob; + mb->code = mrq; + mb->flags = flags; + if (data != nullptr) { + std::memcpy(mb->data, data, data_size); + } + + /* Signal to slave that message is available. */ + BpmpSignalSlave(channel); + + return 0; + } + + /* NOTE: linux "__bpmp_read_ch" */ + int BpmpReadChannel(int channel, void *data, size_t data_size) { + /* Get the message. */ + MailboxData *mb = g_channel_area[channel].ib; + + /* Copy any return data. */ + if (data != nullptr) { + std::memcpy(data, mb->data, data_size); + } + + /* Free the channel. */ + BpmpFreeMaster(channel); + + /* Return result. */ + return mb->code; + } + + /* NOTE: linux "tegra_bpmp_send_receive_atomic" or "tegra_bpmp_send_receive". */ + int BpmpSendAndReceive(int mrq, const void *ob, int ob_size, void *ib, int ib_size) { + /* Validate that the data transfer is valid. */ + if (!IsTransferValid(ob, ob_size, ib, ib_size)) { + return -1; + } + + /* Validate that the bpmp is connected. */ + if (!g_bpmp_connected) { + return -1; + } + + /* Disable interrupts. */ + KScopedInterruptDisable di; + + /* Acquire exclusive access to send mrqs. */ + KScopedSpinLock lk(g_bpmp_mrq_lock); + + /* Send the message. */ + int channel = BpmpGetOutboundChannel(); + if (int res = BpmpWriteChannel(channel, mrq, BPMP_MSG_DO_ACK, ob, ob_size); res != 0) { + return res; + } + + /* Send "doorbell" irq to the bpmp firmware. */ + BpmpRingDoorbell(); + + /* Wait for the bpmp firmware to acknowledge our request. */ + if (int res = BpmpWaitAck(channel); res != 0) { + return res; + } + + /* Read the data the bpmp sent back. */ + return BpmpReadChannel(channel, ib, ib_size); + } + + /* NOTE: linux "tegra_bpmp_send" */ + int BpmpSend(int mrq, const void *ob, int ob_size) { + /* Validate that the data transfer is valid. */ + if (!IsTransferValid(ob, ob_size, nullptr, 0)) { + return -1; + } + + /* Validate that the bpmp is connected. */ + if (!g_bpmp_connected) { + return -1; + } + + /* Disable interrupts. */ + KScopedInterruptDisable di; + + /* Acquire exclusive access to send mrqs. */ + KScopedSpinLock lk(g_bpmp_mrq_lock); + + /* Send the message. */ + int channel = BpmpGetOutboundChannel(); + if (int res = BpmpWriteChannel(channel, mrq, 0, ob, ob_size); res != 0) { + return res; + } + + /* Send "doorbell" irq to the bpmp firmware. */ + BpmpRingDoorbell(); + + return 0; + } + + /* NOTE: modified linux "tegra_bpmp_enable_suspend" */ + int BpmpEnableSuspend(int mode, int flags) { + /* Prepare data for bpmp. */ + const s32 data[] = { mode, flags }; + + /* Send the data. */ + return BpmpSend(MRQ_ENABLE_SUSPEND, data, sizeof(data)); + } + + /* NOTE: linux "__bpmp_connect" */ + int ConnectToBpmp() { + /* Check if we've already connected. */ + if (g_bpmp_connected) { + return 0; + } + + /* Verify that the resource semaphore state is set. */ + if (Read(g_sema_address + RES_SEMA_SHRD_SMP_STA) == 0) { + return -1; + } + + /* Get the channels, which the bpmp firmware has configured in advance. */ + { + const KVirtualAddress iram_virt_addr = KMemoryLayout::GetDeviceVirtualAddress (KMemoryRegionType_LegacyLpsIram); + const KPhysicalAddress iram_phys_addr = KMemoryLayout::GetDevicePhysicalAddress(KMemoryRegionType_LegacyLpsIram); + + for (auto i = 0; i < ChannelCount; ++i) { + /* Trigger a get command for the desired channel. */ + Write(g_atomics_address + ATOMICS_AP0_TRIGGER, TRIGGER_CMD_GET | (i << 16)); + + /* Retrieve the channel phys-addr-in-iram, and convert it to a kernel address. */ + auto *ch = GetPointer(iram_virt_addr + (Read(g_atomics_address + ATOMICS_AP0_RESULT(i)) - GetInteger(iram_phys_addr))); + + /* Verify the channel isn't null. */ + /* NOTE: This is an utterly nonsense check, as this would require the bpmp firmware to specify */ + /* a phys-to-virt diff as an address. On 1.0.0, which had no ASLR, this was 0x8028C000. */ + /* However, Nintendo has the check, and we'll preserve it to be faithful. */ + if (ch == nullptr) { + return -1; + } + + /* Set the channel in the channel area. */ + g_channel_area[i].ib = ch; + g_channel_area[i].ob = ch; + } + } + + /* Mark driver as connected to bpmp. */ + g_bpmp_connected = true; + + return 0; + } + + /* NOTE: Modified linux "bpmp_mail_init" */ + int InitializeBpmpMail() { + /* Check if we've already initialized. */ + if (g_bpmp_mail_initialized) { + return 0; + } + + /* Mark function as having been called. */ + g_bpmp_mail_initialized = true; + + /* Forward declare result/reply variables. */ + int res, request = 0, reply = 0; + + /* Try to connect to the bpmp. */ + if (res = ConnectToBpmp(); res != 0) { + MESOSPHERE_LOG("bpmp: connect error returns %d\n", res); + return res; + } + + /* Ensure that we can successfully ping the bpmp. */ + request = 1; + if (res = BpmpSendAndReceive(MRQ_PING, std::addressof(request), sizeof(request), std::addressof(reply), sizeof(reply)); res != 0) { + MESOSPHERE_LOG("bpmp: MRQ_PING error returns %d with reply %d\n", res, reply); + return res; + } + + /* Configure the PMIC. */ + request = 1; + if (res = BpmpSendAndReceive(MRQ_CPU_PMIC_SELECT, std::addressof(request), sizeof(request), std::addressof(reply), sizeof(reply)); res != 0) { + MESOSPHERE_LOG("bpmp: MRQ_CPU_PMIC_SELECT for MAX77621 error returns %d with reply %d\n", res, reply); + return res; + } + + return 0; + } + + } + + void Initialize() { + if (!g_lps_init_done) { + /* Get the addresses of the devices the driver needs. */ + InitializeDeviceVirtualAddresses(); + + /* Configure CC3/CC4. */ + ConfigureCc3AndCc4(); + + /* Initialize ccplex <-> bpmp mail. */ + /* NOTE: Nintendo does not check that this call succeeds. */ + InitializeBpmpMail(); + + g_lps_init_done = true; + } + } + + Result EnableSuspend(bool enable) { + /* If we're not on core 0, there's nothing to do. */ + R_SUCCEED_IF(GetCurrentCoreId() != 0); + + /* If we're not enabling suspend, there's nothing to do. */ + R_SUCCEED_IF(!enable); + + /* Instruct BPMP to enable suspend-to-sc7. */ + R_UNLESS(BpmpEnableSuspend(TEGRA_BPMP_PM_SC7, 0) == 0, svc::ResultInvalidState()); + + return ResultSuccess(); + } + + void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry) { + /* Verify that we're allowed to perform suspension. */ + MESOSPHERE_ABORT_UNLESS(g_lps_init_done); + MESOSPHERE_ABORT_UNLESS(GetCurrentCoreId() == 0); + + /* Save the CSITE clock source. */ + g_csite_clk_source = Read(g_clkrst_address + CLK_RST_CONTROLLER_CLK_SOURCE_CSITE); + + /* Configure CSITE clock source as CLK_M. */ + Write(g_clkrst_address + CLK_RST_CONTROLLER_CLK_SOURCE_CSITE, (0x6 << 29)); + + /* Clear the top bit of PMC_SCRATCH4. */ + Write(g_pmc_address + APBDEV_PMC_SCRATCH4, Read(g_pmc_address + APBDEV_PMC_SCRATCH4) & 0x7FFFFFFF); + + /* Write 1 to PMC_SCRATCH0. This will cause the bootrom to use the warmboot code-path. */ + Write(g_pmc_address + APBDEV_PMC_SCRATCH0, 1); + + /* Read PMC_SCRATCH0 to be sure our write takes. */ + Read(g_pmc_address + APBDEV_PMC_SCRATCH0); + + /* Invoke the sleep hander. */ + KSleepManager::CpuSleepHandler(arg, entry); + + /* Disable deep power down. */ + Write(g_pmc_address + APBDEV_PMC_DPD_ENABLE, 0); + + /* Restore the saved CSITE clock source. */ + Write(g_clkrst_address + CLK_RST_CONTROLLER_CLK_SOURCE_CSITE, g_csite_clk_source); + + /* Read the CSITE clock source to ensure our configuration takes. */ + Read(g_clkrst_address + CLK_RST_CONTROLLER_CLK_SOURCE_CSITE); + + /* Configure CC3/CC4. */ + ConfigureCc3AndCc4(); + } + + void ResumeBpmpFirmware() { + /* Halt the bpmp. */ + Write(g_flow_address + FLOW_CTLR_HALT_COP_EVENTS, (0x2 << 29)); + + /* Hold the bpmp in reset. */ + Write(g_clkrst_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, 0x2); + + /* Read the saved bpmp entrypoint, and write it to the relevant exception vector. */ + const u32 bpmp_entry = Read(g_pmc_address + APBDEV_PMC_SCRATCH39); + Write(g_evp_address + EVP_COP_RESET_VECTOR, bpmp_entry); + + /* Verify that we can read back the address we wrote. */ + while (Read(g_evp_address + EVP_COP_RESET_VECTOR) != bpmp_entry) { + /* ... */ + } + + /* Spin for 40 ticks, to give enough time for the bpmp to be reset. */ + const auto start_tick = KHardwareTimer::GetTick(); + do { + __asm__ __volatile__("" ::: "memory"); + } while ((KHardwareTimer::GetTick() - start_tick) < 40); + + /* Take the bpmp out of reset. */ + Write(g_clkrst_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, 0x2); + + /* Resume the bpmp. */ + Write(g_flow_address + FLOW_CTLR_HALT_COP_EVENTS, (0x0 << 29)); + } + +} \ No newline at end of file diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.hpp new file mode 100644 index 000000000..45139f7b2 --- /dev/null +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_lps_driver.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 . + */ +#pragma once +#include + +namespace ams::kern::board::nintendo::nx { + + namespace lps { + + void Initialize(); + Result EnableSuspend(bool enable); + void InvokeCpuSleepHandler(uintptr_t arg, uintptr_t entry); + void ResumeBpmpFirmware(); + + } + +} \ No newline at end of file diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_mc_registers.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_mc_registers.hpp deleted file mode 100644 index edd925ee2..000000000 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_mc_registers.hpp +++ /dev/null @@ -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 . - */ -#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 diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp index 9405d8a67..76af4e484 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp @@ -24,6 +24,10 @@ namespace ams::kern::board::nintendo::nx::smc { u64 x[8]; }; + enum UserFunctionId : u32 { + UserFunctionId_SetConfig = 0xC3000401, + }; + enum FunctionId : u32 { FunctionId_CpuSuspend = 0xC4000001, FunctionId_CpuOff = 0x84000002, @@ -33,6 +37,9 @@ namespace ams::kern::board::nintendo::nx::smc { FunctionId_Panic = 0xC3000006, FunctionId_ConfigureCarveout = 0xC3000007, FunctionId_ReadWriteRegister = 0xC3000008, + + /* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */ + FunctionId_SetConfig = 0xC3000409, }; void CallPrivilegedSecureMonitorFunction(SecureMonitorArguments &args) { @@ -179,13 +186,28 @@ namespace ams::kern::board::nintendo::nx::smc { } - void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { + bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { SecureMonitorArguments args = { FunctionId_GetConfig, static_cast(config_item) }; CallPrivilegedSecureMonitorFunction(args); - MESOSPHERE_ABORT_UNLESS((static_cast(args.x[0]) == SmcResult::Success)); + if (static_cast(args.x[0]) != SmcResult::Success) { + return false; + } + for (size_t i = 0; i < num_qwords && i < 7; i++) { out[i] = args.x[1 + i]; } + + return true; + } + + void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { + MESOSPHERE_ABORT_UNLESS(TryGetConfig(out, num_qwords, config_item)); + } + + bool SetConfig(ConfigItem config_item, u64 value) { + SecureMonitorArguments args = { FunctionId_SetConfig, static_cast(config_item), 0, value }; + CallPrivilegedSecureMonitorFunction(args); + return static_cast(args.x[0]) == SmcResult::Success; } bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value) { @@ -227,7 +249,7 @@ namespace ams::kern::board::nintendo::nx::smc { void NORETURN Panic(u32 color) { SecureMonitorArguments args = { FunctionId_Panic, color }; CallPrivilegedSecureMonitorFunction(args); - while (true) { /* ... */ } + AMS_INFINITE_LOOP(); } void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args) { diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp index fe3e7634f..b7d17072e 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp @@ -60,6 +60,10 @@ namespace ams::kern::board::nintendo::nx::smc { ExosphereNeedsShutdown = 65002, ExosphereGitCommitHash = 65003, ExosphereHasRcmBugPatch = 65004, + ExosphereBlankProdInfo = 65005, + ExosphereAllowCalWrites = 65006, + ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, }; enum class SmcResult { @@ -83,12 +87,20 @@ namespace ams::kern::board::nintendo::nx::smc { using MemorySize = util::BitPack32::Field; }; - /* TODO: Rest of Secure Monitor API. */ + enum UserRebootType { + UserRebootType_None = 0, + UserRebootType_ToRcm = 1, + UserRebootType_ToPayload = 2, + }; + void GenerateRandomBytes(void *dst, size_t size); + bool TryGetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); bool ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); void ConfigureCarveout(size_t which, uintptr_t address, size_t size); + bool SetConfig(ConfigItem config_item, u64 value); + void CpuOn(u64 core_id, uintptr_t entrypoint, uintptr_t arg); void NORETURN Panic(u32 color); diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_sema_registers.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_sema_registers.hpp new file mode 100644 index 000000000..b623df176 --- /dev/null +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_sema_registers.hpp @@ -0,0 +1,20 @@ +/* + * 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 . + */ +#pragma once + +#define RES_SEMA_SHRD_SMP_STA 0x000 +#define RES_SEMA_SHRD_SMP_SET 0x004 +#define RES_SEMA_SHRD_SMP_CLR 0x008 diff --git a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp index 5737afea7..87199c431 100644 --- a/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp +++ b/libraries/libmesosphere/source/init/kern_init_slab_setup.cpp @@ -71,8 +71,12 @@ namespace ams::kern::init { constexpr size_t SlabCountExtraKThread = 160; - /* This is used for gaps between the slab allocators. */ - constexpr size_t SlabRegionReservedSize = 2_MB; + namespace test { + + constexpr size_t RequiredSizeForExtraThreadCount = SlabCountExtraKThread * (sizeof(KThread) + (sizeof(KLinkedListNode) * 17) + (sizeof(KThreadLocalPage) / 8) + sizeof(KEventInfo)); + static_assert(RequiredSizeForExtraThreadCount <= KernelSlabHeapAdditionalSize); + + } /* Global to hold our resource counts. */ KSlabResourceCounts g_slab_resource_counts = { @@ -98,7 +102,9 @@ namespace ams::kern::init { KVirtualAddress start = util::AlignUp(GetInteger(address), alignof(T)); if (size > 0) { - MESOSPHERE_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().FindContainingRegion(GetInteger(start) + size - 1)->IsDerivedFrom(KMemoryRegionType_KernelSlab)); + const KMemoryRegion *region = KMemoryLayout::Find(start + size - 1); + MESOSPHERE_ABORT_UNLESS(region != nullptr); + MESOSPHERE_ABORT_UNLESS(region->IsDerivedFrom(KMemoryRegionType_KernelSlab)); T::InitializeSlabHeap(GetVoidPointer(start), size); } @@ -119,6 +125,10 @@ namespace ams::kern::init { } } + size_t CalculateSlabHeapGapSize() { + return (kern::GetTargetFirmware() >= TargetFirmware_10_0_0) ? KernelSlabHeapGapsSize : KernelSlabHeapGapsSizeDeprecated; + } + size_t CalculateTotalSlabHeapSize() { size_t size = 0; @@ -133,7 +143,7 @@ namespace ams::kern::init { #undef ADD_SLAB_SIZE /* Add the reserved size. */ - size += SlabRegionReservedSize; + size += CalculateSlabHeapGapSize(); return size; } @@ -173,11 +183,12 @@ namespace ams::kern::init { } /* Create an array to represent the gaps between the slabs. */ + const size_t total_gap_size = CalculateSlabHeapGapSize(); size_t slab_gaps[util::size(slab_types)]; for (size_t i = 0; i < util::size(slab_gaps); i++) { /* Note: This is an off-by-one error from Nintendo's intention, because GenerateRandomRange is inclusive. */ /* However, Nintendo also has the off-by-one error, and it's "harmless", so we will include it ourselves. */ - slab_gaps[i] = KSystemControl::GenerateRandomRange(0, SlabRegionReservedSize); + slab_gaps[i] = KSystemControl::GenerateRandomRange(0, total_gap_size); } /* Sort the array, so that we can treat differences between values as offsets to the starts of slabs. */ diff --git a/libraries/libmesosphere/source/kern_debug_log.cpp b/libraries/libmesosphere/source/kern_debug_log.cpp index 6288f1df2..9a14405bd 100644 --- a/libraries/libmesosphere/source/kern_debug_log.cpp +++ b/libraries/libmesosphere/source/kern_debug_log.cpp @@ -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::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(*(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(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(_width); - } else { - SetFlag(FormatSpecifierFlag_LeftJustify); - width = static_cast(-_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(_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(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(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(width); i++) { - WriteCharacter(' '); - } - } - }; - - /* Output the integer. */ - if (is_unsigned) { - uintmax_t n = 0; - if (HasFlag(FormatSpecifierFlag_LongLong)) { - n = static_cast(va_arg(vl, unsigned long long)); - } else if (HasFlag(FormatSpecifierFlag_Long)) { - n = static_cast(va_arg(vl, unsigned long)); - } else if (HasFlag(FormatSpecifierFlag_Char)) { - n = static_cast(va_arg(vl, unsigned int)); - } else if (HasFlag(FormatSpecifierFlag_Short)) { - n = static_cast(va_arg(vl, unsigned int)); - } else { - n = static_cast(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(va_arg(vl, signed long long)); - } else if (HasFlag(FormatSpecifierFlag_Long)) { - n = static_cast(va_arg(vl, signed long)); - } else if (HasFlag(FormatSpecifierFlag_Char)) { - n = static_cast(va_arg(vl, signed int)); - } else if (HasFlag(FormatSpecifierFlag_Short)) { - n = static_cast(va_arg(vl, signed int)); - } else { - n = static_cast(va_arg(vl, signed int)); - } - const bool negative = n < 0; - const uintmax_t u = (negative) ? static_cast(-n) : static_cast(n); - PrintInteger(negative, u); - } - } - break; - case 'c': - { - size_t len = 1; - if (!HasFlag(FormatSpecifierFlag_LeftJustify)) { - while (len++ < width) { - WriteCharacter(' '); - } - } - WriteCharacter(static_cast(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::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 user_str, size_t len) { @@ -498,6 +118,8 @@ namespace ams::kern { R_TRY(PutUserString(user_str, len)); } + #else + MESOSPHERE_UNUSED(user_str, len); #endif return ResultSuccess(); diff --git a/libraries/libmesosphere/source/kern_debug_log_impl.board.nintendo_nx.cpp b/libraries/libmesosphere/source/kern_debug_log_impl.board.nintendo_nx.cpp index dc90e1c9e..0f54d3361 100644 --- a/libraries/libmesosphere/source/kern_debug_log_impl.board.nintendo_nx.cpp +++ b/libraries/libmesosphere/source/kern_debug_log_impl.board.nintendo_nx.cpp @@ -18,6 +18,8 @@ namespace ams::kern { +#if defined(MESOSPHERE_DEBUG_LOG_USE_UART_A) || defined(MESOSPHERE_DEBUG_LOG_USE_UART_B) || defined(MESOSPHERE_DEBUG_LOG_USE_UART_C) || defined(MESOSPHERE_DEBUG_LOG_USE_UART_D) + namespace { enum UartRegister { @@ -50,7 +52,7 @@ namespace ams::kern { bool KDebugLogImpl::Initialize() { /* Set the uart register base address. */ - g_uart_address = KMemoryLayout::GetUartAddress(); + g_uart_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_Uart); /* Parameters for uart. */ constexpr u32 BaudRate = 115200; @@ -138,4 +140,52 @@ namespace ams::kern { ReadUartRegister(UartRegister_FCR); } +#elif defined(MESOSPHERE_DEBUG_LOG_USE_IRAM_RINGBUFFER) + + namespace { + + constinit KVirtualAddress g_debug_iram_address = 0; + + constexpr size_t RingBufferSize = 0x5000; + constinit uintptr_t g_offset = 0; + + constinit u8 g_saved_buffer[RingBufferSize]; + + } + + bool KDebugLogImpl::Initialize() { + /* Set the base address. */ + g_debug_iram_address = KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsIram) + 0x38000; + + std::memset(GetVoidPointer(g_debug_iram_address), 0xFF, RingBufferSize); + + return true; + } + + void KDebugLogImpl::PutChar(char c) { + GetPointer(g_debug_iram_address)[g_offset++] = c; + + if (g_offset == RingBufferSize) { + g_offset = 0; + } + } + + void KDebugLogImpl::Flush() { + /* ... */ + } + + void KDebugLogImpl::Save() { + std::memcpy(g_saved_buffer, GetVoidPointer(g_debug_iram_address), RingBufferSize); + } + + void KDebugLogImpl::Restore() { + std::memcpy(GetVoidPointer(g_debug_iram_address), g_saved_buffer, RingBufferSize); + } + +#else + + #error "Unknown Debug UART device!" + +#endif + } diff --git a/libraries/libmesosphere/source/kern_initial_process.cpp b/libraries/libmesosphere/source/kern_initial_process.cpp index 598863894..f4c387cad 100644 --- a/libraries/libmesosphere/source/kern_initial_process.cpp +++ b/libraries/libmesosphere/source/kern_initial_process.cpp @@ -38,10 +38,38 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(header->num_processes <= init::GetSlabResourceCounts().num_KProcess); } + size_t GetProcessesSecureMemorySize(KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) { + u8 *current = GetPointer(binary_address + sizeof(InitialProcessBinaryHeader)); + const u8 * const end = GetPointer(binary_address + header.size - sizeof(KInitialProcessHeader)); + + size_t size = 0; + const size_t num_processes = header.num_processes; + for (size_t i = 0; i < num_processes; i++) { + /* Validate that we can read the current KIP. */ + MESOSPHERE_ABORT_UNLESS(current <= end); + KInitialProcessReader reader; + MESOSPHERE_ABORT_UNLESS(reader.Attach(current)); + + /* If the process uses secure memory, account for that. */ + if (reader.UsesSecureMemory()) { + size += util::AlignUp(reader.GetSize(), PageSize); + } + + /* Advance the reader. */ + current += reader.GetBinarySize(); + } + + return size; + } + void CreateProcesses(InitialProcessInfo *infos, KVirtualAddress binary_address, const InitialProcessBinaryHeader &header) { u8 *current = GetPointer(binary_address + sizeof(InitialProcessBinaryHeader)); const u8 * const end = GetPointer(binary_address + header.size - sizeof(KInitialProcessHeader)); + /* Decide on pools to use. */ + const auto unsafe_pool = static_cast(KSystemControl::GetCreateProcessMemoryPool()); + const auto secure_pool = (GetTargetFirmware() >= TargetFirmware_2_0_0) ? KMemoryManager::Pool_Secure : unsafe_pool; + const size_t num_processes = header.num_processes; for (size_t i = 0; i < num_processes; i++) { /* Validate that we can read the current KIP. */ @@ -62,7 +90,7 @@ namespace ams::kern { /* Allocate memory for the process. */ auto &mm = Kernel::GetMemoryManager(); - const auto pool = static_cast(reader.UsesSecureMemory() ? KMemoryManager::Pool_System : KSystemControl::GetInitialProcessBinaryPool()); + const auto pool = reader.UsesSecureMemory() ? secure_pool : unsafe_pool; MESOSPHERE_R_ABORT_UNLESS(mm.Allocate(std::addressof(pg), params.code_num_pages, KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront))); { @@ -108,10 +136,10 @@ namespace ams::kern { } } - KVirtualAddress g_initial_process_binary_address; - InitialProcessBinaryHeader g_initial_process_binary_header; - u64 g_initial_process_id_min = std::numeric_limits::max(); - u64 g_initial_process_id_max = std::numeric_limits::min(); + constinit KVirtualAddress g_initial_process_binary_address = Null; + constinit InitialProcessBinaryHeader g_initial_process_binary_header = {}; + constinit u64 g_initial_process_id_min = std::numeric_limits::max(); + constinit u64 g_initial_process_id_max = std::numeric_limits::min(); } @@ -123,6 +151,12 @@ namespace ams::kern { return g_initial_process_id_max; } + size_t GetInitialProcessesSecureMemorySize() { + LoadInitialProcessBinaryHeader(&g_initial_process_binary_header); + + return GetProcessesSecureMemorySize(g_initial_process_binary_address != Null ? g_initial_process_binary_address : GetInitialProcessBinaryAddress(), g_initial_process_binary_header); + } + void CopyInitialProcessBinaryToKernelMemory() { LoadInitialProcessBinaryHeader(&g_initial_process_binary_header); @@ -134,7 +168,7 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_PhysicalMemoryMax, total_size)); /* Allocate memory for the image. */ - const KMemoryManager::Pool pool = static_cast(KSystemControl::GetInitialProcessBinaryPool()); + const KMemoryManager::Pool pool = static_cast(KSystemControl::GetCreateProcessMemoryPool()); const auto allocate_option = KMemoryManager::EncodeOption(pool, KMemoryManager::Direction_FromFront); KVirtualAddress allocated_memory = mm.AllocateContinuous(num_pages, 1, allocate_option); MESOSPHERE_ABORT_UNLESS(allocated_memory != Null); diff --git a/libraries/libmesosphere/source/kern_k_address_arbiter.cpp b/libraries/libmesosphere/source/kern_k_address_arbiter.cpp index dfcd40883..171fd6d33 100644 --- a/libraries/libmesosphere/source/kern_k_address_arbiter.cpp +++ b/libraries/libmesosphere/source/kern_k_address_arbiter.cpp @@ -73,15 +73,16 @@ namespace ams::kern { s32 num_waiters = 0; { KScopedSchedulerLock sl; - g_cv_arbiter_compare_thread.SetupForAddressArbiterCompare(addr, -1); - - auto it = this->tree.nfind(g_cv_arbiter_compare_thread); /* Check the userspace value. */ s32 user_value; R_UNLESS(UpdateIfEqual(std::addressof(user_value), addr, value, value + 1), svc::ResultInvalidCurrentMemory()); R_UNLESS(user_value == value, svc::ResultInvalidState()); + g_cv_arbiter_compare_thread.SetupForAddressArbiterCompare(addr, -1); + + auto it = this->tree.nfind(g_cv_arbiter_compare_thread); + while ((it != this->tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { KThread *target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, ResultSuccess()); @@ -108,26 +109,54 @@ namespace ams::kern { /* Determine the updated value. */ s32 new_value; - if (count <= 0) { - if ((it != this->tree.end()) && (it->GetAddressArbiterKey() == addr)) { - new_value = value - 1; + if (GetTargetFirmware() >= TargetFirmware_7_0_0) { + if (count <= 0) { + if ((it != this->tree.end()) && (it->GetAddressArbiterKey() == addr)) { + new_value = value - 2; + } else { + new_value = value + 1; + } } else { - new_value = value + 1; + if ((it != this->tree.end()) && (it->GetAddressArbiterKey() == addr)) { + auto tmp_it = it; + s32 tmp_num_waiters = 0; + while ((++tmp_it != this->tree.end()) && (tmp_it->GetAddressArbiterKey() == addr)) { + if ((tmp_num_waiters++) >= count) { + break; + } + } + + if (tmp_num_waiters < count) { + new_value = value - 1; + } else { + new_value = value; + } + } else { + new_value = value + 1; + } } } else { - auto tmp_it = it; - int tmp_num_waiters = 0; - while ((tmp_it != this->tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) && (tmp_num_waiters < count + 1)) { - ++tmp_num_waiters; - ++tmp_it; - } - - if (tmp_num_waiters == 0) { - new_value = value + 1; - } else if (tmp_num_waiters <= count) { - new_value = value - 1; + if (count <= 0) { + if ((it != this->tree.end()) && (it->GetAddressArbiterKey() == addr)) { + new_value = value - 1; + } else { + new_value = value + 1; + } } else { - new_value = value; + auto tmp_it = it; + s32 tmp_num_waiters = 0; + while ((tmp_it != this->tree.end()) && (tmp_it->GetAddressArbiterKey() == addr) && (tmp_num_waiters < count + 1)) { + ++tmp_num_waiters; + ++tmp_it; + } + + if (tmp_num_waiters == 0) { + new_value = value + 1; + } else if (tmp_num_waiters <= count) { + new_value = value - 1; + } else { + new_value = value; + } } } diff --git a/libraries/libmesosphere/source/kern_k_capabilities.cpp b/libraries/libmesosphere/source/kern_k_capabilities.cpp index e8e5d2a86..c7abfc4b7 100644 --- a/libraries/libmesosphere/source/kern_k_capabilities.cpp +++ b/libraries/libmesosphere/source/kern_k_capabilities.cpp @@ -27,8 +27,10 @@ namespace ams::kern { /* Initial processes may use any user priority they like. */ this->priority_mask = ~0xFul; - /* TODO: Here, Nintendo sets the kernel version to (current kernel version). */ - /* How should we handle this? Not a MESOSPHERE_TODO because it's not critical. */ + /* Here, Nintendo sets the kernel version to the current kernel version. */ + /* We will follow suit and set the version to the highest supported kernel version. */ + this->intended_kernel_version.Set(ams::svc::SupportedKernelMajorVersion); + this->intended_kernel_version.Set(ams::svc::SupportedKernelMinorVersion); /* Parse the capabilities array. */ return this->SetCapabilities(caps, num_caps, page_table); @@ -157,6 +159,7 @@ namespace ams::kern { case RegionType::OnMemoryBootImage: case RegionType::DTB: R_TRY(page_table->MapRegion(MemoryRegions[static_cast(type)], perm)); + break; default: return svc::ResultNotFound(); } diff --git a/libraries/libmesosphere/source/kern_k_code_memory.cpp b/libraries/libmesosphere/source/kern_k_code_memory.cpp index 57931326d..3dcd101b4 100644 --- a/libraries/libmesosphere/source/kern_k_code_memory.cpp +++ b/libraries/libmesosphere/source/kern_k_code_memory.cpp @@ -133,7 +133,7 @@ namespace ams::kern { } /* Map the memory. */ - R_TRY(GetCurrentProcess().GetPageTable().MapPageGroup(address, GetReference(this->page_group), KMemoryState_GeneratedCode, k_perm)); + R_TRY(this->owner->GetPageTable().MapPageGroup(address, GetReference(this->page_group), KMemoryState_GeneratedCode, k_perm)); /* Mark ourselves as mapped. */ this->is_owner_mapped = true; @@ -151,7 +151,7 @@ namespace ams::kern { KScopedLightLock lk(this->lock); /* Unmap the memory. */ - R_TRY(GetCurrentProcess().GetPageTable().UnmapPageGroup(address, GetReference(this->page_group), KMemoryState_GeneratedCode)); + R_TRY(this->owner->GetPageTable().UnmapPageGroup(address, GetReference(this->page_group), KMemoryState_GeneratedCode)); /* Mark ourselves as unmapped. */ MESOSPHERE_ASSERT(this->is_owner_mapped); diff --git a/libraries/libmesosphere/source/kern_k_condition_variable.cpp b/libraries/libmesosphere/source/kern_k_condition_variable.cpp index 270c3c8ac..dc768e43c 100644 --- a/libraries/libmesosphere/source/kern_k_condition_variable.cpp +++ b/libraries/libmesosphere/source/kern_k_condition_variable.cpp @@ -205,13 +205,8 @@ namespace ams::kern { } /* Close threads in the list. */ - if (num_waiters > MaxThreads) { - auto it = thread_list.begin(); - while (it != thread_list.end()) { - KThread *thread = std::addressof(*it); - thread->Close(); - it = thread_list.erase(it); - } + for (auto it = thread_list.begin(); it != thread_list.end(); it = thread_list.erase(it)) { + (*it).Close(); } } diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index 93c3d044c..f7ab4328c 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -471,8 +471,9 @@ namespace ams::kern { KScopedLightLock lk(this->lock); /* Get the thread from its id. */ - KScopedAutoObject thread = KThread::GetThreadFromId(thread_id); - R_UNLESS(thread.IsNotNull(), svc::ResultInvalidId()); + KThread *thread = KThread::GetThreadFromId(thread_id); + R_UNLESS(thread != nullptr, svc::ResultInvalidId()); + ON_SCOPE_EXIT { thread->Close(); }; /* Verify that the thread is owned by our process. */ R_UNLESS(this->process == thread->GetOwnerProcess(), svc::ResultInvalidId()); @@ -482,7 +483,7 @@ namespace ams::kern { /* Check that the thread is not the current one. */ /* NOTE: Nintendo does not check this, and thus the following loop will deadlock. */ - R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(), svc::ResultInvalidId()); + R_UNLESS(thread != GetCurrentThreadPointer(), svc::ResultInvalidId()); /* Try to get the thread context until the thread isn't current on any core. */ while (true) { @@ -495,7 +496,7 @@ namespace ams::kern { if (thread->GetRawState() != KThread::ThreadState_Runnable) { bool current = false; for (auto i = 0; i < static_cast(cpu::NumCores); ++i) { - if (thread.GetPointerUnsafe() == Kernel::GetCurrentContext(i).current_thread) { + if (thread == Kernel::GetCurrentContext(i).current_thread) { current = true; } break; @@ -508,7 +509,7 @@ namespace ams::kern { } /* Get the thread context. */ - return this->GetThreadContextImpl(out, thread.GetPointerUnsafe(), context_flags); + return this->GetThreadContextImpl(out, thread, context_flags); } } @@ -517,8 +518,9 @@ namespace ams::kern { KScopedLightLock lk(this->lock); /* Get the thread from its id. */ - KScopedAutoObject thread = KThread::GetThreadFromId(thread_id); - R_UNLESS(thread.IsNotNull(), svc::ResultInvalidId()); + KThread *thread = KThread::GetThreadFromId(thread_id); + R_UNLESS(thread != nullptr, svc::ResultInvalidId()); + ON_SCOPE_EXIT { thread->Close(); }; /* Verify that the thread is owned by our process. */ R_UNLESS(this->process == thread->GetOwnerProcess(), svc::ResultInvalidId()); @@ -528,7 +530,7 @@ namespace ams::kern { /* Check that the thread is not the current one. */ /* NOTE: Nintendo does not check this, and thus the following loop will deadlock. */ - R_UNLESS(thread.GetPointerUnsafe() != GetCurrentThreadPointer(), svc::ResultInvalidId()); + R_UNLESS(thread != GetCurrentThreadPointer(), svc::ResultInvalidId()); /* Try to get the thread context until the thread isn't current on any core. */ while (true) { @@ -541,7 +543,7 @@ namespace ams::kern { if (thread->GetRawState() != KThread::ThreadState_Runnable) { bool current = false; for (auto i = 0; i < static_cast(cpu::NumCores); ++i) { - if (thread.GetPointerUnsafe() == Kernel::GetCurrentContext(i).current_thread) { + if (thread == Kernel::GetCurrentContext(i).current_thread) { current = true; } break; @@ -560,7 +562,7 @@ namespace ams::kern { } /* Set the thread context. */ - return this->SetThreadContextImpl(ctx, thread.GetPointerUnsafe(), context_flags); + return this->SetThreadContextImpl(ctx, thread, context_flags); } } diff --git a/libraries/libmesosphere/source/kern_k_dpc_manager.cpp b/libraries/libmesosphere/source/kern_k_dpc_manager.cpp index 1113d5590..9fbcaf0bf 100644 --- a/libraries/libmesosphere/source/kern_k_dpc_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_dpc_manager.cpp @@ -86,6 +86,10 @@ namespace ams::kern { /* Manager thread functions. */ void DpcManagerNormalThreadFunction(uintptr_t arg) { + /* Input argument goes unused. */ + MESOSPHERE_UNUSED(arg); + + /* Forever wait and service requests. */ while (true) { KDpcTask::WaitForRequest(); KDpcTask::HandleRequest(); @@ -93,6 +97,10 @@ namespace ams::kern { } void DpcManagerPreemptionThreadFunction(uintptr_t arg) { + /* Input argument goes unused. */ + MESOSPHERE_UNUSED(arg); + + /* Forever wait and service requests, rotating the scheduled queue every 10 ms. */ s64 timeout = KHardwareTimer::GetTick() + DpcManagerTimeout; while (true) { if (KDpcTask::TimedWaitForRequest(timeout)) { diff --git a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp index 09a65b769..8e2196a3c 100644 --- a/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp +++ b/libraries/libmesosphere/source/kern_k_initial_process_reader.cpp @@ -105,8 +105,8 @@ namespace ams::kern { const uintptr_t start_address = rx_address; const uintptr_t end_address = bss_size > 0 ? bss_address + bss_size : rw_address + rw_size; - const size_t as_width = this->Is64BitAddressSpace() ? 39 : 32; - const ASType as_type = this->Is64BitAddressSpace() ? KAddressSpaceInfo::Type_Map39Bit : KAddressSpaceInfo::Type_MapSmall; + const size_t as_width = this->Is64BitAddressSpace() ? ((GetTargetFirmware() >= TargetFirmware_2_0_0) ? 39 : 36) : 32; + const ASType as_type = this->Is64BitAddressSpace() ? ((GetTargetFirmware() >= TargetFirmware_2_0_0) ? KAddressSpaceInfo::Type_Map39Bit : KAddressSpaceInfo::Type_MapSmall) : KAddressSpaceInfo::Type_MapSmall; const uintptr_t map_start = KAddressSpaceInfo::GetAddressSpaceStart(as_width, as_type); const size_t map_size = KAddressSpaceInfo::GetAddressSpaceSize(as_width, as_type); const uintptr_t map_end = map_start + map_size; @@ -135,7 +135,7 @@ namespace ams::kern { out->flags |= ams::svc::CreateProcessFlag_Is64Bit; } if (this->Is64BitAddressSpace()) { - out->flags |= ams::svc::CreateProcessFlag_AddressSpace64Bit; + out->flags |= (GetTargetFirmware() >= TargetFirmware_2_0_0) ? ams::svc::CreateProcessFlag_AddressSpace64Bit : ams::svc::CreateProcessFlag_AddressSpace64BitDeprecated; } else { out->flags |= ams::svc::CreateProcessFlag_AddressSpace32Bit; } diff --git a/libraries/libmesosphere/source/kern_k_interrupt_event.cpp b/libraries/libmesosphere/source/kern_k_interrupt_event.cpp index b8e24f699..af0b4b21b 100644 --- a/libraries/libmesosphere/source/kern_k_interrupt_event.cpp +++ b/libraries/libmesosphere/source/kern_k_interrupt_event.cpp @@ -128,7 +128,7 @@ namespace ams::kern { KInterruptTask *KInterruptEventTask::OnInterrupt(s32 interrupt_id) { MESOSPHERE_ASSERT_THIS(); - + MESOSPHERE_UNUSED(interrupt_id); return this; } diff --git a/libraries/libmesosphere/source/kern_k_light_lock.cpp b/libraries/libmesosphere/source/kern_k_light_lock.cpp index e0a317539..a085e1ce7 100644 --- a/libraries/libmesosphere/source/kern_k_light_lock.cpp +++ b/libraries/libmesosphere/source/kern_k_light_lock.cpp @@ -37,10 +37,13 @@ namespace ams::kern { /* Set thread states. */ if (AMS_LIKELY(cur_thread->GetState() == KThread::ThreadState_Runnable)) { cur_thread->SetState(KThread::ThreadState_Waiting); + } else { + KScheduler::SetSchedulerUpdateNeeded(); } if (owner_thread->IsSuspended()) { owner_thread->ContinueIfHasKernelWaiters(); + KScheduler::SetSchedulerUpdateNeeded(); } } @@ -75,6 +78,8 @@ namespace ams::kern { if (AMS_LIKELY(next_owner->GetState() == KThread::ThreadState_Waiting)) { next_owner->SetState(KThread::ThreadState_Runnable); + } else { + KScheduler::SetSchedulerUpdateNeeded(); } if (next_owner->IsSuspended()) { diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp index d36c45cbc..d3a98cc62 100644 --- a/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_layout.board.nintendo_nx.cpp @@ -22,20 +22,42 @@ namespace ams::kern { constexpr uintptr_t DramPhysicalAddress = 0x80000000; constexpr size_t ReservedEarlyDramSize = 0x60000; + constexpr size_t CarveoutAlignment = 0x20000; + constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment; + ALWAYS_INLINE bool SetupUartPhysicalMemoryRegion() { #if defined(MESOSPHERE_DEBUG_LOG_USE_UART_A) - return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); + return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006000, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); #elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_B) - return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); + return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006040, 0x40, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); #elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_C) return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006200, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); #elif defined(MESOSPHERE_DEBUG_LOG_USE_UART_D) return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70006300, 0x100, KMemoryRegionType_Uart | KMemoryRegionAttr_ShouldKernelMap); + #elif defined(MESOSPHERE_DEBUG_LOG_USE_IRAM_RINGBUFFER) + return true; #else #error "Unknown Debug UART device!" #endif } + ALWAYS_INLINE bool SetupPowerManagementControllerMemoryRegion() { + /* For backwards compatibility, the PMC must remain mappable on < 2.0.0. */ + const auto rtc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : static_cast(0); + const auto pmc_restrict_attr = GetTargetFirmware() >= TargetFirmware_2_0_0 ? KMemoryRegionAttr_NoUserMap : KMemoryRegionAttr_ShouldKernelMap; + + return KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E000, 0x400, KMemoryRegionType_None | rtc_restrict_attr) && + KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E400, 0xC00, KMemoryRegionType_PowerManagementController | pmc_restrict_attr); + } + + void InsertPoolPartitionRegionIntoBothTrees(size_t start, size_t size, KMemoryRegionType phys_type, KMemoryRegionType virt_type, u32 &cur_attr) { + const u32 attr = cur_attr++; + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(start, size, phys_type, attr)); + const KMemoryRegion *phys = KMemoryLayout::GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(phys_type, attr); + MESOSPHERE_INIT_ABORT_UNLESS(phys != nullptr); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(phys->GetPairAddress(), size, virt_type, attr)); + } + } namespace init { @@ -43,17 +65,31 @@ namespace ams::kern { void SetupDevicePhysicalMemoryRegions() { /* TODO: Give these constexpr defines somewhere? */ MESOSPHERE_INIT_ABORT_UNLESS(SetupUartPhysicalMemoryRegion()); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E000, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7000E400, 0xC00, KMemoryRegionType_PowerManagementController | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50041000, 0x1000, KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50042000, 0x1000, KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + MESOSPHERE_INIT_ABORT_UNLESS(SetupPowerManagementControllerMemoryRegion()); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70019000, 0x1000, KMemoryRegionType_MemoryController | KMemoryRegionAttr_NoUserMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001C000, 0x1000, KMemoryRegionType_MemoryController0 | KMemoryRegionAttr_NoUserMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x7001D000, 0x1000, KMemoryRegionType_MemoryController1 | KMemoryRegionAttr_NoUserMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50040000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50041000, 0x1000, KMemoryRegionType_InterruptDistributor | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50042000, 0x1000, KMemoryRegionType_InterruptCpuInterface | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x50043000, 0x1D000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + + /* Map IRAM unconditionally, to support debug-logging-to-iram build config. */ + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x40000000, 0x40000, KMemoryRegionType_LegacyLpsIram | KMemoryRegionAttr_ShouldKernelMap)); + + if (GetTargetFirmware() >= TargetFirmware_2_0_0) { + /* Prevent mapping the bpmp exception vectors or the ipatch region. */ + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6001DC00, 0x400, KMemoryRegionType_None | KMemoryRegionAttr_NoUserMap)); + } else { + /* Map devices required for legacy lps driver. */ + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x6000F000, 0x1000, KMemoryRegionType_LegacyLpsExceptionVectors | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60007000, 0x1000, KMemoryRegionType_LegacyLpsFlowController | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60004000, 0x1000, KMemoryRegionType_LegacyLpsPrimaryICtlr | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60001000, 0x1000, KMemoryRegionType_LegacyLpsSemaphore | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x70016000, 0x1000, KMemoryRegionType_LegacyLpsAtomics | KMemoryRegionAttr_ShouldKernelMap)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(0x60006000, 0x1000, KMemoryRegionType_LegacyLpsClkRst | KMemoryRegionAttr_ShouldKernelMap)); + } } void SetupDramPhysicalMemoryRegions() { @@ -65,6 +101,155 @@ namespace ams::kern { MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(physical_memory_base_address), ReservedEarlyDramSize, KMemoryRegionType_DramReservedEarly)); } + void SetupPoolPartitionMemoryRegions() { + /* Start by identifying the extents of the DRAM memory region. */ + const auto dram_extents = KMemoryLayout::GetMainMemoryPhysicalExtents(); + + /* Determine the end of the pool region. */ + const uintptr_t pool_end = dram_extents.GetEndAddress() - KTraceBufferSize; + + /* Find the start of the kernel DRAM region. */ + const KMemoryRegion *kernel_dram_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstDerived(KMemoryRegionType_DramKernelBase); + MESOSPHERE_INIT_ABORT_UNLESS(kernel_dram_region != nullptr); + + const uintptr_t kernel_dram_start = kernel_dram_region->GetAddress(); + MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(kernel_dram_start, CarveoutAlignment)); + + /* Find the start of the pool partitions region. */ + const KMemoryRegion *pool_partitions_region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindByTypeAndAttribute(KMemoryRegionType_DramPoolPartition, 0); + MESOSPHERE_INIT_ABORT_UNLESS(pool_partitions_region != nullptr); + const uintptr_t pool_partitions_start = pool_partitions_region->GetAddress(); + + /* Setup the pool partition layouts. */ + if (GetTargetFirmware() >= TargetFirmware_5_0_0) { + /* On 5.0.0+, setup modern 4-pool-partition layout. */ + + /* Get Application and Applet pool sizes. */ + const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize(); + const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize(); + const size_t unsafe_system_pool_min_size = KSystemControl::Init::GetMinimumNonSecureSystemPoolSize(); + + /* Decide on starting addresses for our pools. */ + const uintptr_t application_pool_start = pool_end - application_pool_size; + const uintptr_t applet_pool_start = application_pool_start - applet_pool_size; + const uintptr_t unsafe_system_pool_start = std::min(kernel_dram_start + CarveoutSizeMax, util::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment)); + const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start; + + /* We want to arrange application pool depending on where the middle of dram is. */ + const uintptr_t dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2; + u32 cur_pool_attr = 0; + size_t total_overhead_size = 0; + if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) { + InsertPoolPartitionRegionIntoBothTrees(application_pool_start, application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(application_pool_size); + } else { + const size_t first_application_pool_size = dram_midpoint - application_pool_start; + const size_t second_application_pool_size = application_pool_start + application_pool_size - dram_midpoint; + InsertPoolPartitionRegionIntoBothTrees(application_pool_start, first_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); + InsertPoolPartitionRegionIntoBothTrees(dram_midpoint, second_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size); + } + + /* Insert the applet pool. */ + InsertPoolPartitionRegionIntoBothTrees(applet_pool_start, applet_pool_size, KMemoryRegionType_DramAppletPool, KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(applet_pool_size); + + /* Insert the nonsecure system pool. */ + InsertPoolPartitionRegionIntoBothTrees(unsafe_system_pool_start, unsafe_system_pool_size, KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool, cur_pool_attr); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(unsafe_system_pool_size); + + /* Insert the pool management region. */ + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize((unsafe_system_pool_start - pool_partitions_start) - total_overhead_size); + const uintptr_t pool_management_start = unsafe_system_pool_start - total_overhead_size; + const size_t pool_management_size = total_overhead_size; + u32 pool_management_attr = 0; + InsertPoolPartitionRegionIntoBothTrees(pool_management_start, pool_management_size, KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, pool_management_attr); + + /* Insert the system pool. */ + const uintptr_t system_pool_size = pool_management_start - pool_partitions_start; + InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr); + } else { + /* On < 5.0.0, setup a legacy 2-pool layout for backwards compatibility. */ + + static_assert(KMemoryManager::Pool_Count == 4); + static_assert(KMemoryManager::Pool_Unsafe == KMemoryManager::Pool_Application); + static_assert(KMemoryManager::Pool_Secure == KMemoryManager::Pool_System); + + /* Get Secure pool size. */ + const size_t secure_pool_size = [] ALWAYS_INLINE_LAMBDA (auto target_firmware) -> size_t { + constexpr size_t LegacySecureKernelSize = 8_MB; /* KPageBuffer pages, other small kernel allocations. */ + constexpr size_t LegacySecureMiscSize = 1_MB; /* Miscellaneous pages for secure process mapping. */ + constexpr size_t LegacySecureHeapSize = 24_MB; /* Heap pages for secure process mapping (fs). */ + constexpr size_t LegacySecureEsSize = 1_MB + 232_KB; /* Size for additional secure process (es, 4.0.0+). */ + + /* The baseline size for the secure region is enough to cover any allocations the kernel might make. */ + size_t size = LegacySecureKernelSize; + + /* If on 2.0.0+, initial processes will fall within the secure region. */ + if (target_firmware >= TargetFirmware_2_0_0) { + /* Account for memory used directly for the processes. */ + size += GetInitialProcessesSecureMemorySize(); + + /* Account for heap and transient memory used by the processes. */ + size += LegacySecureHeapSize + LegacySecureMiscSize; + } + + /* If on 4.0.0+, any process may use secure memory via a create process flag. */ + /* In process this is used for es alone, and the secure pool's size should be */ + /* increased to accommodate es's binary. */ + if (target_firmware >= TargetFirmware_4_0_0) { + size += LegacySecureEsSize; + } + + return size; + }(GetTargetFirmware()); + + /* Calculate the overhead for the secure and (defunct) applet/non-secure-system pools. */ + size_t total_overhead_size = KMemoryManager::CalculateManagementOverheadSize(secure_pool_size); + + /* Calculate the overhead for (an amount larger than) the unsafe pool. */ + const size_t approximate_total_overhead_size = total_overhead_size + KMemoryManager::CalculateManagementOverheadSize((pool_end - pool_partitions_start) - secure_pool_size - total_overhead_size) + 2 * PageSize; + + /* Determine the start of the unsafe region. */ + const uintptr_t unsafe_memory_start = util::AlignUp(pool_partitions_start + secure_pool_size + approximate_total_overhead_size, CarveoutAlignment); + + /* Determine the start of the pool regions. */ + const uintptr_t application_pool_start = unsafe_memory_start; + + /* Determine the pool sizes. */ + const size_t application_pool_size = pool_end - application_pool_start; + + /* We want to arrange application pool depending on where the middle of dram is. */ + const uintptr_t dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2; + u32 cur_pool_attr = 0; + if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) { + InsertPoolPartitionRegionIntoBothTrees(application_pool_start, application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(application_pool_size); + } else { + const size_t first_application_pool_size = dram_midpoint - application_pool_start; + const size_t second_application_pool_size = application_pool_start + application_pool_size - dram_midpoint; + InsertPoolPartitionRegionIntoBothTrees(application_pool_start, first_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); + InsertPoolPartitionRegionIntoBothTrees(dram_midpoint, second_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(first_application_pool_size); + total_overhead_size += KMemoryManager::CalculateManagementOverheadSize(second_application_pool_size); + } + + /* Insert the secure pool. */ + InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, secure_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr); + + /* Insert the pool management region. */ + MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= approximate_total_overhead_size); + + const uintptr_t pool_management_start = pool_partitions_start + secure_pool_size; + const size_t pool_management_size = unsafe_memory_start - pool_management_start; + MESOSPHERE_INIT_ABORT_UNLESS(total_overhead_size <= pool_management_size); + + u32 pool_management_attr = 0; + InsertPoolPartitionRegionIntoBothTrees(pool_management_start, pool_management_size, KMemoryRegionType_DramPoolManagement, KMemoryRegionType_VirtualDramPoolManagement, pool_management_attr); + } + } + } } \ No newline at end of file diff --git a/libraries/libmesosphere/source/kern_k_memory_layout.cpp b/libraries/libmesosphere/source/kern_k_memory_layout.cpp index e768f6af1..4eca1d728 100644 --- a/libraries/libmesosphere/source/kern_k_memory_layout.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_layout.cpp @@ -17,55 +17,99 @@ namespace ams::kern { + namespace { + + class KMemoryRegionAllocator { + NON_COPYABLE(KMemoryRegionAllocator); + NON_MOVEABLE(KMemoryRegionAllocator); + public: + static constexpr size_t MaxMemoryRegions = 1000; + private: + KMemoryRegion region_heap[MaxMemoryRegions]; + size_t num_regions; + public: + constexpr ALWAYS_INLINE KMemoryRegionAllocator() : region_heap(), num_regions() { /* ... */ } + public: + template + ALWAYS_INLINE KMemoryRegion *Allocate(Args&&... args) { + /* Ensure we stay within the bounds of our heap. */ + MESOSPHERE_INIT_ABORT_UNLESS(this->num_regions < MaxMemoryRegions); + + /* Create the new region. */ + KMemoryRegion *region = std::addressof(this->region_heap[this->num_regions++]); + new (region) KMemoryRegion(std::forward(args)...); + + return region; + + return &this->region_heap[this->num_regions++]; + } + }; + + constinit KMemoryRegionAllocator g_memory_region_allocator; + + template + ALWAYS_INLINE KMemoryRegion *AllocateRegion(Args&&... args) { + return g_memory_region_allocator.Allocate(std::forward(args)...); + } + + + } + + void KMemoryRegionTree::InsertDirectly(uintptr_t address, size_t size, u32 attr, u32 type_id) { + this->insert(*AllocateRegion(address, size, attr, type_id)); + } + bool KMemoryRegionTree::Insert(uintptr_t address, size_t size, u32 type_id, u32 new_attr, u32 old_attr) { /* Locate the memory region that contains the address. */ - auto it = this->FindContainingRegion(address); + KMemoryRegion *found = this->FindModifiable(address); /* We require that the old attr is correct. */ - if (it->GetAttributes() != old_attr) { + if (found->GetAttributes() != old_attr) { return false; } /* We further require that the region can be split from the old region. */ const uintptr_t inserted_region_end = address + size; const uintptr_t inserted_region_last = inserted_region_end - 1; - if (it->GetLastAddress() < inserted_region_last) { + if (found->GetLastAddress() < inserted_region_last) { return false; } /* Further, we require that the type id is a valid transformation. */ - if (!it->CanDerive(type_id)) { + if (!found->CanDerive(type_id)) { return false; } /* Cache information from the region before we remove it. */ - KMemoryRegion *cur_region = std::addressof(*it); - const uintptr_t old_address = it->GetAddress(); - const size_t old_size = it->GetSize(); + const uintptr_t old_address = found->GetAddress(); + const size_t old_size = found->GetSize(); const uintptr_t old_end = old_address + old_size; const uintptr_t old_last = old_end - 1; - const uintptr_t old_pair = it->GetPairAddress(); - const u32 old_type = it->GetType(); + const uintptr_t old_pair = found->GetPairAddress(); + const u32 old_type = found->GetType(); /* Erase the existing region from the tree. */ - this->erase(it); + this->erase(this->iterator_to(*found)); - /* If we need to insert a region before the region, do so. */ - if (old_address != address) { - new (cur_region) KMemoryRegion(old_address, address - old_address, old_pair, old_attr, old_type); - this->insert(*cur_region); - cur_region = KMemoryLayout::GetMemoryRegionAllocator().Allocate(); - } - - /* Insert a new region. */ + /* Insert the new region into the tree. */ const uintptr_t new_pair = (old_pair != std::numeric_limits::max()) ? old_pair + (address - old_address) : old_pair; - new (cur_region) KMemoryRegion(address, size, new_pair, new_attr, type_id); - this->insert(*cur_region); + if (old_address == address) { + /* Reuse the old object for the new region, if we can. */ + found->Reset(address, size, new_pair, new_attr, type_id); + this->insert(*found); + } else { + /* If we can't re-use, adjust the old region. */ + found->Reset(old_address, address - old_address, old_pair, old_attr, old_type); + this->insert(*found); + + /* Insert a new region for the split. */ + this->insert(*AllocateRegion(address, size, new_pair, new_attr, type_id)); + } /* If we need to insert a region after the region, do so. */ if (old_last != inserted_region_last) { const uintptr_t after_pair = (old_pair != std::numeric_limits::max()) ? old_pair + (inserted_region_end - old_address) : old_pair; - this->insert(*KMemoryLayout::GetMemoryRegionAllocator().Create(inserted_region_end, old_end - inserted_region_end, after_pair, old_attr, old_type)); + this->insert(*AllocateRegion(inserted_region_end, old_end - inserted_region_end, after_pair, old_attr, old_type)); } return true; @@ -96,16 +140,11 @@ namespace ams::kern { continue; } - /* Locate the candidate region, and ensure it fits. */ - const KMemoryRegion *candidate_region = std::addressof(*this->FindContainingRegion(candidate)); - if (candidate_last > candidate_region->GetLastAddress()) { + /* Locate the candidate region, and ensure it fits and has the correct type id. */ + if (const auto &candidate_region = *this->Find(candidate); !(candidate_last <= candidate_region.GetLastAddress() && candidate_region.GetType() == type_id)) { continue; } - /* Ensure that the region has the correct type id. */ - if (candidate_region->GetType() != type_id) - continue; - return candidate; } } @@ -117,29 +156,37 @@ namespace ams::kern { /* Initialize linear trees. */ for (auto ®ion : GetPhysicalMemoryRegionTree()) { - if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { - continue; + if (region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { + GetPhysicalLinearMemoryRegionTree().InsertDirectly(region.GetAddress(), region.GetSize(), region.GetAttributes(), region.GetType()); } - GetPhysicalLinearMemoryRegionTree().insert(*GetMemoryRegionAllocator().Create(region.GetAddress(), region.GetSize(), region.GetAttributes(), region.GetType())); } for (auto ®ion : GetVirtualMemoryRegionTree()) { - if (!region.IsDerivedFrom(KMemoryRegionType_Dram)) { - continue; + if (region.IsDerivedFrom(KMemoryRegionType_Dram)) { + GetVirtualLinearMemoryRegionTree().InsertDirectly(region.GetAddress(), region.GetSize(), region.GetAttributes(), region.GetType()); } - GetVirtualLinearMemoryRegionTree().insert(*GetMemoryRegionAllocator().Create(region.GetAddress(), region.GetSize(), region.GetAttributes(), region.GetType())); } } + size_t KMemoryLayout::GetResourceRegionSizeForInit() { + /* Calculate resource region size based on whether we allow extra threads. */ + const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); + size_t resource_region_size = KernelResourceSize + (use_extra_resources ? KernelSlabHeapAdditionalSize : 0); + + /* 10.0.0 reduced the slab heap gaps by 64K. */ + if (kern::GetTargetFirmware() < ams::TargetFirmware_10_0_0) { + resource_region_size += (KernelSlabHeapGapsSizeDeprecated - KernelSlabHeapGapsSize); + } + + return resource_region_size; + } + namespace init { namespace { constexpr PageTableEntry KernelRwDataAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); - constexpr size_t CarveoutAlignment = 0x20000; - constexpr size_t CarveoutSizeMax = 512_MB - CarveoutAlignment; - constexpr size_t CoreLocalRegionAlign = PageSize; constexpr size_t CoreLocalRegionSize = PageSize * (1 + cpu::NumCores); constexpr size_t CoreLocalRegionSizeWithGuards = CoreLocalRegionSize + 2 * PageSize; @@ -152,13 +199,13 @@ namespace ams::kern { const uintptr_t candidate_end = candidate_start + CoreLocalRegionSizeWithGuards; const uintptr_t candidate_last = candidate_end - 1; - const KMemoryRegion *containing_region = std::addressof(*KMemoryLayout::GetVirtualMemoryRegionTree().FindContainingRegion(candidate_start)); + const auto &containing_region = *KMemoryLayout::GetVirtualMemoryRegionTree().Find(candidate_start); - if (candidate_last > containing_region->GetLastAddress()) { + if (candidate_last > containing_region.GetLastAddress()) { continue; } - if (containing_region->GetType() != KMemoryRegionType_None) { + if (containing_region.GetType() != KMemoryRegionType_None) { continue; } @@ -166,11 +213,11 @@ namespace ams::kern { continue; } - if (containing_region->GetAddress() > util::AlignDown(candidate_start, CoreLocalRegionBoundsAlign)) { + if (containing_region.GetAddress() > util::AlignDown(candidate_start, CoreLocalRegionBoundsAlign)) { continue; } - if (util::AlignUp(candidate_last, CoreLocalRegionBoundsAlign) - 1 > containing_region->GetLastAddress()) { + if (util::AlignUp(candidate_last, CoreLocalRegionBoundsAlign) - 1 > containing_region.GetLastAddress()) { continue; } @@ -179,17 +226,15 @@ namespace ams::kern { } - void InsertPoolPartitionRegionIntoBothTrees(size_t start, size_t size, KMemoryRegionType phys_type, KMemoryRegionType virt_type, u32 &cur_attr) { - const u32 attr = cur_attr++; - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(start, size, phys_type, attr)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstRegionByTypeAttr(phys_type, attr)->GetPairAddress(), size, virt_type, attr)); - } - } void SetupCoreLocalRegionMemoryRegions(KInitialPageTable &page_table, KInitialPageAllocator &page_allocator) { + /* NOTE: Nintendo passes page table here to use num_l1_entries; we don't use this at present. */ + MESOSPHERE_UNUSED(page_table); + + /* Get the virtual address of the core local reigon. */ const KVirtualAddress core_local_virt_start = GetCoreLocalRegionVirtualAddress(); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(core_local_virt_start), CoreLocalRegionSize, KMemoryRegionType_CoreLocal)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(core_local_virt_start), CoreLocalRegionSize, KMemoryRegionType_CoreLocalRegion)); /* Allocate a page for each core. */ KPhysicalAddress core_local_region_start_phys[cpu::NumCores] = {}; @@ -216,68 +261,6 @@ namespace ams::kern { /* Setup the InitArguments. */ SetInitArguments(static_cast(i), core_local_region_start_phys[i], GetInteger(core_l1_ttbr1_phys[i])); } - - /* Ensure the InitArguments are flushed to cache. */ - StoreInitArguments(); - } - - void SetupPoolPartitionMemoryRegions() { - /* Start by identifying the extents of the DRAM memory region. */ - const auto dram_extents = KMemoryLayout::GetPhysicalMemoryRegionTree().GetDerivedRegionExtents(KMemoryRegionType_Dram); - - /* Get Application and Applet pool sizes. */ - const size_t application_pool_size = KSystemControl::Init::GetApplicationPoolSize(); - const size_t applet_pool_size = KSystemControl::Init::GetAppletPoolSize(); - const size_t unsafe_system_pool_min_size = KSystemControl::Init::GetMinimumNonSecureSystemPoolSize(); - - /* Find the start of the kernel DRAM region. */ - const uintptr_t kernel_dram_start = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstDerivedRegion(KMemoryRegionType_DramKernel)->GetAddress(); - MESOSPHERE_INIT_ABORT_UNLESS(util::IsAligned(kernel_dram_start, CarveoutAlignment)); - - /* Find the start of the pool partitions region. */ - const uintptr_t pool_partitions_start = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstRegionByTypeAttr(KMemoryRegionType_DramPoolPartition)->GetAddress(); - - /* Decide on starting addresses for our pools. */ - const uintptr_t application_pool_start = dram_extents.GetEndAddress() - application_pool_size; - const uintptr_t applet_pool_start = application_pool_start - applet_pool_size; - const uintptr_t unsafe_system_pool_start = std::min(kernel_dram_start + CarveoutSizeMax, util::AlignDown(applet_pool_start - unsafe_system_pool_min_size, CarveoutAlignment)); - const size_t unsafe_system_pool_size = applet_pool_start - unsafe_system_pool_start; - - /* We want to arrange application pool depending on where the middle of dram is. */ - const uintptr_t dram_midpoint = (dram_extents.GetAddress() + dram_extents.GetEndAddress()) / 2; - u32 cur_pool_attr = 0; - size_t total_overhead_size = 0; - if (dram_extents.GetEndAddress() <= dram_midpoint || dram_midpoint <= application_pool_start) { - InsertPoolPartitionRegionIntoBothTrees(application_pool_start, application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); - total_overhead_size += KMemoryManager::CalculateMetadataOverheadSize(application_pool_size); - } else { - const size_t first_application_pool_size = dram_midpoint - application_pool_start; - const size_t second_application_pool_size = application_pool_start + application_pool_size - dram_midpoint; - InsertPoolPartitionRegionIntoBothTrees(application_pool_start, first_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); - InsertPoolPartitionRegionIntoBothTrees(dram_midpoint, second_application_pool_size, KMemoryRegionType_DramApplicationPool, KMemoryRegionType_VirtualDramApplicationPool, cur_pool_attr); - total_overhead_size += KMemoryManager::CalculateMetadataOverheadSize(first_application_pool_size); - total_overhead_size += KMemoryManager::CalculateMetadataOverheadSize(second_application_pool_size); - } - - /* Insert the applet pool. */ - InsertPoolPartitionRegionIntoBothTrees(applet_pool_start, applet_pool_size, KMemoryRegionType_DramAppletPool, KMemoryRegionType_VirtualDramAppletPool, cur_pool_attr); - total_overhead_size += KMemoryManager::CalculateMetadataOverheadSize(applet_pool_size); - - /* Insert the nonsecure system pool. */ - InsertPoolPartitionRegionIntoBothTrees(unsafe_system_pool_start, unsafe_system_pool_size, KMemoryRegionType_DramSystemNonSecurePool, KMemoryRegionType_VirtualDramSystemNonSecurePool, cur_pool_attr); - total_overhead_size += KMemoryManager::CalculateMetadataOverheadSize(unsafe_system_pool_size); - - /* Insert the metadata pool. */ - total_overhead_size += KMemoryManager::CalculateMetadataOverheadSize((unsafe_system_pool_start - pool_partitions_start) - total_overhead_size); - const uintptr_t metadata_pool_start = unsafe_system_pool_start - total_overhead_size; - const size_t metadata_pool_size = total_overhead_size; - u32 metadata_pool_attr = 0; - InsertPoolPartitionRegionIntoBothTrees(metadata_pool_start, metadata_pool_size, KMemoryRegionType_DramMetadataPool, KMemoryRegionType_VirtualDramMetadataPool, metadata_pool_attr); - - /* Insert the system pool. */ - const uintptr_t system_pool_size = metadata_pool_start - pool_partitions_start; - InsertPoolPartitionRegionIntoBothTrees(pool_partitions_start, system_pool_size, KMemoryRegionType_DramSystemPool, KMemoryRegionType_VirtualDramSystemPool, cur_pool_attr); - } } diff --git a/libraries/libmesosphere/source/kern_k_memory_manager.cpp b/libraries/libmesosphere/source/kern_k_memory_manager.cpp index 082c1db01..517805dee 100644 --- a/libraries/libmesosphere/source/kern_k_memory_manager.cpp +++ b/libraries/libmesosphere/source/kern_k_memory_manager.cpp @@ -31,10 +31,10 @@ namespace ams::kern { } - void KMemoryManager::Initialize(KVirtualAddress metadata_region, size_t metadata_region_size) { - /* Clear the metadata region to zero. */ - const KVirtualAddress metadata_region_end = metadata_region + metadata_region_size; - std::memset(GetVoidPointer(metadata_region), 0, metadata_region_size); + void KMemoryManager::Initialize(KVirtualAddress management_region, size_t management_region_size) { + /* Clear the management region to zero. */ + const KVirtualAddress management_region_end = management_region + management_region_size; + std::memset(GetVoidPointer(management_region), 0, management_region_size); /* Traverse the virtual memory layout tree, initializing each manager as appropriate. */ while (true) { @@ -42,7 +42,7 @@ namespace ams::kern { const KMemoryRegion *region = nullptr; for (const auto &it : KMemoryLayout::GetVirtualMemoryRegionTree()) { /* We only care about regions that we need to create managers for. */ - if (!it.IsDerivedFrom(KMemoryRegionType_VirtualDramManagedPool)) { + if (!it.IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)) { continue; } @@ -64,7 +64,7 @@ namespace ams::kern { MESOSPHERE_ASSERT(region->GetAddress() != NullGetAddress())>); MESOSPHERE_ASSERT(region->GetSize() > 0); MESOSPHERE_ASSERT(region->GetEndAddress() >= region->GetAddress()); - MESOSPHERE_ASSERT(region->IsDerivedFrom(KMemoryRegionType_VirtualDramManagedPool)); + MESOSPHERE_ASSERT(region->IsDerivedFrom(KMemoryRegionType_VirtualDramUserPool)); MESOSPHERE_ASSERT(region->GetAttributes() == this->num_managers); /* Initialize a new manager for the region. */ @@ -72,9 +72,9 @@ namespace ams::kern { Impl *manager = std::addressof(this->managers[this->num_managers++]); MESOSPHERE_ABORT_UNLESS(this->num_managers <= util::size(this->managers)); - const size_t cur_size = manager->Initialize(region, pool, metadata_region, metadata_region_end); - metadata_region += cur_size; - MESOSPHERE_ABORT_UNLESS(metadata_region <= metadata_region_end); + const size_t cur_size = manager->Initialize(region, pool, management_region, management_region_end); + management_region += cur_size; + MESOSPHERE_ABORT_UNLESS(management_region <= management_region_end); /* Insert the manager into the pool list. */ if (this->pool_managers_tail[pool] == nullptr) { @@ -203,7 +203,6 @@ namespace ams::kern { } /* Only succeed if we allocated as many pages as we wanted. */ - MESOSPHERE_ASSERT(num_pages >= 0); R_UNLESS(num_pages == 0, svc::ResultOutOfMemory()); /* We succeeded! */ @@ -234,21 +233,24 @@ namespace ams::kern { const auto [pool, dir] = DecodeOption(option); /* Allocate the memory. */ - bool has_optimized, is_optimized; + bool optimized; { /* Lock the pool that we're allocating from. */ KScopedLightLock lk(this->pool_locks[pool]); /* Check if we have an optimized process. */ - has_optimized = this->has_optimized_process[pool]; - is_optimized = this->optimized_process_ids[pool] == process_id; + const bool has_optimized = this->has_optimized_process[pool]; + const bool is_optimized = this->optimized_process_ids[pool] == process_id; /* Allocate the page group. */ R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, has_optimized && !is_optimized, false)); + + /* Set whether we should optimize. */ + optimized = has_optimized && is_optimized; } /* Perform optimized memory tracking, if we should. */ - if (has_optimized && is_optimized) { + if (optimized) { /* Iterate over the allocated blocks. */ for (const auto &block : *out) { /* Get the block extents. */ @@ -264,41 +266,41 @@ namespace ams::kern { bool any_new = false; { KVirtualAddress cur_address = block_address; - size_t cur_pages = block_pages; - while (cur_pages > 0) { + size_t remaining_pages = block_pages; + while (remaining_pages > 0) { /* Get the manager for the current address. */ auto &manager = this->GetManager(cur_address); /* Process part or all of the block. */ - const size_t processed_pages = manager.ProcessOptimizedAllocation(std::addressof(any_new), cur_address, cur_pages, fill_pattern); + const size_t cur_pages = std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); + any_new = manager.ProcessOptimizedAllocation(cur_address, cur_pages, fill_pattern); /* Advance. */ - cur_address += processed_pages * PageSize; - cur_pages -= processed_pages; + cur_address += cur_pages * PageSize; + remaining_pages -= cur_pages; } } - /* If there are no new pages, move on to the next block. */ - if (!any_new) { - continue; - } + /* If there are new pages, update tracking for the allocation. */ + if (any_new) { + /* Update tracking for the allocation. */ + KVirtualAddress cur_address = block_address; + size_t remaining_pages = block_pages; + while (remaining_pages > 0) { + /* Get the manager for the current address. */ + auto &manager = this->GetManager(cur_address); - /* Update tracking for the allocation. */ - KVirtualAddress cur_address = block_address; - size_t cur_pages = block_pages; - while (cur_pages > 0) { - /* Get the manager for the current address. */ - auto &manager = this->GetManager(cur_address); + /* Lock the pool for the manager. */ + KScopedLightLock lk(this->pool_locks[manager.GetPool()]); - /* Lock the pool for the manager. */ - KScopedLightLock lk(this->pool_locks[manager.GetPool()]); + /* Track some or all of the current pages. */ + const size_t cur_pages = std::min(remaining_pages, manager.GetPageOffsetToEnd(cur_address)); + manager.TrackOptimizedAllocation(cur_address, cur_pages); - /* Track some or all of the current pages. */ - const size_t processed_pages = manager.TrackOptimizedAllocation(cur_address, cur_pages); - - /* Advance. */ - cur_address += processed_pages * PageSize; - cur_pages -= processed_pages; + /* Advance. */ + cur_address += cur_pages * PageSize; + remaining_pages -= cur_pages; + } } } } else { @@ -311,25 +313,25 @@ namespace ams::kern { return ResultSuccess(); } - size_t KMemoryManager::Impl::Initialize(const KMemoryRegion *region, Pool p, KVirtualAddress metadata, KVirtualAddress metadata_end) { - /* Calculate metadata sizes. */ + size_t KMemoryManager::Impl::Initialize(const KMemoryRegion *region, Pool p, KVirtualAddress management, KVirtualAddress management_end) { + /* Calculate management sizes. */ const size_t ref_count_size = (region->GetSize() / PageSize) * sizeof(u16); const size_t optimize_map_size = CalculateOptimizedProcessOverheadSize(region->GetSize()); const size_t manager_size = util::AlignUp(optimize_map_size + ref_count_size, PageSize); - const size_t page_heap_size = KPageHeap::CalculateMetadataOverheadSize(region->GetSize()); - const size_t total_metadata_size = manager_size + page_heap_size; - MESOSPHERE_ABORT_UNLESS(manager_size <= total_metadata_size); - MESOSPHERE_ABORT_UNLESS(metadata + total_metadata_size <= metadata_end); - MESOSPHERE_ABORT_UNLESS(util::IsAligned(total_metadata_size, PageSize)); + const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region->GetSize()); + const size_t total_management_size = manager_size + page_heap_size; + MESOSPHERE_ABORT_UNLESS(manager_size <= total_management_size); + MESOSPHERE_ABORT_UNLESS(management + total_management_size <= management_end); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(total_management_size, PageSize)); /* Setup region. */ this->pool = p; - this->metadata_region = metadata; - this->page_reference_counts = GetPointer(metadata + optimize_map_size); - MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(this->metadata_region), PageSize)); + this->management_region = management; + this->page_reference_counts = GetPointer(management + optimize_map_size); + MESOSPHERE_ABORT_UNLESS(util::IsAligned(GetInteger(this->management_region), PageSize)); /* Initialize the manager's KPageHeap. */ - this->heap.Initialize(region->GetAddress(), region->GetSize(), metadata + manager_size, page_heap_size); + this->heap.Initialize(region->GetAddress(), region->GetSize(), management + manager_size, page_heap_size); /* Free the memory to the heap. */ this->heap.Free(region->GetAddress(), region->GetSize() / PageSize); @@ -337,58 +339,54 @@ namespace ams::kern { /* Update the heap's used size. */ this->heap.UpdateUsedSize(); - return total_metadata_size; + return total_management_size; } void KMemoryManager::Impl::TrackUnoptimizedAllocation(KVirtualAddress block, size_t num_pages) { - size_t offset = this->heap.GetPageOffset(block); + /* Get the range we're tracking. */ + size_t offset = this->GetPageOffset(block); const size_t last = offset + num_pages - 1; - u64 *optimize_map = GetPointer(this->metadata_region); + + /* Track. */ + u64 *optimize_map = GetPointer(this->management_region); while (offset <= last) { + /* Mark the page as not being optimized-allocated. */ optimize_map[offset / BITSIZEOF(u64)] &= ~(u64(1) << (offset % BITSIZEOF(u64))); + offset++; } } - size_t KMemoryManager::Impl::TrackOptimizedAllocation(KVirtualAddress block, size_t num_pages) { - /* Get the number of tracking pages. */ - const size_t cur_pages = std::min(num_pages, this->heap.GetPageOffsetToEnd(block)); - + void KMemoryManager::Impl::TrackOptimizedAllocation(KVirtualAddress block, size_t num_pages) { /* Get the range we're tracking. */ - size_t offset = this->heap.GetPageOffset(block); - const size_t last = offset + cur_pages - 1; + size_t offset = this->GetPageOffset(block); + const size_t last = offset + num_pages - 1; /* Track. */ - u64 *optimize_map = GetPointer(this->metadata_region); + u64 *optimize_map = GetPointer(this->management_region); while (offset <= last) { /* Mark the page as being optimized-allocated. */ optimize_map[offset / BITSIZEOF(u64)] |= (u64(1) << (offset % BITSIZEOF(u64))); offset++; } - - /* Return the number of pages we tracked. */ - return cur_pages; } - size_t KMemoryManager::Impl::ProcessOptimizedAllocation(bool *out_any_new, KVirtualAddress block, size_t num_pages, u8 fill_pattern) { - /* Get the number of processable pages. */ - const size_t cur_pages = std::min(num_pages, this->heap.GetPageOffsetToEnd(block)); - - /* Clear any new. */ - *out_any_new = false; + bool KMemoryManager::Impl::ProcessOptimizedAllocation(KVirtualAddress block, size_t num_pages, u8 fill_pattern) { + /* We want to return whether any pages were newly allocated. */ + bool any_new = false; /* Get the range we're processing. */ - size_t offset = this->heap.GetPageOffset(block); - const size_t last = offset + cur_pages - 1; + size_t offset = this->GetPageOffset(block); + const size_t last = offset + num_pages - 1; /* Process. */ - u64 *optimize_map = GetPointer(this->metadata_region); + u64 *optimize_map = GetPointer(this->management_region); while (offset <= last) { /* Check if the page has been optimized-allocated before. */ if ((optimize_map[offset / BITSIZEOF(u64)] & (u64(1) << (offset % BITSIZEOF(u64)))) == 0) { /* If not, it's new. */ - *out_any_new = true; + any_new = true; /* Fill the page. */ std::memset(GetVoidPointer(this->heap.GetAddress() + offset * PageSize), fill_pattern, PageSize); @@ -398,14 +396,14 @@ namespace ams::kern { } /* Return the number of pages we processed. */ - return cur_pages; + return any_new; } - size_t KMemoryManager::Impl::CalculateMetadataOverheadSize(size_t region_size) { + size_t KMemoryManager::Impl::CalculateManagementOverheadSize(size_t region_size) { const size_t ref_count_size = (region_size / PageSize) * sizeof(u16); const size_t optimize_map_size = (util::AlignUp((region_size / PageSize), BITSIZEOF(u64)) / BITSIZEOF(u64)) * sizeof(u64); const size_t manager_meta_size = util::AlignUp(optimize_map_size + ref_count_size, PageSize); - const size_t page_heap_size = KPageHeap::CalculateMetadataOverheadSize(region_size); + const size_t page_heap_size = KPageHeap::CalculateManagementOverheadSize(region_size); return manager_meta_size + page_heap_size; } diff --git a/libraries/libmesosphere/source/kern_k_page_heap.cpp b/libraries/libmesosphere/source/kern_k_page_heap.cpp index dd971ea7f..6199649e8 100644 --- a/libraries/libmesosphere/source/kern_k_page_heap.cpp +++ b/libraries/libmesosphere/source/kern_k_page_heap.cpp @@ -17,12 +17,12 @@ namespace ams::kern { - void KPageHeap::Initialize(KVirtualAddress address, size_t size, KVirtualAddress metadata_address, size_t metadata_size, const size_t *block_shifts, size_t num_block_shifts) { + void KPageHeap::Initialize(KVirtualAddress address, size_t size, KVirtualAddress management_address, size_t management_size, const size_t *block_shifts, size_t num_block_shifts) { /* Check our assumptions. */ MESOSPHERE_ASSERT(util::IsAligned(GetInteger(address), PageSize)); MESOSPHERE_ASSERT(util::IsAligned(size, PageSize)); MESOSPHERE_ASSERT(0 < num_block_shifts && num_block_shifts <= NumMemoryBlockPageShifts); - const KVirtualAddress metadata_end = metadata_address + metadata_size; + const KVirtualAddress management_end = management_address + management_size; /* Set our members. */ this->heap_address = address; @@ -30,7 +30,7 @@ namespace ams::kern { this->num_blocks = num_block_shifts; /* Setup bitmaps. */ - u64 *cur_bitmap_storage = GetPointer(metadata_address); + u64 *cur_bitmap_storage = GetPointer(management_address); for (size_t i = 0; i < num_block_shifts; i++) { const size_t cur_block_shift = block_shifts[i]; const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0; @@ -38,7 +38,7 @@ namespace ams::kern { } /* Ensure we didn't overextend our bounds. */ - MESOSPHERE_ABORT_UNLESS(KVirtualAddress(cur_bitmap_storage) <= metadata_end); + MESOSPHERE_ABORT_UNLESS(KVirtualAddress(cur_bitmap_storage) <= management_end); } size_t KPageHeap::GetNumFreePages() const { @@ -122,12 +122,12 @@ namespace ams::kern { } } - size_t KPageHeap::CalculateMetadataOverheadSize(size_t region_size, const size_t *block_shifts, size_t num_block_shifts) { + size_t KPageHeap::CalculateManagementOverheadSize(size_t region_size, const size_t *block_shifts, size_t num_block_shifts) { size_t overhead_size = 0; for (size_t i = 0; i < num_block_shifts; i++) { const size_t cur_block_shift = block_shifts[i]; const size_t next_block_shift = (i != num_block_shifts - 1) ? block_shifts[i + 1] : 0; - overhead_size += KPageHeap::Block::CalculateMetadataOverheadSize(region_size, cur_block_shift, next_block_shift); + overhead_size += KPageHeap::Block::CalculateManagementOverheadSize(region_size, cur_block_shift, next_block_shift); } return util::AlignUp(overhead_size, PageSize); } diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index c489c0cca..fb9363592 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -1582,28 +1582,30 @@ namespace ams::kern { const size_t region_num_pages = region_size / PageSize; /* Locate the memory region. */ - auto region_it = KMemoryLayout::FindContainingRegion(phys_addr); - const auto end_it = KMemoryLayout::GetEnd(phys_addr); - R_UNLESS(region_it != end_it, svc::ResultInvalidAddress()); + const KMemoryRegion *region = KMemoryLayout::Find(phys_addr); + R_UNLESS(region != nullptr, svc::ResultInvalidAddress()); - MESOSPHERE_ASSERT(region_it->Contains(GetInteger(phys_addr))); + MESOSPHERE_ASSERT(region->Contains(GetInteger(phys_addr))); /* Ensure that the region is mappable. */ const bool is_rw = perm == KMemoryPermission_UserReadWrite; - do { + while (true) { + /* Check that the region exists. */ + R_UNLESS(region != nullptr, svc::ResultInvalidAddress()); + /* Check the region attributes. */ - R_UNLESS(!region_it->IsDerivedFrom(KMemoryRegionType_Dram), svc::ResultInvalidAddress()); - R_UNLESS(!region_it->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw, svc::ResultInvalidAddress()); - R_UNLESS(!region_it->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), svc::ResultInvalidAddress()); + R_UNLESS(!region->IsDerivedFrom(KMemoryRegionType_Dram), svc::ResultInvalidAddress()); + R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw, svc::ResultInvalidAddress()); + R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), svc::ResultInvalidAddress()); /* Check if we're done. */ - if (GetInteger(last) <= region_it->GetLastAddress()) { + if (GetInteger(last) <= region->GetLastAddress()) { break; } /* Advance. */ - region_it++; - } while (region_it != end_it); + region = region->GetNext(); + }; /* Lock the table. */ KScopedLightLock lk(this->general_lock); @@ -1660,18 +1662,17 @@ namespace ams::kern { const size_t region_num_pages = region_size / PageSize; /* Locate the memory region. */ - auto region_it = KMemoryLayout::FindContainingRegion(phys_addr); - const auto end_it = KMemoryLayout::GetEnd(phys_addr); - R_UNLESS(region_it != end_it, svc::ResultInvalidAddress()); + const KMemoryRegion *region = KMemoryLayout::Find(phys_addr); + R_UNLESS(region != nullptr, svc::ResultInvalidAddress()); - MESOSPHERE_ASSERT(region_it->Contains(GetInteger(phys_addr))); - R_UNLESS(GetInteger(last) <= region_it->GetLastAddress(), svc::ResultInvalidAddress()); + MESOSPHERE_ASSERT(region->Contains(GetInteger(phys_addr))); + R_UNLESS(GetInteger(last) <= region->GetLastAddress(), svc::ResultInvalidAddress()); /* Check the region attributes. */ const bool is_rw = perm == KMemoryPermission_UserReadWrite; - R_UNLESS( region_it->IsDerivedFrom(KMemoryRegionType_Dram), svc::ResultInvalidAddress()); - R_UNLESS(!region_it->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), svc::ResultInvalidAddress()); - R_UNLESS(!region_it->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw, svc::ResultInvalidAddress()); + R_UNLESS( region->IsDerivedFrom(KMemoryRegionType_Dram), svc::ResultInvalidAddress()); + R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_NoUserMap), svc::ResultInvalidAddress()); + R_UNLESS(!region->HasTypeAttribute(KMemoryRegionAttr_UserReadOnly) || !is_rw, svc::ResultInvalidAddress()); /* Lock the table. */ KScopedLightLock lk(this->general_lock); @@ -1716,12 +1717,11 @@ namespace ams::kern { Result KPageTableBase::MapRegion(KMemoryRegionType region_type, KMemoryPermission perm) { /* Get the memory region. */ - auto &tree = KMemoryLayout::GetPhysicalMemoryRegionTree(); - auto it = tree.TryFindFirstDerivedRegion(region_type); - R_UNLESS(it != tree.end(), svc::ResultOutOfRange()); + const KMemoryRegion *region = KMemoryLayout::GetPhysicalMemoryRegionTree().FindFirstDerived(region_type); + R_UNLESS(region != nullptr, svc::ResultOutOfRange()); /* Map the region. */ - R_TRY_CATCH(this->MapStatic(it->GetAddress(), it->GetSize(), perm)) { + R_TRY_CATCH(this->MapStatic(region->GetAddress(), region->GetSize(), perm)) { R_CONVERT(svc::ResultInvalidAddress, svc::ResultOutOfRange()) } R_END_TRY_CATCH; @@ -2157,6 +2157,8 @@ namespace ams::kern { if (cur_size >= sizeof(u32)) { const size_t copy_size = util::AlignDown(cur_size, sizeof(u32)); R_UNLESS(UserspaceAccess::CopyMemoryFromUserAligned32Bit(GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), buffer, copy_size), svc::ResultInvalidCurrentMemory()); + cpu::StoreDataCache(GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), copy_size); + buffer = reinterpret_cast(reinterpret_cast(buffer) + copy_size); cur_addr += copy_size; cur_size -= copy_size; @@ -2165,6 +2167,7 @@ namespace ams::kern { /* Copy remaining data. */ if (cur_size > 0) { R_UNLESS(UserspaceAccess::CopyMemoryFromUser(GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), buffer, cur_size), svc::ResultInvalidCurrentMemory()); + cpu::StoreDataCache(GetVoidPointer(GetLinearMappedVirtualAddress(cur_addr)), cur_size); } return ResultSuccess(); @@ -2200,6 +2203,9 @@ namespace ams::kern { /* Perform copy for the last block. */ R_TRY(PerformCopy()); + /* Invalidate the entire instruction cache, as this svc allows modifying executable pages. */ + cpu::InvalidateEntireInstructionCache(); + return ResultSuccess(); } @@ -2761,9 +2767,12 @@ namespace ams::kern { lk1.emplace(lock_1); } - /* Check memory state. */ + /* Check memory state for source. */ R_TRY(src_page_table.CheckMemoryStateContiguous(src_addr, size, src_state_mask, src_state, src_test_perm, src_test_perm, src_attr_mask | KMemoryAttribute_Uncached, src_attr)); + /* Destination state is intentionally unchecked. */ + MESOSPHERE_UNUSED(dst_state_mask, dst_state, dst_test_perm, dst_attr_mask, dst_attr); + /* Get implementations. */ auto &src_impl = src_page_table.GetImpl(); auto &dst_impl = dst_page_table.GetImpl(); @@ -2906,35 +2915,10 @@ namespace ams::kern { /* Ensure that on failure, we roll back appropriately. */ size_t mapped_size = 0; - auto unmap_guard = SCOPE_GUARD { + auto cleanup_guard = SCOPE_GUARD { if (mapped_size > 0) { - /* Determine where the mapping ends. */ - const auto mapped_end = GetInteger(mapping_src_start) + mapped_size; - const auto mapped_last = mapped_end - 1; - - KMemoryBlockManager::const_iterator it = this->memory_block_manager.FindIterator(mapping_src_start); - while (true) { - const KMemoryInfo info = it->GetMemoryInfo(); - - const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start) ? info.GetAddress() : GetInteger(mapping_src_start); - const auto cur_end = mapped_last <= info.GetLastAddress() ? mapped_end : info.GetEndAddress(); - const size_t cur_size = cur_end - cur_start; - - /* Fix the permissions, if we need to. */ - if ((info.GetPermission() & KMemoryPermission_IpcLockChangeMask) != src_perm) { - const KPageProperties properties = { info.GetPermission(), false, false, false }; - MESOSPHERE_R_ABORT_UNLESS(this->Operate(page_list, cur_start, cur_size / PageSize, Null, false, properties, OperationType_ChangePermissions, true)); - } - - /* If the block is at the end, we're done. */ - if (mapped_last <= info.GetLastAddress()) { - break; - } - - /* Advance. */ - ++it; - MESOSPHERE_ABORT_UNLESS(it != this->memory_block_manager.end()); - } + /* NOTE: Nintendo does not check that this cleanup succeeds. */ + this->CleanupForIpcClientOnServerSetupFailure(page_list, mapping_src_start, mapped_size, test_perm); } }; @@ -2971,8 +2955,8 @@ namespace ams::kern { MESOSPHERE_ABORT_UNLESS(it != this->memory_block_manager.end()); } - /* We succeeded, so no need to unmap. */ - unmap_guard.Cancel(); + /* We succeeded, so no need to cleanup. */ + cleanup_guard.Cancel(); return ResultSuccess(); } diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index 051379f32..bd8336160 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -146,8 +146,11 @@ namespace ams::kern { } Result KProcess::Initialize(const ams::svc::CreateProcessParameter ¶ms) { - /* TODO: Validate intended kernel version. */ - /* How should we do this? */ + /* Validate that the intended kernel version is high enough for us to support. */ + R_UNLESS(this->capabilities.GetIntendedKernelVersion() >= ams::svc::RequiredKernelVersion, svc::ResultInvalidCombination()); + + /* Validate that the intended kernel version isn't too high for us to support. */ + R_UNLESS(this->capabilities.GetIntendedKernelVersion() <= ams::svc::SupportedKernelVersion, svc::ResultInvalidCombination()); /* Create and clear the process local region. */ R_TRY(this->CreateThreadLocalRegion(std::addressof(this->plr_address))); @@ -493,6 +496,9 @@ namespace ams::kern { /* Lock ourselves, to prevent concurrent access. */ KScopedLightLock lk(this->state_lock); + /* Address and size parameters aren't used. */ + MESOSPHERE_UNUSED(address, size); + /* Try to find an existing info for the memory. */ KSharedMemoryInfo *info = nullptr; for (auto it = this->shared_memory_list.begin(); it != this->shared_memory_list.end(); ++it) { @@ -524,6 +530,9 @@ namespace ams::kern { /* Lock ourselves, to prevent concurrent access. */ KScopedLightLock lk(this->state_lock); + /* Address and size parameters aren't used. */ + MESOSPHERE_UNUSED(address, size); + /* Find an existing info for the memory. */ KSharedMemoryInfo *info = nullptr; auto it = this->shared_memory_list.begin(); @@ -731,6 +740,8 @@ namespace ams::kern { this->exception_thread->AddWaiter(cur_thread); if (cur_thread->GetState() == KThread::ThreadState_Runnable) { cur_thread->SetState(KThread::ThreadState_Waiting); + } else { + KScheduler::SetSchedulerUpdateNeeded(); } } /* Remove the thread as a waiter from the lock owner. */ @@ -739,6 +750,7 @@ namespace ams::kern { KThread *owner_thread = cur_thread->GetLockOwner(); if (owner_thread != nullptr) { owner_thread->RemoveWaiter(cur_thread); + KScheduler::SetSchedulerUpdateNeeded(); } } } @@ -763,6 +775,8 @@ namespace ams::kern { if (next != nullptr) { if (next->GetState() == KThread::ThreadState_Waiting) { next->SetState(KThread::ThreadState_Runnable); + } else { + KScheduler::SetSchedulerUpdateNeeded(); } } @@ -974,6 +988,36 @@ namespace ams::kern { return ResultSuccess(); } + void KProcess::PinCurrentThread() { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + + /* Get the current thread. */ + const s32 core_id = GetCurrentCoreId(); + KThread *cur_thread = GetCurrentThreadPointer(); + + /* Pin it. */ + this->PinThread(core_id, cur_thread); + cur_thread->Pin(); + + /* An update is needed. */ + KScheduler::SetSchedulerUpdateNeeded(); + } + + void KProcess::UnpinCurrentThread() { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + + /* Get the current thread. */ + const s32 core_id = GetCurrentCoreId(); + KThread *cur_thread = GetCurrentThreadPointer(); + + /* Unpin it. */ + cur_thread->Unpin(); + this->UnpinThread(core_id, cur_thread); + + /* An update is needed. */ + KScheduler::SetSchedulerUpdateNeeded(); + } + Result KProcess::GetThreadList(s32 *out_num_threads, ams::kern::svc::KUserPointer out_thread_ids, s32 max_out_count) { /* Lock the list. */ KScopedLightLock lk(this->list_lock); diff --git a/libraries/libmesosphere/source/kern_k_resource_limit.cpp b/libraries/libmesosphere/source/kern_k_resource_limit.cpp index 7261919be..5ef2e04b5 100644 --- a/libraries/libmesosphere/source/kern_k_resource_limit.cpp +++ b/libraries/libmesosphere/source/kern_k_resource_limit.cpp @@ -31,7 +31,7 @@ namespace ams::kern { for (size_t i = 0; i < util::size(this->limit_values); i++) { this->limit_values[i] = 0; this->current_values[i] = 0; - this->current_hints[i] = 0; + this->current_hints[i] = 0; } */ } @@ -123,7 +123,7 @@ namespace ams::kern { if (this->current_values[which] + value <= this->limit_values[which]) { this->current_values[which] += value; - this->current_hints[which] += value; + this->current_hints[which] += value; return true; } diff --git a/libraries/libmesosphere/source/kern_k_scheduler.cpp b/libraries/libmesosphere/source/kern_k_scheduler.cpp index ee1869557..0a7287129 100644 --- a/libraries/libmesosphere/source/kern_k_scheduler.cpp +++ b/libraries/libmesosphere/source/kern_k_scheduler.cpp @@ -28,6 +28,7 @@ namespace ams::kern { constexpr KSchedulerInterruptTask() : KInterruptTask() { /* ... */ } virtual KInterruptTask *OnInterrupt(s32 interrupt_id) override { + MESOSPHERE_UNUSED(interrupt_id); return GetDummyInterruptTask(); } @@ -98,6 +99,8 @@ namespace ams::kern { } } + MESOSPHERE_KTRACE_SCHEDULE_UPDATE(this->core_id, (prev_highest_thread != nullptr ? prev_highest_thread : this->idle_thread), (highest_thread != nullptr ? highest_thread : this->idle_thread)); + this->state.highest_priority_thread = highest_thread; this->state.needs_scheduling = true; return (1ul << this->core_id); @@ -162,6 +165,7 @@ namespace ams::kern { /* The suggested thread isn't bound to its core, so we can migrate it! */ suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested); + MESOSPHERE_KTRACE_CORE_MIGRATION(suggested->GetId(), suggested_core, core_id, 1); top_threads[core_id] = suggested; cores_needing_scheduling |= Kernel::GetScheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]); break; @@ -187,6 +191,7 @@ namespace ams::kern { /* Perform the migration. */ suggested->SetActiveCore(core_id); priority_queue.ChangeCore(candidate_core, suggested); + MESOSPHERE_KTRACE_CORE_MIGRATION(suggested->GetId(), candidate_core, core_id, 2); top_threads[core_id] = suggested; cores_needing_scheduling |= Kernel::GetScheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]); break; @@ -252,6 +257,8 @@ namespace ams::kern { this->prev_thread = nullptr; } + MESOSPHERE_KTRACE_THREAD_SWITCH(next_thread); + /* Switch the current process, if we're switching processes. */ if (KProcess *next_process = next_thread->GetOwnerProcess(); next_process != cur_process) { /* MESOSPHERE_LOG("!!! PROCESS SWITCH !!! %s -> %s\n", cur_process != nullptr ? cur_process->GetName() : nullptr, next_process != nullptr ? next_process->GetName() : nullptr); */ @@ -269,43 +276,16 @@ namespace ams::kern { void KScheduler::ClearPreviousThread(KThread *thread) { MESOSPHERE_ASSERT(IsSchedulerLockedByCurrentThread()); for (size_t i = 0; i < cpu::NumCores; ++i) { - std::atomic *prev_thread_ptr = reinterpret_cast *>(std::addressof(Kernel::GetScheduler(static_cast(i)).prev_thread)); - static_assert(sizeof(*prev_thread_ptr) == sizeof(KThread *)); + /* Get an atomic reference to the core scheduler's previous thread. */ + std::atomic_ref prev_thread(Kernel::GetScheduler(static_cast(i)).prev_thread); + static_assert(std::atomic_ref::is_always_lock_free); - prev_thread_ptr->compare_exchange_weak(thread, nullptr); + /* Atomically clear the previous thread if it's our target. */ + KThread *compare = thread; + prev_thread.compare_exchange_strong(compare, nullptr); } } - void KScheduler::PinCurrentThread(KProcess *cur_process) { - MESOSPHERE_ASSERT(IsSchedulerLockedByCurrentThread()); - - /* Get the current thread. */ - const s32 core_id = GetCurrentCoreId(); - KThread *cur_thread = GetCurrentThreadPointer(); - - /* Pin it. */ - cur_process->PinThread(core_id, cur_thread); - cur_thread->Pin(); - - /* An update is needed. */ - SetSchedulerUpdateNeeded(); - } - - void KScheduler::UnpinCurrentThread(KProcess *cur_process) { - MESOSPHERE_ASSERT(IsSchedulerLockedByCurrentThread()); - - /* Get the current thread. */ - const s32 core_id = GetCurrentCoreId(); - KThread *cur_thread = GetCurrentThreadPointer(); - - /* Unpin it. */ - cur_thread->Unpin(); - cur_process->UnpinThread(core_id, cur_thread); - - /* An update is needed. */ - SetSchedulerUpdateNeeded(); - } - void KScheduler::OnThreadStateChanged(KThread *thread, KThread::ThreadState old_state) { MESOSPHERE_ASSERT(IsSchedulerLockedByCurrentThread()); @@ -525,6 +505,7 @@ namespace ams::kern { if (running_on_suggested_core == nullptr || running_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) { suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested, true); + MESOSPHERE_KTRACE_CORE_MIGRATION(suggested->GetId(), suggested_core, core_id, 3); IncrementScheduledCount(suggested); break; } else { @@ -578,6 +559,7 @@ namespace ams::kern { /* Migrate the current thread to core -1. */ cur_thread.SetActiveCore(-1); priority_queue.ChangeCore(core_id, std::addressof(cur_thread)); + MESOSPHERE_KTRACE_CORE_MIGRATION(cur_thread.GetId(), core_id, -1, 4); IncrementScheduledCount(std::addressof(cur_thread)); /* If there's nothing scheduled, we can try to perform a migration. */ @@ -592,6 +574,7 @@ namespace ams::kern { if (top_on_suggested_core == nullptr || top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) { suggested->SetActiveCore(core_id); priority_queue.ChangeCore(suggested_core, suggested); + MESOSPHERE_KTRACE_CORE_MIGRATION(suggested->GetId(), suggested_core, core_id, 5); IncrementScheduledCount(suggested); } diff --git a/libraries/libmesosphere/source/kern_k_scoped_disable_dispatch.cpp b/libraries/libmesosphere/source/kern_k_scoped_disable_dispatch.cpp new file mode 100644 index 000000000..413390577 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_scoped_disable_dispatch.cpp @@ -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 . + */ +#include + +namespace ams::kern { + + KScopedDisableDispatch::~KScopedDisableDispatch() { + if (GetCurrentThread().GetDisableDispatchCount() <= 1) { + Kernel::GetScheduler().RescheduleCurrentCore(); + } else { + GetCurrentThread().EnableDispatch(); + } + } + +} diff --git a/libraries/libmesosphere/source/kern_k_server_port.cpp b/libraries/libmesosphere/source/kern_k_server_port.cpp index 965f7972a..da8482577 100644 --- a/libraries/libmesosphere/source/kern_k_server_port.cpp +++ b/libraries/libmesosphere/source/kern_k_server_port.cpp @@ -28,8 +28,11 @@ namespace ams::kern { void KServerPort::CleanupSessions() { /* Ensure our preconditions are met. */ - MESOSPHERE_ASSERT(this->IsLight() || this->session_list.empty()); - MESOSPHERE_ASSERT(!this->IsLight() || this->light_session_list.empty()); + if (this->IsLight()) { + MESOSPHERE_ASSERT(this->session_list.empty()); + } else { + MESOSPHERE_ASSERT(this->light_session_list.empty()); + } /* Cleanup the session list. */ while (true) { diff --git a/libraries/libmesosphere/source/kern_k_server_session.cpp b/libraries/libmesosphere/source/kern_k_server_session.cpp index 36159014c..e9da0fc6e 100644 --- a/libraries/libmesosphere/source/kern_k_server_session.cpp +++ b/libraries/libmesosphere/source/kern_k_server_session.cpp @@ -53,6 +53,10 @@ namespace ams::kern { this->msg_buffer_end = dst_address + sizeof(u32) * out_offset; this->msg_buffer_space_end = dst_address + msg_size; + /* NOTE: Nintendo calculates the receive list index here using the special header. */ + /* We pre-calculate it in the caller, and pass it as a parameter. */ + MESOSPHERE_UNUSED(dst_special_header); + const u32 *recv_list = dst_msg + dst_recv_list_idx; const auto entry_count = GetEntryCount(dst_header); @@ -494,6 +498,9 @@ namespace ams::kern { auto &dst_page_table = dst_process.GetPageTable(); auto &src_page_table = src_process.GetPageTable(); + /* NOTE: Session is used only for debugging, and so may go unused. */ + MESOSPHERE_UNUSED(session); + /* The receive list is initially not broken. */ recv_list_broken = false; @@ -711,7 +718,7 @@ namespace ams::kern { return ResultSuccess(); } - ALWAYS_INLINE Result ProcessSendMessagePointerDescriptors(int &offset, int &pointer_key, KProcessPageTable &dst_page_table, KProcessPageTable &src_page_table, const ipc::MessageBuffer &dst_msg, const ipc::MessageBuffer &src_msg, const ReceiveList &dst_recv_list, bool dst_user) { + ALWAYS_INLINE Result ProcessSendMessagePointerDescriptors(int &offset, int &pointer_key, KProcessPageTable &dst_page_table, const ipc::MessageBuffer &dst_msg, const ipc::MessageBuffer &src_msg, const ReceiveList &dst_recv_list, bool dst_user) { /* Get the offset at the start of processing. */ const int cur_offset = offset; @@ -758,6 +765,9 @@ namespace ams::kern { auto &dst_page_table = dst_process.GetPageTable(); auto &src_page_table = src_process.GetPageTable(); + /* NOTE: Session is used only for debugging, and so may go unused. */ + MESOSPHERE_UNUSED(session); + /* Determine the message buffers. */ u32 *dst_msg_ptr, *src_msg_ptr; bool dst_user, src_user; @@ -860,7 +870,7 @@ namespace ams::kern { /* Process any pointer buffers. */ for (auto i = 0; i < src_header.GetPointerCount(); ++i) { - R_TRY(ProcessSendMessagePointerDescriptors(offset, pointer_key, dst_page_table, src_page_table, dst_msg, src_msg, dst_recv_list, dst_user && dst_header.GetReceiveListCount() == ipc::MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer)); + R_TRY(ProcessSendMessagePointerDescriptors(offset, pointer_key, dst_page_table, dst_msg, src_msg, dst_recv_list, dst_user && dst_header.GetReceiveListCount() == ipc::MessageBuffer::MessageHeader::ReceiveListCountType_ToMessageBuffer)); } /* Clear any map alias buffers. */ diff --git a/libraries/libmesosphere/source/kern_k_shared_memory.cpp b/libraries/libmesosphere/source/kern_k_shared_memory.cpp index 641e8c4f6..7756fef52 100644 --- a/libraries/libmesosphere/source/kern_k_shared_memory.cpp +++ b/libraries/libmesosphere/source/kern_k_shared_memory.cpp @@ -99,6 +99,7 @@ namespace ams::kern { Result KSharedMemory::Unmap(KProcessPageTable *table, KProcessAddress address, size_t size, KProcess *process) { MESOSPHERE_ASSERT_THIS(); + MESOSPHERE_UNUSED(process); /* Validate the size. */ R_UNLESS(this->page_group.GetNumPages() == util::DivideUp(size, PageSize), svc::ResultInvalidSize()); diff --git a/libraries/libmesosphere/source/kern_k_thread.cpp b/libraries/libmesosphere/source/kern_k_thread.cpp index ae84126fa..eb9fc571b 100644 --- a/libraries/libmesosphere/source/kern_k_thread.cpp +++ b/libraries/libmesosphere/source/kern_k_thread.cpp @@ -328,7 +328,7 @@ namespace ams::kern { if (this->parent != nullptr) { this->parent->ReleaseUserException(this); if (this->parent->GetPinnedThread(GetCurrentCoreId()) == this) { - KScheduler::UnpinCurrentThread(this->parent); + this->parent->UnpinCurrentThread(); } } @@ -1099,13 +1099,7 @@ namespace ams::kern { const bool first_request = [&] ALWAYS_INLINE_LAMBDA () -> bool { /* Perform an atomic compare-and-swap from false to true. */ bool expected = false; - do { - if (expected) { - return false; - } - } while (!this->termination_requested.compare_exchange_weak(expected, true)); - - return true; + return this->termination_requested.compare_exchange_strong(expected, true); }(); /* If this is the first request, start termination procedure. */ @@ -1133,6 +1127,7 @@ namespace ams::kern { /* If the thread is runnable, send a termination interrupt to other cores. */ if (this->GetState() == ThreadState_Runnable) { if (const u64 core_mask = this->affinity_mask.GetAffinityMask() & ~(1ul << GetCurrentCoreId()); core_mask != 0) { + cpu::DataSynchronizationBarrier(); Kernel::GetInterruptManager().SendInterProcessorInterrupt(KInterruptName_ThreadTerminate, core_mask); } } diff --git a/libraries/libmesosphere/source/kern_k_trace.cpp b/libraries/libmesosphere/source/kern_k_trace.cpp new file mode 100644 index 000000000..5cba5d080 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_trace.cpp @@ -0,0 +1,150 @@ +/* + * 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 . + */ +#include + +namespace ams::kern { + + /* Static initializations. */ + constinit bool KTrace::s_is_active = false; + + namespace { + + constinit KSpinLock g_ktrace_lock; + constinit KVirtualAddress g_ktrace_buffer_address = Null; + constinit size_t g_ktrace_buffer_size = 0; + constinit u64 g_type_filter = 0; + + struct KTraceHeader { + u32 magic; + u32 offset; + u32 index; + u32 count; + + static constexpr u32 Magic = util::FourCC<'K','T','R','0'>::Code; + }; + static_assert(util::is_pod::value); + + struct KTraceRecord { + u8 core_id; + u8 type; + u16 process_id; + u32 thread_id; + u64 tick; + u64 data[6]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(KTraceRecord) == 0x40); + + ALWAYS_INLINE bool IsTypeFiltered(u8 type) { + return (g_type_filter & (UINT64_C(1) << (type & (BITSIZEOF(u64) - 1)))) != 0; + } + + } + + void KTrace::Initialize(KVirtualAddress address, size_t size) { + /* Only perform tracing when on development hardware. */ + if (KTargetSystem::IsDebugMode()) { + const size_t offset = util::AlignUp(sizeof(KTraceHeader), sizeof(KTraceRecord)); + if (offset < size) { + /* Clear the trace buffer. */ + std::memset(GetVoidPointer(address), 0, size); + + /* Initialize the KTrace header. */ + KTraceHeader *header = GetPointer(address); + header->magic = KTraceHeader::Magic; + header->offset = offset; + header->index = 0; + header->count = (size - offset) / sizeof(KTraceRecord); + + /* Set the global data. */ + g_ktrace_buffer_address = address; + g_ktrace_buffer_size = size; + + /* Set the filters to defaults. */ + g_type_filter = ~(UINT64_C(0)); + } + } + } + + void KTrace::Start() { + if (g_ktrace_buffer_address != Null) { + /* Get exclusive access to the trace buffer. */ + KScopedInterruptDisable di; + KScopedSpinLock lk(g_ktrace_lock); + + /* Reset the header. */ + KTraceHeader *header = GetPointer(g_ktrace_buffer_address); + header->index = 0; + + /* Reset the records. */ + KTraceRecord *records = GetPointer(g_ktrace_buffer_address + header->offset); + std::memset(records, 0, sizeof(*records) * header->count); + + /* Note that we're active. */ + s_is_active = true; + } + } + + void KTrace::Stop() { + if (g_ktrace_buffer_address != Null) { + /* Get exclusive access to the trace buffer. */ + KScopedInterruptDisable di; + KScopedSpinLock lk(g_ktrace_lock); + + /* Note that we're paused. */ + s_is_active = false; + } + } + + void KTrace::PushRecord(u8 type, u64 param0, u64 param1, u64 param2, u64 param3, u64 param4, u64 param5) { + /* Get exclusive access to the trace buffer. */ + KScopedInterruptDisable di; + KScopedSpinLock lk(g_ktrace_lock); + + /* Check whether we should push the record to the trace buffer. */ + if (s_is_active && IsTypeFiltered(type)) { + /* Get the current thread and process. */ + KThread &cur_thread = GetCurrentThread(); + KProcess *cur_process = GetCurrentProcessPointer(); + + /* Get the current record index from the header. */ + KTraceHeader *header = GetPointer(g_ktrace_buffer_address); + u32 index = header->index; + + /* Get the current record. */ + KTraceRecord *record = GetPointer(g_ktrace_buffer_address + header->offset + index * sizeof(KTraceRecord)); + + /* Set the record's data. */ + *record = { + .core_id = static_cast(GetCurrentCoreId()), + .type = type, + .process_id = static_cast(cur_process != nullptr ? cur_process->GetId() : ~0), + .thread_id = static_cast(cur_thread.GetId()), + .tick = static_cast(KHardwareTimer::GetTick()), + .data = { param0, param1, param2, param3, param4, param5 }, + }; + + /* Advance the current index. */ + if ((++index) >= header->count) { + index = 0; + } + + /* Set the next index. */ + header->index = index; + } + } + +} diff --git a/libraries/libmesosphere/source/kern_kernel.cpp b/libraries/libmesosphere/source/kern_kernel.cpp index f9aa6f747..750cc2a2f 100644 --- a/libraries/libmesosphere/source/kern_kernel.cpp +++ b/libraries/libmesosphere/source/kern_kernel.cpp @@ -139,7 +139,7 @@ namespace ams::kern { PrintMemoryRegion(" Misc", KMemoryLayout::GetKernelMiscRegionExtents()); PrintMemoryRegion(" Slab", KMemoryLayout::GetKernelSlabRegionExtents()); PrintMemoryRegion(" CoreLocalRegion", KMemoryLayout::GetCoreLocalRegion()); - PrintMemoryRegion(" LinearRegion", KMemoryLayout::GetLinearRegionExtents()); + PrintMemoryRegion(" LinearRegion", KMemoryLayout::GetLinearRegionVirtualExtents()); MESOSPHERE_LOG("\n"); MESOSPHERE_LOG("Physical Memory Layout\n"); @@ -152,11 +152,21 @@ namespace ams::kern { PrintMemoryRegion(" PageTableHeap", KMemoryLayout::GetKernelPageTableHeapRegionPhysicalExtents()); PrintMemoryRegion(" InitPageTable", KMemoryLayout::GetKernelInitPageTableRegionPhysicalExtents()); PrintMemoryRegion(" MemoryPoolRegion", KMemoryLayout::GetKernelPoolPartitionRegionPhysicalExtents()); - PrintMemoryRegion(" System", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents()); - PrintMemoryRegion(" Internal", KMemoryLayout::GetKernelMetadataPoolRegionPhysicalExtents()); - PrintMemoryRegion(" SystemUnsafe", KMemoryLayout::GetKernelSystemNonSecurePoolRegionPhysicalExtents()); - PrintMemoryRegion(" Applet", KMemoryLayout::GetKernelAppletPoolRegionPhysicalExtents()); - PrintMemoryRegion(" Application", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents()); + if (GetTargetFirmware() >= TargetFirmware_5_0_0) { + PrintMemoryRegion(" System", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents()); + PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents()); + PrintMemoryRegion(" SystemUnsafe", KMemoryLayout::GetKernelSystemNonSecurePoolRegionPhysicalExtents()); + PrintMemoryRegion(" Applet", KMemoryLayout::GetKernelAppletPoolRegionPhysicalExtents()); + PrintMemoryRegion(" Application", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents()); + } else { + PrintMemoryRegion(" Secure", KMemoryLayout::GetKernelSystemPoolRegionPhysicalExtents()); + PrintMemoryRegion(" Management", KMemoryLayout::GetKernelPoolManagementRegionPhysicalExtents()); + PrintMemoryRegion(" Unsafe", KMemoryLayout::GetKernelApplicationPoolRegionPhysicalExtents()); + } + if constexpr (IsKTraceEnabled) { + MESOSPHERE_LOG(" Debug\n"); + PrintMemoryRegion(" Trace Buffer", KMemoryLayout::GetKernelTraceBufferRegionPhysicalExtents()); + } MESOSPHERE_LOG("\n"); } diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp index 063e6b301..e481b8f6b 100644 --- a/libraries/libmesosphere/source/kern_main.cpp +++ b/libraries/libmesosphere/source/kern_main.cpp @@ -51,8 +51,8 @@ namespace ams::kern { /* Initialize the memory manager and the KPageBuffer slabheap. */ { - const auto &metadata_region = KMemoryLayout::GetMetadataPoolRegion(); - Kernel::GetMemoryManager().Initialize(metadata_region.GetAddress(), metadata_region.GetSize()); + const auto &management_region = KMemoryLayout::GetPoolManagementRegion(); + Kernel::GetMemoryManager().Initialize(management_region.GetAddress(), management_region.GetSize()); init::InitializeKPageBufferSlabHeap(); } @@ -139,7 +139,7 @@ namespace ams::kern { /* Main() is done, and we should never get to this point. */ MESOSPHERE_PANIC("Main Thread continued after exit."); - while (true) { /* ... */ } + AMS_INFINITE_LOOP(); } } diff --git a/libraries/libmesosphere/source/kern_panic.cpp b/libraries/libmesosphere/source/kern_panic.cpp index fc4322e7d..1dbbc54c9 100644 --- a/libraries/libmesosphere/source/kern_panic.cpp +++ b/libraries/libmesosphere/source/kern_panic.cpp @@ -27,6 +27,11 @@ namespace ams::result::impl { namespace ams::kern { + /* NOTE: This is not exposed via a header, but is referenced via assembly. */ + /* NOTE: Nintendo does not save register contents on panic; we use this */ + /* to generate an atmosphere fatal report on panic. */ + constinit KExceptionContext g_panic_exception_contexts[cpu::NumCores]; + namespace { constexpr std::array NegativeArray = [] { @@ -73,18 +78,38 @@ namespace ams::kern { } while (!g_current_ticket.compare_exchange_weak(compare, desired)); } + ALWAYS_INLINE KExceptionContext *GetPanicExceptionContext(int core_id) { + #if defined(MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP) + return std::addressof(g_panic_exception_contexts[core_id]); + #else + return nullptr; + #endif + } + [[gnu::unused]] void PrintCurrentState() { /* Wait for it to be our turn to print. */ WaitCoreTicket(); - const s32 core_id = GetCurrentCoreId(); + /* Get the current exception context. */ + const s32 core_id = GetCurrentCoreId(); + const auto *core_ctx = GetPanicExceptionContext(core_id); + + /* Print the state. */ MESOSPHERE_RELEASE_LOG("Core[%d] Current State:\n", core_id); - /* TODO: Dump register state. */ - #ifdef ATMOSPHERE_ARCH_ARM64 + /* Print registers. */ + if (core_ctx != nullptr) { + MESOSPHERE_RELEASE_LOG(" Registers:\n"); + for (size_t i = 0; i < util::size(core_ctx->x); ++i) { + MESOSPHERE_RELEASE_LOG(" X[%02zx]: %p\n", i, reinterpret_cast(core_ctx->x[i])); + } + MESOSPHERE_RELEASE_LOG(" SP: %p\n", reinterpret_cast(core_ctx->x[30])); + } + + /* Print backtrace. */ MESOSPHERE_RELEASE_LOG(" Backtrace:\n"); - uintptr_t fp = reinterpret_cast(__builtin_frame_address(0)); + uintptr_t fp = core_ctx != nullptr ? core_ctx->x[29] : reinterpret_cast(__builtin_frame_address(0)); for (size_t i = 0; i < 32 && fp && util::IsAligned(fp, 0x10) && cpu::GetPhysicalAddressWritable(nullptr, fp, true); i++) { struct { uintptr_t fp; @@ -107,12 +132,12 @@ namespace ams::kern { PrintCurrentState(); #endif - KSystemControl::StopSystem(); + KSystemControl::StopSystem(GetPanicExceptionContext(GetCurrentCoreId())); } } - NORETURN WEAK_SYMBOL void Panic(const char *file, int line, const char *format, ...) { + NORETURN void PanicImpl(const char *file, int line, const char *format, ...) { #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING /* Wait for it to be our turn to print. */ WaitCoreTicket(); @@ -126,12 +151,14 @@ namespace ams::kern { MESOSPHERE_RELEASE_VLOG(format, vl); MESOSPHERE_RELEASE_LOG("\n"); va_end(vl); + #else + MESOSPHERE_UNUSED(file, line, format); #endif StopSystem(); } - NORETURN WEAK_SYMBOL void Panic() { + NORETURN void PanicImpl() { StopSystem(); } diff --git a/libraries/libmesosphere/source/svc/kern_svc_address_translation.cpp b/libraries/libmesosphere/source/svc/kern_svc_address_translation.cpp index 93b43565c..80e161171 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_address_translation.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_address_translation.cpp @@ -80,21 +80,14 @@ namespace ams::kern::svc { R_UNLESS(phys_addr < PageSize, svc::ResultNotFound()); /* Try to find the memory region. */ - const KMemoryRegion *region; - switch (static_cast(phys_addr)) { - case ams::svc::MemoryRegionType_KernelTraceBuffer: - region = KMemoryLayout::TryGetKernelTraceBufferRegion(); - break; - case ams::svc::MemoryRegionType_OnMemoryBootImage: - region = KMemoryLayout::TryGetOnMemoryBootImageRegion(); - break; - case ams::svc::MemoryRegionType_DTB: - region = KMemoryLayout::TryGetDTBRegion(); - break; - default: - region = nullptr; - break; - } + const KMemoryRegion * const region = [] ALWAYS_INLINE_LAMBDA (ams::svc::MemoryRegionType type) -> const KMemoryRegion * { + switch (type) { + case ams::svc::MemoryRegionType_KernelTraceBuffer: return KMemoryLayout::GetPhysicalKernelTraceBufferRegion(); + case ams::svc::MemoryRegionType_OnMemoryBootImage: return KMemoryLayout::GetPhysicalOnMemoryBootImageRegion(); + case ams::svc::MemoryRegionType_DTB: return KMemoryLayout::GetPhysicalDTBRegion(); + default: return nullptr; + } + }(static_cast(phys_addr)); /* Ensure that we found the region. */ R_UNLESS(region != nullptr, svc::ResultNotFound()); diff --git a/libraries/libmesosphere/source/svc/kern_svc_code_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_code_memory.cpp index f217950da..2ab4b878c 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_code_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_code_memory.cpp @@ -110,7 +110,7 @@ namespace ams::kern::svc { case ams::svc::CodeMemoryOperation_MapToOwner: { /* Check that the region is in range. */ - R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_GeneratedCode), svc::ResultInvalidMemoryRegion()); + R_UNLESS(code_mem->GetOwner()->GetPageTable().CanContain(address, size, KMemoryState_GeneratedCode), svc::ResultInvalidMemoryRegion()); /* Check the memory permission. */ R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission()); @@ -122,7 +122,7 @@ namespace ams::kern::svc { case ams::svc::CodeMemoryOperation_UnmapFromOwner: { /* Check that the region is in range. */ - R_UNLESS(GetCurrentProcess().GetPageTable().CanContain(address, size, KMemoryState_GeneratedCode), svc::ResultInvalidMemoryRegion()); + R_UNLESS(code_mem->GetOwner()->GetPageTable().CanContain(address, size, KMemoryState_GeneratedCode), svc::ResultInvalidMemoryRegion()); /* Check the memory permission. */ R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), svc::ResultInvalidNewMemoryPermission()); diff --git a/libraries/libmesosphere/source/svc/kern_svc_debug.cpp b/libraries/libmesosphere/source/svc/kern_svc_debug.cpp index 846b749f6..2377117e0 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_debug.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_debug.cpp @@ -291,8 +291,9 @@ namespace ams::kern::svc { R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); /* Get the thread from its id. */ - KScopedAutoObject thread = KThread::GetThreadFromId(thread_id); - R_UNLESS(thread.IsNotNull(), svc::ResultInvalidThreadId()); + KThread *thread = KThread::GetThreadFromId(thread_id); + R_UNLESS(thread != nullptr, svc::ResultInvalidThreadId()); + ON_SCOPE_EXIT { thread->Close(); }; /* Get the process from the debug object. */ KScopedAutoObject process = debug->GetProcess(); diff --git a/libraries/libmesosphere/source/svc/kern_svc_info.cpp b/libraries/libmesosphere/source/svc/kern_svc_info.cpp index 15dd55a5f..42a8b4cd3 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_info.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_info.cpp @@ -235,6 +235,30 @@ namespace ams::kern::svc { *out = tick_count; } break; + case ams::svc::InfoType_MesosphereMeta: + { + /* Verify the handle is invalid. */ + R_UNLESS(handle == ams::svc::InvalidHandle, svc::ResultInvalidHandle()); + + switch (static_cast(info_subtype)) { + case ams::svc::MesosphereMetaInfo_KernelVersion: + { + /* Return the supported kernel version. */ + *out = ams::svc::SupportedKernelVersion; + } + break; + case ams::svc::MesosphereMetaInfo_IsKTraceEnabled: + { + /* Return whether the kernel supports tracing. */ + constexpr u64 KTraceValue = ams::kern::IsKTraceEnabled ? 1 : 0; + *out = KTraceValue; + } + break; + default: + return svc::ResultInvalidCombination(); + } + } + break; default: { /* For debug, log the invalid info call. */ diff --git a/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp b/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp index 8d18f46e6..177f4a50d 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp @@ -26,6 +26,10 @@ namespace ams::kern::svc { { /* TODO: Implement Kernel Debugging. */ } + #else + { + MESOSPHERE_UNUSED(kern_debug_type, arg0, arg1, arg2); + } #endif } @@ -35,18 +39,22 @@ namespace ams::kern::svc { switch (kern_trace_state) { case ams::svc::KernelTraceState_Enabled: { - /* TODO: MESOSPHERE_KTRACE_RESUME(); */ + MESOSPHERE_KTRACE_RESUME(); } break; case ams::svc::KernelTraceState_Disabled: { - /* TODO: MESOSPHERE_KTRACE_PAUSE(); */ + MESOSPHERE_KTRACE_PAUSE(); } break; default: break; } } + #else + { + MESOSPHERE_UNUSED(kern_trace_state); + } #endif } diff --git a/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp index 1f97e4aca..8ffcbd402 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_physical_memory.cpp @@ -22,8 +22,6 @@ namespace ams::kern::svc { namespace { Result SetHeapSize(uintptr_t *out_address, size_t size) { - MESOSPHERE_LOG("%s: SetHeapSize(%012zx)\n", GetCurrentProcess().GetName(), size); - /* Validate size. */ R_UNLESS(util::IsAligned(size, ams::svc::HeapSizeAlignment), svc::ResultInvalidSize()); R_UNLESS(size < ams::kern::MainMemorySize, svc::ResultInvalidSize()); diff --git a/libraries/libmesosphere/source/svc/kern_svc_process.cpp b/libraries/libmesosphere/source/svc/kern_svc_process.cpp index 5369409ee..c279b21be 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_process.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_process.cpp @@ -131,15 +131,16 @@ namespace ams::kern::svc { } /* Validate the pool partition. */ - /* TODO: 4.0.0 UseSecureMemory flag, pre-4.0.0 behavior. */ - switch (params.flags & ams::svc::CreateProcessFlag_PoolPartitionMask) { - case ams::svc::CreateProcessFlag_PoolPartitionApplication: - case ams::svc::CreateProcessFlag_PoolPartitionApplet: - case ams::svc::CreateProcessFlag_PoolPartitionSystem: - case ams::svc::CreateProcessFlag_PoolPartitionSystemNonSecure: - break; - default: - return svc::ResultInvalidEnumValue(); + if (GetTargetFirmware() >= TargetFirmware_5_0_0) { + switch (params.flags & ams::svc::CreateProcessFlag_PoolPartitionMask) { + case ams::svc::CreateProcessFlag_PoolPartitionApplication: + case ams::svc::CreateProcessFlag_PoolPartitionApplet: + case ams::svc::CreateProcessFlag_PoolPartitionSystem: + case ams::svc::CreateProcessFlag_PoolPartitionSystemNonSecure: + break; + default: + return svc::ResultInvalidEnumValue(); + } } /* Check that the code address is aligned. */ @@ -201,23 +202,29 @@ namespace ams::kern::svc { KResourceLimit *process_resource_limit = resource_limit.IsNotNull() ? resource_limit.GetPointerUnsafe() : std::addressof(Kernel::GetSystemResourceLimit()); /* Get the pool for the process. */ - /* TODO: 4.0.0 UseSecureMemory flag, pre-4.0.0 behavior. */ - KMemoryManager::Pool pool; - switch (params.flags & ams::svc::CreateProcessFlag_PoolPartitionMask) { - case ams::svc::CreateProcessFlag_PoolPartitionApplication: - pool = KMemoryManager::Pool_Application; - break; - case ams::svc::CreateProcessFlag_PoolPartitionApplet: - pool = KMemoryManager::Pool_Applet; - break; - case ams::svc::CreateProcessFlag_PoolPartitionSystem: - pool = KMemoryManager::Pool_System; - break; - case ams::svc::CreateProcessFlag_PoolPartitionSystemNonSecure: - default: - pool = KMemoryManager::Pool_SystemNonSecure; - break; - } + const auto pool = [] ALWAYS_INLINE_LAMBDA (u32 flags) -> KMemoryManager::Pool { + if (GetTargetFirmware() >= TargetFirmware_5_0_0) { + switch (flags & ams::svc::CreateProcessFlag_PoolPartitionMask) { + case ams::svc::CreateProcessFlag_PoolPartitionApplication: + return KMemoryManager::Pool_Application; + case ams::svc::CreateProcessFlag_PoolPartitionApplet: + return KMemoryManager::Pool_Applet; + case ams::svc::CreateProcessFlag_PoolPartitionSystem: + return KMemoryManager::Pool_System; + case ams::svc::CreateProcessFlag_PoolPartitionSystemNonSecure: + default: + return KMemoryManager::Pool_SystemNonSecure; + } + } else if (GetTargetFirmware() >= TargetFirmware_4_0_0) { + if ((flags & ams::svc::CreateProcessFlag_DeprecatedUseSecureMemory) != 0) { + return KMemoryManager::Pool_Secure; + } else { + return static_cast(KSystemControl::GetCreateProcessMemoryPool()); + } + } else { + return static_cast(KSystemControl::GetCreateProcessMemoryPool()); + } + }(params.flags); /* Initialize the process. */ R_TRY(process->Initialize(params, user_caps, num_caps, process_resource_limit, pool)); diff --git a/libraries/libmesosphere/source/svc/kern_svc_synchronization.cpp b/libraries/libmesosphere/source/svc/kern_svc_synchronization.cpp index 1db753eea..7c4b8c8e3 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_synchronization.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_synchronization.cpp @@ -123,7 +123,7 @@ namespace ams::kern::svc { GetCurrentThread().ClearInterruptFlag(); /* Unpin the current thread. */ - KScheduler::UnpinCurrentThread(cur_process); + cur_process->UnpinCurrentThread(); } } diff --git a/libraries/libmesosphere/source/svc/kern_svc_thread_profiler.cpp b/libraries/libmesosphere/source/svc/kern_svc_thread_profiler.cpp index fd968bd30..0f29b5668 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_thread_profiler.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_thread_profiler.cpp @@ -21,6 +21,56 @@ namespace ams::kern::svc { namespace { + Result GetLastThreadInfoImpl(ams::svc::LastThreadContext *out_context, uintptr_t *out_tls_address, uint32_t *out_flags) { + /* Disable interrupts. */ + KScopedInterruptDisable di; + + /* Get the previous thread. */ + KThread *prev_thread = Kernel::GetScheduler().GetPreviousThread(); + R_UNLESS(prev_thread != nullptr, svc::ResultNoThread()); + + /* Verify the last thread was owned by the current process. */ + R_UNLESS(prev_thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultUnknownThread()); + + /* Clear the output flags. */ + *out_flags = 0; + + /* Get the thread's exception context. */ + GetExceptionContext(prev_thread)->GetSvcThreadContext(out_context); + + /* Get the tls address. */ + *out_tls_address = GetInteger(prev_thread->GetThreadLocalRegionAddress()); + + /* Set the syscall flag if appropriate. */ + if (prev_thread->IsCallingSvc()) { + *out_flags |= ams::svc::LastThreadInfoFlag_ThreadInSystemCall; + } + + return ResultSuccess(); + } + + Result SynchronizeCurrentProcessToFutureTime(int64_t ns) { + /* Get the wait object. */ + KWaitObject *wait_object = GetCurrentProcess().GetWaitObjectPointer(); + + /* Convert the timeout from nanoseconds to ticks. */ + s64 timeout; + if (ns > 0) { + u64 ticks = KHardwareTimer::GetTick(); + ticks += ams::svc::Tick(TimeSpan::FromNanoSeconds(ns)); + ticks += 2; + + timeout = ticks; + } else { + timeout = ns; + } + + /* Synchronize to the desired time. */ + R_TRY(wait_object->Synchronize(timeout)); + + return ResultSuccess(); + } + Result GetDebugFutureThreadInfo(ams::svc::LastThreadContext *out_context, uint64_t *out_thread_id, ams::svc::Handle debug_handle, int64_t ns) { /* Only allow invoking the svc on development hardware. */ R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNoThread()); @@ -30,25 +80,7 @@ namespace ams::kern::svc { R_UNLESS(debug.IsNotNull(), svc::ResultInvalidHandle()); /* Synchronize the current process to the desired time. */ - { - /* Get the wait object. */ - KWaitObject *wait_object = GetCurrentProcess().GetWaitObjectPointer(); - - /* Convert the timeout from nanoseconds to ticks. */ - s64 timeout; - if (ns > 0) { - u64 ticks = KHardwareTimer::GetTick(); - ticks += ams::svc::Tick(TimeSpan::FromNanoSeconds(ns)); - ticks += 2; - - timeout = ticks; - } else { - timeout = ns; - } - - /* Synchronize to the desired time. */ - R_TRY(wait_object->Synchronize(timeout)); - } + R_TRY(SynchronizeCurrentProcessToFutureTime(ns)); /* Get the running thread info. */ R_TRY(debug->GetRunningThreadInfo(out_context, out_thread_id)); @@ -56,35 +88,25 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result LegacyGetFutureThreadInfo(ams::svc::LastThreadContext *out_context, uintptr_t *out_tls_address, uint32_t *out_flags, int64_t ns) { + /* Only allow invoking the svc on development hardware. */ + R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNoThread()); + + /* Synchronize the current process to the desired time. */ + R_TRY(SynchronizeCurrentProcessToFutureTime(ns)); + + /* Get the thread info. */ + R_TRY(GetLastThreadInfoImpl(out_context, out_tls_address, out_flags)); + + return ResultSuccess(); + } + Result GetLastThreadInfo(ams::svc::LastThreadContext *out_context, uintptr_t *out_tls_address, uint32_t *out_flags) { /* Only allow invoking the svc on development hardware. */ R_UNLESS(KTargetSystem::IsDebugMode(), svc::ResultNoThread()); /* Get the thread info. */ - { - KScopedInterruptDisable di; - - /* Get the previous thread. */ - KThread *prev_thread = Kernel::GetScheduler().GetPreviousThread(); - R_UNLESS(prev_thread != nullptr, svc::ResultNoThread()); - - /* Verify the last thread was owned by the current process. */ - R_UNLESS(prev_thread->GetOwnerProcess() == GetCurrentProcessPointer(), svc::ResultUnknownThread()); - - /* Clear the output flags. */ - *out_flags = 0; - - /* Get the thread's exception context. */ - GetExceptionContext(prev_thread)->GetSvcThreadContext(out_context); - - /* Get the tls address. */ - *out_tls_address = GetInteger(prev_thread->GetThreadLocalRegionAddress()); - - /* Set the syscall flag if appropriate. */ - if (prev_thread->IsCallingSvc()) { - *out_flags |= ams::svc::LastThreadInfoFlag_ThreadInSystemCall; - } - } + R_TRY(GetLastThreadInfoImpl(out_context, out_tls_address, out_flags)); return ResultSuccess(); } @@ -97,6 +119,10 @@ namespace ams::kern::svc { return GetDebugFutureThreadInfo(out_context, out_thread_id, debug_handle, ns); } + Result LegacyGetFutureThreadInfo64(ams::svc::lp64::LastThreadContext *out_context, ams::svc::Address *out_tls_address, uint32_t *out_flags, int64_t ns) { + return LegacyGetFutureThreadInfo(out_context, reinterpret_cast(out_tls_address), out_flags, ns); + } + Result GetLastThreadInfo64(ams::svc::lp64::LastThreadContext *out_context, ams::svc::Address *out_tls_address, uint32_t *out_flags) { static_assert(sizeof(*out_tls_address) == sizeof(uintptr_t)); return GetLastThreadInfo(out_context, reinterpret_cast(out_tls_address), out_flags); @@ -117,6 +143,21 @@ namespace ams::kern::svc { return ResultSuccess(); } + Result LegacyGetFutureThreadInfo64From32(ams::svc::ilp32::LastThreadContext *out_context, ams::svc::Address *out_tls_address, uint32_t *out_flags, int64_t ns) { + static_assert(sizeof(*out_tls_address) == sizeof(uintptr_t)); + + ams::svc::LastThreadContext context = {}; + R_TRY(LegacyGetFutureThreadInfo(std::addressof(context), reinterpret_cast(out_tls_address), out_flags, ns)); + + *out_context = { + .fp = static_cast(context.fp), + .sp = static_cast(context.sp), + .lr = static_cast(context.lr), + .pc = static_cast(context.pc), + }; + return ResultSuccess(); + } + Result GetLastThreadInfo64From32(ams::svc::ilp32::LastThreadContext *out_context, ams::svc::Address *out_tls_address, uint32_t *out_flags) { static_assert(sizeof(*out_tls_address) == sizeof(uintptr_t)); diff --git a/libraries/libstratosphere/Makefile b/libraries/libstratosphere/Makefile index 29de3a2a6..d26ef1556 100644 --- a/libraries/libstratosphere/Makefile +++ b/libraries/libstratosphere/Makefile @@ -1,7 +1,9 @@ #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../config/common.mk +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) +include $(CURRENT_DIRECTORY)/../config/common.mk #--------------------------------------------------------------------------------- # pull in switch rules @@ -15,7 +17,7 @@ include $(DEVKITPRO)/libnx/switch_rules #--------------------------------------------------------------------------------- # options for code generation #--------------------------------------------------------------------------------- -PRECOMPILED_HEADERS := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/include/stratosphere.hpp +PRECOMPILED_HEADERS := $(CURRENT_DIRECTORY)/include/stratosphere.hpp DEFINES := $(ATMOSPHERE_DEFINES) -DATMOSPHERE_IS_STRATOSPHERE -D_GNU_SOURCE SETTINGS := $(ATMOSPHERE_SETTINGS) -O2 @@ -26,6 +28,7 @@ ASFLAGS := $(ATMOSPHERE_ASFLAGS) $(SETTINGS) LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs $(SETTINGS) -Wl,-Map,$(notdir $*.map) SOURCES += $(call ALL_SOURCE_DIRS,../libvapours/source) +SOURCES += $(call UNFILTERED_SOURCE_DIRS,source/os) LIBS := -lnx @@ -45,23 +48,9 @@ ifneq ($(BUILD),$(notdir $(CURDIR))) export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +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 @@ -79,7 +68,7 @@ endif export OFILES_BIN := $(addsuffix .o,$(BINFILES)) export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) -export GCH_FILES := $(foreach hdr,$(PRECOMPILED_HEADERS:.hpp=.gch),$(notdir $(hdr))) +export GCH_FILES := $(PRECOMPILED_HEADERS:.hpp=.hpp.gch) export OFILES := $(OFILES_BIN) $(OFILES_SRC) export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) @@ -116,12 +105,12 @@ dist: dist-src dist-bin #--------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr release lib *.bz2 include/stratosphere.hpp.gch + @rm -fr release lib *.bz2 $(GCH_FILES) #--------------------------------------------------------------------------------- else -DEPENDS := $(OFILES:.o=.d) $(GCH_FILES:.gch=.d) +DEPENDS := $(OFILES:.o=.d) $(foreach hdr,$(GCH_FILES:.hpp.gch=.d),$(notdir $(hdr))) #--------------------------------------------------------------------------------- # main targets diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index 9fffefa9f..06444bda7 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -41,14 +41,19 @@ /* At this point, just include the rest alphabetically. */ /* TODO: Figure out optimal order. */ #include +#include #include #include +#include +#include #include #include #include #include +#include #include #include +#include #include #include #include @@ -57,10 +62,14 @@ #include #include #include +#include #include +#include +#include #include #include -#include +#include +#include #include #include #include @@ -68,6 +77,7 @@ #include #include #include +#include /* Include FS last. */ #include diff --git a/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp b/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp index 54b69ac4c..48789fd11 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp @@ -65,47 +65,9 @@ namespace ams::exosphere { namespace ams { - struct FatalErrorContext : sf::LargeData, sf::PrefersMapAliasTransferMode { - static constexpr size_t MaxStackTrace = 0x20; - static constexpr size_t MaxStackDumpSize = 0x100; - static constexpr size_t ThreadLocalSize = 0x100; - static constexpr size_t NumGprs = 29; - static constexpr uintptr_t StdAbortMagicAddress = 0x8; - static constexpr u64 StdAbortMagicValue = 0xA55AF00DDEADCAFEul; - static constexpr u32 StdAbortErrorDesc = 0xFFE; - static constexpr u32 StackOverflowErrorDesc = 0xFFD; - static constexpr u32 DataAbortErrorDesc = 0x101; - static constexpr u32 Magic = util::FourCC<'A', 'F', 'E', '2'>::Code; + struct FatalErrorContext : ::ams::impl::FatalErrorContext, sf::LargeData, sf::PrefersMapAliasTransferMode {}; - u32 magic; - u32 error_desc; - u64 program_id; - union { - u64 gprs[32]; - struct { - u64 _gprs[29]; - u64 fp; - u64 lr; - u64 sp; - }; - }; - u64 pc; - u64 module_base; - u32 pstate; - u32 afsr0; - u32 afsr1; - u32 esr; - u64 far; - u64 report_identifier; /* Normally just system tick. */ - u64 stack_trace_size; - u64 stack_dump_size; - u64 stack_trace[MaxStackTrace]; - u8 stack_dump[MaxStackDumpSize]; - u8 tls[ThreadLocalSize]; - }; - - static_assert(sizeof(FatalErrorContext) == 0x450, "sizeof(FatalErrorContext)"); - static_assert(util::is_pod::value, "FatalErrorContext"); + static_assert(sizeof(FatalErrorContext) == sizeof(::ams::impl::FatalErrorContext)); #ifdef ATMOSPHERE_GIT_BRANCH NX_CONSTEXPR const char *GetGitBranch() { diff --git a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp index a58d28f44..cfb097f65 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/impl/ams_system_thread_definitions.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); @@ -87,9 +89,15 @@ namespace ams::impl { /* ro. */ AMS_DEFINE_SYSTEM_THREAD(16, ro, Main); + /* gpio. */ + AMS_DEFINE_SYSTEM_THREAD(-12, gpio, InterruptHandler); + /* bpc. */ AMS_DEFINE_SYSTEM_THREAD(4, bpc, IpcServer); + /* powctl. */ + AMS_DEFINE_SYSTEM_THREAD(9, powctl, InterruptHandler); + /* hid. */ AMS_DEFINE_SYSTEM_THREAD(-10, hid, IpcServer); diff --git a/libraries/libstratosphere/include/stratosphere/cal.hpp b/libraries/libstratosphere/include/stratosphere/cal.hpp new file mode 100644 index 000000000..49df7ae1b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cal.hpp @@ -0,0 +1,19 @@ +/* + * 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 . + */ + +#pragma once + +#include diff --git a/libraries/libstratosphere/include/stratosphere/cal/cal_battery_api.hpp b/libraries/libstratosphere/include/stratosphere/cal/cal_battery_api.hpp new file mode 100644 index 000000000..e576c95f8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/cal/cal_battery_api.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::cal { + + Result GetBatteryVersion(u8 *out); + Result GetBatteryVendor(size_t *out_vendor_size, void *dst, size_t dst_size); + +} diff --git a/libraries/libstratosphere/include/stratosphere/clkrst.hpp b/libraries/libstratosphere/include/stratosphere/clkrst.hpp new file mode 100644 index 000000000..45f3d6f0c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/clkrst.hpp @@ -0,0 +1,21 @@ +/* + * 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 . + */ + +#pragma once + +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_api.hpp b/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_api.hpp new file mode 100644 index 000000000..69f4e7cba --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::clkrst { + + void Initialize(); + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_session_api.hpp b/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_session_api.hpp new file mode 100644 index 000000000..3ca34615c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_session_api.hpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::clkrst { + + struct ClkRstSession { + void *_session; + }; + + Result OpenSession(ClkRstSession *out, DeviceCode device_code); + void CloseSession(ClkRstSession *session); + + void SetResetAsserted(ClkRstSession *session); + void SetResetDeasserted(ClkRstSession *session); + + void SetClockRate(ClkRstSession *session, u32 hz); + + void SetClockDisabled(ClkRstSession *session); + +} diff --git a/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_types.hpp b/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_types.hpp new file mode 100644 index 000000000..7c234ca14 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/clkrst/clkrst_types.hpp @@ -0,0 +1,23 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::clkrst { + + /* ... */ + +} diff --git a/libraries/libstratosphere/include/stratosphere/dd.hpp b/libraries/libstratosphere/include/stratosphere/dd.hpp index dc14d77b4..a089ed302 100644 --- a/libraries/libstratosphere/include/stratosphere/dd.hpp +++ b/libraries/libstratosphere/include/stratosphere/dd.hpp @@ -16,5 +16,7 @@ #pragma once -#include "dd/dd_io_mappings.hpp" -#include "dd/dd_process_handle.hpp" +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space.hpp b/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space.hpp new file mode 100644 index 000000000..0ae119401 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space.hpp @@ -0,0 +1,21 @@ +/* + * 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 . + */ +#pragma once +#include + +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_api.hpp b/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_api.hpp new file mode 100644 index 000000000..31d7148d8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_api.hpp @@ -0,0 +1,48 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::dd { + + Result CreateDeviceAddressSpace(DeviceAddressSpaceType *das, u64 address, u64 size); + Result CreateDeviceAddressSpace(DeviceAddressSpaceType *das, u64 size); + void DestroyDeviceAddressSpace(DeviceAddressSpaceType *das); + + void AttachDeviceAddressSpaceHandle(DeviceAddressSpaceType *das, Handle handle, bool managed); + + Handle GetDeviceAddressSpaceHandle(DeviceAddressSpaceType *das); + + Result MapDeviceAddressSpaceAligned(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address, MemoryPermission device_perm); + Result MapDeviceAddressSpaceNotAligned(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address, MemoryPermission device_perm); + void UnmapDeviceAddressSpace(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address); + + void InitializeDeviceAddressSpaceMapInfo(DeviceAddressSpaceMapInfo *info, DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address, MemoryPermission device_perm); + + Result MapNextDeviceAddressSpaceRegion(size_t *out_mapped_size, DeviceAddressSpaceMapInfo *info); + void UnmapDeviceAddressSpaceRegion(DeviceAddressSpaceMapInfo *info); + + u64 GetMappedProcessAddress(DeviceAddressSpaceMapInfo *info); + DeviceVirtualAddress GetMappedDeviceVirtualAddress(DeviceAddressSpaceMapInfo *info); + size_t GetMappedSize(DeviceAddressSpaceMapInfo *info); + + Result AttachDeviceAddressSpace(DeviceAddressSpaceType *das, DeviceName device_name); + void DetachDeviceAddressSpace(DeviceAddressSpaceType *das, DeviceName device_name); + +} diff --git a/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_common.hpp b/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_common.hpp new file mode 100644 index 000000000..13676348f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_common.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::dd { + + using DeviceName = ::ams::svc::DeviceName; + + constexpr inline u64 DeviceAddressSpaceMemoryRegionAlignment = 4_KB; + +} diff --git a/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_types.hpp b/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_types.hpp new file mode 100644 index 000000000..7de417fb7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/dd/dd_device_address_space_types.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::dd { + + using DeviceVirtualAddress = u64; + + using DeviceAddressSpaceHandle = ::Handle; + + struct DeviceAddressSpaceType { + enum State { + State_NotInitialized = 0, + State_Initialized = 1, + }; + DeviceAddressSpaceHandle device_handle; + u8 state; + bool is_handle_managed; + }; + static_assert(std::is_trivial::value); + + struct DeviceAddressSpaceMapInfo { + size_t last_mapped_size; + size_t size; + u64 process_address; + DeviceVirtualAddress device_start_address; + DeviceVirtualAddress device_end_address; + ProcessHandle process_handle; + MemoryPermission device_permission; + DeviceAddressSpaceType *device_address_space; + }; + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libstratosphere/include/stratosphere/dd/dd_io_mappings.hpp b/libraries/libstratosphere/include/stratosphere/dd/dd_io_mappings.hpp index 4cd493b28..c3f2d11b8 100644 --- a/libraries/libstratosphere/include/stratosphere/dd/dd_io_mappings.hpp +++ b/libraries/libstratosphere/include/stratosphere/dd/dd_io_mappings.hpp @@ -19,16 +19,9 @@ 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); - /* 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; } diff --git a/libraries/libstratosphere/include/stratosphere/dd/dd_process_handle.hpp b/libraries/libstratosphere/include/stratosphere/dd/dd_process_handle.hpp index 6688682de..2f06ef2eb 100644 --- a/libraries/libstratosphere/include/stratosphere/dd/dd_process_handle.hpp +++ b/libraries/libstratosphere/include/stratosphere/dd/dd_process_handle.hpp @@ -13,12 +13,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once #include +#include namespace ams::dd { - ::Handle GetCurrentProcessHandle(); + ProcessHandle GetCurrentProcessHandle(); } diff --git a/libraries/libstratosphere/include/stratosphere/dd/dd_types.hpp b/libraries/libstratosphere/include/stratosphere/dd/dd_types.hpp new file mode 100644 index 000000000..2d1587296 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/dd/dd_types.hpp @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +#pragma once +#include +#include + +namespace ams::dd { + + using ProcessHandle = ::Handle; + + enum MemoryPermission { + MemoryPermission_None = 0, + MemoryPermission_ReadOnly = (1u << 0), + MemoryPermission_WriteOnly = (1u << 1), + + MemoryPermission_ReadWrite = MemoryPermission_ReadOnly | MemoryPermission_WriteOnly, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf.hpp b/libraries/libstratosphere/include/stratosphere/ddsf.hpp new file mode 100644 index 000000000..9d1bb7fc7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf.hpp @@ -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 . + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp new file mode 100644 index 000000000..376601bbb --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry.hpp @@ -0,0 +1,110 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::ddsf { + + class IDevice; + + class DeviceCodeEntry { + NON_COPYABLE(DeviceCodeEntry); + NON_MOVEABLE(DeviceCodeEntry); + private: + ams::DeviceCode device_code = ams::InvalidDeviceCode; + IDevice *device = nullptr; + public: + constexpr DeviceCodeEntry(ams::DeviceCode dc, IDevice *dev) : device_code(dc), device(dev) { + AMS_ASSERT(dev != nullptr); + } + + constexpr ams::DeviceCode GetDeviceCode() const { + return this->device_code; + } + + constexpr IDevice &GetDevice() { + return *this->device; + } + + constexpr const IDevice &GetDevice() const { + return *this->device; + } + }; + + class DeviceCodeEntryHolder { + NON_COPYABLE(DeviceCodeEntryHolder); + NON_MOVEABLE(DeviceCodeEntryHolder); + private: + util::IntrusiveListNode list_node; + TYPED_STORAGE(DeviceCodeEntry) entry_storage; + bool is_constructed; + public: + using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&DeviceCodeEntryHolder::list_node>; + using List = typename ListTraits::ListType; + friend class util::IntrusiveList>; + public: + DeviceCodeEntryHolder() : list_node(), entry_storage(), is_constructed(false) { + /* ... */ + } + + ~DeviceCodeEntryHolder() { + if (this->IsConstructed()) { + this->Destroy(); + } + } + + void AddTo(List &list) { + list.push_back(*this); + } + + void RemoveFrom(List list) { + list.erase(list.iterator_to(*this)); + } + + bool IsLinkedToList() const { + return this->list_node.IsLinked(); + } + + DeviceCodeEntry &Construct(DeviceCode dc, IDevice *dev) { + AMS_ASSERT(!this->IsConstructed()); + DeviceCodeEntry *entry = new (GetPointer(this->entry_storage)) DeviceCodeEntry(dc, dev); + this->is_constructed = true; + return *entry; + } + + bool IsConstructed() const { + return this->is_constructed; + } + + void Destroy() { + AMS_ASSERT(this->IsConstructed()); + GetReference(this->entry_storage).~DeviceCodeEntry(); + this->is_constructed = false; + } + + DeviceCodeEntry &Get() { + AMS_ASSERT(this->IsConstructed()); + return GetReference(this->entry_storage); + } + + const DeviceCodeEntry &Get() const { + AMS_ASSERT(this->IsConstructed()); + return GetReference(this->entry_storage); + } + }; + static_assert(DeviceCodeEntryHolder::ListTraits::IsValid()); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry_manager.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry_manager.hpp new file mode 100644 index 000000000..c5b747af5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_device_code_entry_manager.hpp @@ -0,0 +1,84 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::ddsf { + + class IDevice; + + class DeviceCodeEntryManager { + private: + ams::MemoryResource *memory_resource; + ddsf::DeviceCodeEntryHolder::List entry_list; + mutable os::SdkMutex entry_list_lock; + private: + void DestroyAllEntries() { + auto it = this->entry_list.begin(); + while (it != this->entry_list.end()) { + ddsf::DeviceCodeEntryHolder *entry = std::addressof(*it); + it = this->entry_list.erase(it); + + AMS_ASSERT(entry->IsConstructed()); + if (entry->IsConstructed()) { + entry->Destroy(); + } + + this->memory_resource->Deallocate(entry, sizeof(*entry)); + } + } + public: + DeviceCodeEntryManager(ams::MemoryResource *mr) : memory_resource(mr), entry_list(), entry_list_lock() { /* ... */ } + + ~DeviceCodeEntryManager() { + this->DestroyAllEntries(); + } + + void Reset() { + std::scoped_lock lk(this->entry_list_lock); + this->DestroyAllEntries(); + } + + Result Add(DeviceCode device_code, IDevice *device); + bool Remove(DeviceCode device_code); + + Result FindDeviceCodeEntry(DeviceCodeEntry **out, DeviceCode device_code); + Result FindDeviceCodeEntry(const DeviceCodeEntry **out, DeviceCode device_code) const; + + Result FindDevice(IDevice **out, DeviceCode device_code); + Result FindDevice(const IDevice **out, DeviceCode device_code) const; + + template + int ForEachEntry(F f) { + return impl::ForEach(this->entry_list_lock, this->entry_list, [&](DeviceCodeEntryHolder &holder) -> bool { + AMS_ASSERT(holder.IsConstructed()); + return f(holder.Get()); + }); + } + + template + int ForEachEntry(F f) const { + return impl::ForEach(this->entry_list_lock, this->entry_list, [&](const DeviceCodeEntryHolder &holder) -> bool { + AMS_ASSERT(holder.IsConstructed()); + return f(holder.Get()); + }); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_event_handler_manager.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_event_handler_manager.hpp new file mode 100644 index 000000000..0226b18b7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_event_handler_manager.hpp @@ -0,0 +1,80 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::ddsf { + + class EventHandlerManager; + + class EventHandlerManager { + NON_COPYABLE(EventHandlerManager); + NON_MOVEABLE(EventHandlerManager); + private: + struct LoopControlCommandParameters; + private: + bool is_initialized; + bool is_looping; + os::SdkConditionVariable is_looping_cv; + os::WaitableManagerType waitable_manager; + os::ThreadType *loop_thread; + os::Event loop_control_event; + os::WaitableHolderType loop_control_event_holder; + LoopControlCommandParameters *loop_control_command_params; + os::Event loop_control_command_done_event; /* TODO: os::LightEvent, requires mesosphere for < 4.0.0 compat. */ + os::SdkMutex loop_control_lock; + private: + void ProcessControlCommand(LoopControlCommandParameters *params); + void ProcessControlCommandImpl(LoopControlCommandParameters *params); + public: + EventHandlerManager() + : is_initialized(false), is_looping(false), is_looping_cv(), waitable_manager(), + loop_thread(), loop_control_event(os::EventClearMode_AutoClear), loop_control_event_holder(), + loop_control_command_params(), loop_control_command_done_event(os::EventClearMode_AutoClear), + loop_control_lock() + { + this->Initialize(); + } + + ~EventHandlerManager() { + if (this->is_looping) { + AMS_ASSERT(!this->IsRunningOnLoopThread()); + this->RequestStop(); + } + if (this->is_initialized) { + this->Finalize(); + } + } + + bool IsRunningOnLoopThread() const { return this->loop_thread == os::GetCurrentThread(); } + bool IsLooping() const { return this->is_looping; } + + void Initialize(); + void Finalize(); + + void RegisterHandler(IEventHandler *handler); + void UnregisterHandler(IEventHandler *handler); + + void WaitLoopEnter(); + void WaitLoopExit(); + void RequestStop(); + + void LoopAuto(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_castable.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_castable.hpp new file mode 100644 index 000000000..b863c6152 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_castable.hpp @@ -0,0 +1,88 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::ddsf { + + #if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING) + + #define AMS_DDSF_CASTABLE_TRAITS(__CLASS__, __BASE__) \ + static_assert(std::convertible_to<__CLASS__ *, __BASE__ *>); \ + public: \ + static constexpr inline ::ams::ddsf::impl::TypeTag s_ams_ddsf_castable_type_tag{#__CLASS__, __BASE__::s_ams_ddsf_castable_type_tag}; \ + constexpr virtual const ::ams::ddsf::impl::TypeTag &GetTypeTag() const override { return s_ams_ddsf_castable_type_tag; } + + #else + + #define AMS_DDSF_CASTABLE_TRAITS(__CLASS__, __BASE__) \ + static_assert(std::convertible_to<__CLASS__ *, __BASE__ *>); \ + public: \ + static constexpr inline ::ams::ddsf::impl::TypeTag s_ams_ddsf_castable_type_tag{__BASE__::s_ams_ddsf_castable_type_tag}; \ + constexpr virtual const ::ams::ddsf::impl::TypeTag &GetTypeTag() const override { return s_ams_ddsf_castable_type_tag; } + + #endif + + class ICastable { + private: + constexpr virtual const impl::TypeTag &GetTypeTag() const = 0; + + template + constexpr ALWAYS_INLINE void AssertCastableTo() const { + AMS_ASSERT(this->IsCastableTo()); + } + public: + template + constexpr bool IsCastableTo() const { + return this->GetTypeTag().DerivesFrom(T::s_ams_ddsf_castable_type_tag); + } + + template + constexpr T &SafeCastTo() { + this->AssertCastableTo(); + return static_cast(*this); + } + + template + constexpr const T &SafeCastTo() const { + this->AssertCastableTo(); + return static_cast(*this); + } + + template + constexpr T *SafeCastToPointer() { + this->AssertCastableTo(); + return static_cast(this); + } + + template + constexpr const T *SafeCastToPointer() const { + this->AssertCastableTo(); + return static_cast(this); + } + + #if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) + + constexpr const char *GetClassName() const { + return this->GetTypeTag().GetClassName(); + } + + #endif + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_device.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_device.hpp new file mode 100644 index 000000000..78c1a8e4c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_device.hpp @@ -0,0 +1,140 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::ddsf { + + class IDriver; + + class IDevice : public ICastable { + friend Result OpenSession(IDevice *device, ISession *session, AccessMode mode); + friend void CloseSession(ISession *session); + friend class IDriver; + public: + AMS_DDSF_CASTABLE_ROOT_TRAITS(ams::ddsf::IDevice); + private: + util::IntrusiveListNode list_node; + IDriver *driver; + ISession::List session_list; + mutable os::SdkMutex session_list_lock; + bool is_exclusive_write; + public: + using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&IDevice::list_node>; + using List = typename ListTraits::ListType; + friend class util::IntrusiveList>; + private: + Result AttachSession(ISession *session) { + AMS_ASSERT(session != nullptr); + std::scoped_lock lk(this->session_list_lock); + + /* Check if we're allowed to attach the session. */ + if (this->is_exclusive_write && session->CheckExclusiveWrite()) { + for (const auto &attached : this->session_list) { + R_UNLESS(!attached.CheckAccess(AccessMode_Write), ddsf::ResultAccessModeDenied()); + } + } + + /* Attach the session. */ + this->session_list.push_back(*session); + return ResultSuccess(); + } + + void DetachSession(ISession *session) { + AMS_ASSERT(session != nullptr); + std::scoped_lock lk(this->session_list_lock); + this->session_list.erase(this->session_list.iterator_to(*session)); + } + + void AttachDriver(IDriver *drv) { + AMS_ASSERT(drv != nullptr); + AMS_ASSERT(!this->IsDriverAttached()); + this->driver = drv; + AMS_ASSERT(this->IsDriverAttached()); + } + + void DetachDriver() { + AMS_ASSERT(this->IsDriverAttached()); + this->driver = nullptr; + AMS_ASSERT(!this->IsDriverAttached()); + } + public: + IDevice(bool exclusive_write) : list_node(), driver(nullptr), session_list(), session_list_lock(), is_exclusive_write(exclusive_write) { + this->session_list.clear(); + } + protected: + ~IDevice() { + this->session_list.clear(); + } + public: + void AddTo(List &list) { + list.push_back(*this); + } + + void RemoveFrom(List list) { + list.erase(list.iterator_to(*this)); + } + + bool IsLinkedToList() const { + return this->list_node.IsLinked(); + } + + IDriver &GetDriver() { + AMS_ASSERT(this->IsDriverAttached()); + return *this->driver; + } + + const IDriver &GetDriver() const { + AMS_ASSERT(this->IsDriverAttached()); + return *this->driver; + } + + bool IsDriverAttached() const { + return this->driver != nullptr; + } + + template + Result ForEachSession(F f, bool return_on_fail) { + return impl::ForEach(this->session_list_lock, this->session_list, f, return_on_fail); + } + + template + Result ForEachSession(F f, bool return_on_fail) const { + return impl::ForEach(this->session_list_lock, this->session_list, f, return_on_fail); + } + + template + int ForEachSession(F f) { + return impl::ForEach(this->session_list_lock, this->session_list, f); + } + + template + int ForEachSession(F f) const { + return impl::ForEach(this->session_list_lock, this->session_list, f); + } + + bool HasAnyOpenSession() const { + return !this->session_list.empty(); + } + }; + static_assert(IDevice::ListTraits::IsValid()); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_driver.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_driver.hpp new file mode 100644 index 000000000..6b4838ae6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_driver.hpp @@ -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 . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::ddsf { + + class IDriver : public ICastable { + public: + AMS_DDSF_CASTABLE_ROOT_TRAITS(ams::ddsf::IDriver); + private: + util::IntrusiveListNode list_node; + IDevice::List device_list; + mutable os::SdkMutex device_list_lock; + public: + using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&IDriver::list_node>; + using List = typename ListTraits::ListType; + friend class util::IntrusiveList>; + private: + public: + IDriver() : list_node(), device_list(), device_list_lock() { + this->device_list.clear(); + } + protected: + ~IDriver() { + this->device_list.clear(); + } + public: + void AddTo(List &list) { + list.push_back(*this); + } + + void RemoveFrom(List list) { + list.erase(list.iterator_to(*this)); + } + + bool IsLinkedToList() const { + return this->list_node.IsLinked(); + } + + bool HasAnyDevice() const { + return !this->device_list.empty(); + } + + void RegisterDevice(IDevice *dev) { + AMS_ASSERT(dev != nullptr); + std::scoped_lock lk(this->device_list_lock); + dev->AttachDriver(this); + this->device_list.push_back(*dev); + } + + void UnregisterDevice(IDevice *dev) { + AMS_ASSERT(dev != nullptr); + std::scoped_lock lk(this->device_list_lock); + this->device_list.erase(this->device_list.iterator_to(*dev)); + dev->DetachDriver(); + } + + template + Result ForEachDevice(F f, bool return_on_fail) { + return impl::ForEach(this->device_list_lock, this->device_list, f, return_on_fail); + } + + template + Result ForEachDevice(F f, bool return_on_fail) const { + return impl::ForEach(this->device_list_lock, this->device_list, f, return_on_fail); + } + + template + int ForEachDevice(F f) { + return impl::ForEach(this->device_list_lock, this->device_list, f); + } + + template + int ForEachDevice(F f) const { + return impl::ForEach(this->device_list_lock, this->device_list, f); + } + }; + static_assert(IDriver::ListTraits::IsValid()); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_event_handler.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_event_handler.hpp new file mode 100644 index 000000000..4d85f1425 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_event_handler.hpp @@ -0,0 +1,92 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::ddsf { + + class EventHandlerManager; + + class IEventHandler { + NON_COPYABLE(IEventHandler); + NON_MOVEABLE(IEventHandler); + friend class EventHandlerManager; + private: + os::WaitableHolderType holder; + uintptr_t user_data; + bool is_initialized; + bool is_registered; + private: + void Link(os::WaitableManagerType *manager) { + AMS_ASSERT(this->IsInitialized()); + AMS_ASSERT(!this->IsRegistered()); + AMS_ASSERT(manager != nullptr); + os::LinkWaitableHolder(manager, std::addressof(this->holder)); + } + + void Unlink() { + AMS_ASSERT(this->IsInitialized()); + AMS_ASSERT(this->IsRegistered()); + os::UnlinkWaitableHolder(std::addressof(this->holder)); + } + + static IEventHandler &ToEventHandler(os::WaitableHolderType *holder) { + AMS_ASSERT(holder != nullptr); + auto &event_handler = *reinterpret_cast(os::GetWaitableHolderUserData(holder)); + AMS_ASSERT(event_handler.IsInitialized()); + return event_handler; + } + public: + IEventHandler() : holder(), user_data(0), is_initialized(false), is_registered(false) { /* ... */ } + + ~IEventHandler() { + if (this->IsRegistered()) { + this->Unlink(); + } + if (this->IsInitialized()) { + this->Finalize(); + } + } + + bool IsInitialized() const { return this->is_initialized; } + bool IsRegistered() const { return this->is_registered; } + + uintptr_t GetUserData() const { return this->user_data; } + void SetUserData(uintptr_t d) { this->user_data = d; } + + template + void Initialize(T *object) { + AMS_ASSERT(object != nullptr); + AMS_ASSERT(!this->IsInitialized()); + os::InitializeWaitableHolder(std::addressof(this->holder), object); + os::SetWaitableHolderUserData(std::addressof(this->holder), reinterpret_cast(this)); + this->is_initialized = true; + this->is_registered = false; + } + + void Finalize() { + AMS_ASSERT(this->IsInitialized()); + AMS_ASSERT(!this->IsRegistered()); + os::FinalizeWaitableHolder(std::addressof(this->holder)); + this->is_initialized = false; + this->is_registered = false; + } + protected: + virtual void HandleEvent() = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_session.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_session.hpp new file mode 100644 index 000000000..4cc8b0610 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_i_session.hpp @@ -0,0 +1,100 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::ddsf { + + class ISession; + class IDevice; + + Result OpenSession(IDevice *device, ISession *session, AccessMode access_mode); + void CloseSession(ISession *session); + + class ISession : public ICastable { + friend Result OpenSession(IDevice *device, ISession *session, AccessMode mode); + friend void CloseSession(ISession *session); + public: + AMS_DDSF_CASTABLE_ROOT_TRAITS(ams::ddsf::IDevice); + private: + util::IntrusiveListNode list_node; + IDevice *device; + AccessMode access_mode; + public: + using ListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&ISession::list_node>; + using List = typename ListTraits::ListType; + friend class util::IntrusiveList>; + private: + void AttachDevice(IDevice *dev, AccessMode mode) { + AMS_ASSERT(dev != nullptr); + AMS_ASSERT(!this->IsOpen()); + this->device = dev; + this->access_mode = mode; + AMS_ASSERT(this->IsOpen()); + } + + void DetachDevice() { + AMS_ASSERT(this->IsOpen()); + this->device = nullptr; + this->access_mode = AccessMode_None; + AMS_ASSERT(!this->IsOpen()); + } + public: + ISession() : list_node(), device(nullptr), access_mode() { /* ... */ } + protected: + ~ISession() { this->DetachDevice(); AMS_ASSERT(!this->IsOpen()); } + public: + void AddTo(List &list) { + list.push_back(*this); + } + + void RemoveFrom(List list) { + list.erase(list.iterator_to(*this)); + } + + bool IsLinkedToList() const { + return this->list_node.IsLinked(); + } + + IDevice &GetDevice() { + AMS_ASSERT(this->IsOpen()); + return *this->device; + } + + const IDevice &GetDevice() const { + AMS_ASSERT(this->IsOpen()); + return *this->device; + } + + bool IsOpen() const { + return this->device != nullptr; + } + + bool CheckAccess(AccessMode mode) const { + AMS_ASSERT(this->IsOpen()); + return ((~this->access_mode) & mode) == 0; + } + + bool CheckExclusiveWrite() const { + return this->CheckAccess(AccessMode_Write) && !this->CheckAccess(AccessMode_Shared); + } + }; + static_assert(ISession::ListTraits::IsValid()); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_memory_api.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_memory_api.hpp new file mode 100644 index 000000000..094523ba3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_memory_api.hpp @@ -0,0 +1,31 @@ +/* + * 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 . + */ + +#pragma once +#include +#include + +namespace ams::ddsf { + + void SetMemoryResource(ams::MemoryResource *mr); + ams::MemoryResource *GetMemoryResource(); + + static constexpr size_t DeviceCodeEntryHolderSize = sizeof(DeviceCodeEntryHolder); + + void SetDeviceCodeEntryHolderMemoryResource(ams::MemoryResource *mr); + ams::MemoryResource *GetDeviceCodeEntryHolderMemoryResource(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_types.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_types.hpp new file mode 100644 index 000000000..c46f3ca8d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/ddsf_types.hpp @@ -0,0 +1,33 @@ +/* + * 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 . + */ + +#pragma once +#include + +namespace ams::ddsf { + + enum AccessMode { + AccessMode_None = (0u << 0), + AccessMode_Read = (1u << 0), + AccessMode_Write = (1u << 1), + AccessMode_Shared = (1u << 2), + + AccessMode_ReadWrite = AccessMode_Read | AccessMode_Write, + AccessMode_WriteShared = AccessMode_Write | AccessMode_Shared, + AccessMode_ReadWriteShared = AccessMode_Read | AccessMode_WriteShared, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_for_each.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_for_each.hpp new file mode 100644 index 000000000..5e62d50ff --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_for_each.hpp @@ -0,0 +1,52 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::ddsf::impl { + + template + inline Result ForEach(Lock &lock, List &list, F f, bool return_on_fail) { + std::scoped_lock lk(lock); + + Result result = ResultSuccess(); + for (auto && it : list) { + if (const auto cur_result = f(std::addressof(it)); R_FAILED(cur_result)) { + if (return_on_fail) { + return cur_result; + } else if (R_SUCCEEDED(result)) { + result = cur_result; + } + } + } + return result; + } + + template + inline int ForEach(Lock &lock, List &list, F f) { + std::scoped_lock lk(lock); + + int success_count = 0; + for (auto && it : list) { + if (!f(it)) { + return success_count; + } + ++success_count; + } + return success_count; + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_type_tag.hpp b/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_type_tag.hpp new file mode 100644 index 000000000..4d91ac04c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/ddsf/impl/ddsf_type_tag.hpp @@ -0,0 +1,70 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::ddsf::impl { + + #if defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING) + + #define AMS_DDSF_CASTABLE_ROOT_TRAITS(__CLASS__) \ + public: \ + static constexpr inline ::ams::ddsf::impl::TypeTag s_ams_ddsf_castable_type_tag{#__CLASS__}; \ + constexpr virtual const ::ams::ddsf::impl::TypeTag &GetTypeTag() const override { return s_ams_ddsf_castable_type_tag; } + + #else + + #define AMS_DDSF_CASTABLE_ROOT_TRAITS(__CLASS__) \ + public: \ + static constexpr inline ::ams::ddsf::impl::TypeTag s_ams_ddsf_castable_type_tag{}; \ + constexpr virtual const ::ams::ddsf::impl::TypeTag &GetTypeTag() const override { return s_ams_ddsf_castable_type_tag; } + + #endif + + class TypeTag { + private: + const char * const class_name; + const TypeTag * const base; + public: + #if !(defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING)) + constexpr TypeTag() : class_name(nullptr), base(nullptr) { /* ... */} + constexpr TypeTag(const TypeTag &b) : class_name(nullptr), base(std::addressof(b)) { AMS_ASSERT(this != this->base); } + + constexpr TypeTag(const char *c) : class_name(nullptr), base(nullptr) { AMS_UNUSED(c); } + constexpr TypeTag(const char *c, const TypeTag &b) : class_name(nullptr), base(std::addressof(b)) { AMS_UNUSED(c); AMS_ASSERT(this != this->base); } + #else + constexpr TypeTag(const char *c) : class_name(c), base(nullptr) { /* ... */ } + constexpr TypeTag(const char *c, const TypeTag &b) : class_name(c), base(std::addressof(b)) { AMS_ASSERT(this != this->base); } + #endif + + constexpr const char * GetClassName() const { return this->class_name; } + + constexpr bool Is(const TypeTag &rhs) const { return this == std::addressof(rhs); } + + constexpr bool DerivesFrom(const TypeTag &rhs) const { + const TypeTag * cur = this; + while (cur != nullptr) { + if (cur == std::addressof(rhs)) { + return true; + } + cur = cur->base; + } + return false; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp b/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp index 6dde7c038..4db4ba3dd 100644 --- a/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp +++ b/libraries/libstratosphere/include/stratosphere/fs/fs_code.hpp @@ -19,17 +19,17 @@ namespace ams::fs { - struct CodeInfo { + struct CodeVerificationData { u8 signature[crypto::Rsa2048PssSha256Verifier::SignatureSize]; - u8 hash[crypto::Rsa2048PssSha256Verifier::HashSize]; - bool is_signed; + u8 target_hash[crypto::Rsa2048PssSha256Verifier::HashSize]; + bool has_data; u8 reserved[3]; }; - static_assert(sizeof(CodeInfo) == crypto::Rsa2048PssSha256Verifier::SignatureSize + crypto::Rsa2048PssSha256Verifier::HashSize + 4); + static_assert(sizeof(CodeVerificationData) == crypto::Rsa2048PssSha256Verifier::SignatureSize + crypto::Rsa2048PssSha256Verifier::HashSize + 4); - Result MountCode(CodeInfo *out, const char *name, const char *path, ncm::ProgramId program_id); + Result MountCode(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id); - Result MountCodeForAtmosphereWithRedirection(CodeInfo *out, const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific); - Result MountCodeForAtmosphere(CodeInfo *out, const char *name, const char *path, ncm::ProgramId program_id); + Result MountCodeForAtmosphereWithRedirection(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific); + Result MountCodeForAtmosphere(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id); } diff --git a/libraries/libstratosphere/include/stratosphere/gpio.hpp b/libraries/libstratosphere/include/stratosphere/gpio.hpp new file mode 100644 index 000000000..c5562ee42 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo_nx/gpio_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo_nx/gpio_driver_api.hpp new file mode 100644 index 000000000..4245007bc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/board/nintendo_nx/gpio_driver_api.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::gpio::driver::board::nintendo_nx { + + void Initialize(bool enable_interrupt_handlers); + + void SetInitialGpioConfig(); + void SetInitialWakePinConfig(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_driver_client_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_driver_client_api.hpp new file mode 100644 index 000000000..54e76211e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_driver_client_api.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::gpio::driver { + + void Initialize(); + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_driver_service_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_driver_service_api.hpp new file mode 100644 index 000000000..39f62270b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_driver_service_api.hpp @@ -0,0 +1,35 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::gpio::driver { + + void RegisterDriver(IGpioDriver *driver); + void UnregisterDriver(IGpioDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, Pad *pad); + bool UnregisterDeviceCode(DeviceCode device_code); + + void RegisterInterruptHandler(ddsf::IEventHandler *handler); + void UnregisterInterruptHandler(ddsf::IEventHandler *handler); + + void SetInitialGpioConfig(); + void SetInitialWakePinConfig(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_i_gpio_driver.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_i_gpio_driver.hpp new file mode 100644 index 000000000..85daec0c8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_i_gpio_driver.hpp @@ -0,0 +1,75 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::gpio::driver { + + class Pad; + + class IGpioDriver : public ::ams::ddsf::IDriver { + NON_COPYABLE(IGpioDriver); + NON_MOVEABLE(IGpioDriver); + AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::IGpioDriver, ::ams::ddsf::IDriver); + public: + IGpioDriver() : IDriver() { /* ... */ } + virtual ~IGpioDriver() { /* ... */ } + + virtual void InitializeDriver() = 0; + virtual void FinalizeDriver() = 0; + + virtual Result InitializePad(Pad *pad) = 0; + virtual void FinalizePad(Pad *pad) = 0; + + virtual Result GetDirection(Direction *out, Pad *pad) const = 0; + virtual Result SetDirection(Pad *pad, Direction direction) = 0; + + virtual Result GetValue(GpioValue *out, Pad *pad) const = 0; + virtual Result SetValue(Pad *pad, GpioValue value) = 0; + + virtual Result GetInterruptMode(InterruptMode *out, Pad *pad) const = 0; + virtual Result SetInterruptMode(Pad *pad, InterruptMode mode) = 0; + + virtual Result SetInterruptEnabled(Pad *pad, bool en) = 0; + + virtual Result GetInterruptStatus(InterruptStatus *out, Pad *pad) = 0; + virtual Result ClearInterruptStatus(Pad *pad) = 0; + + virtual os::SdkMutex &GetInterruptControlMutex(const Pad &pad) const = 0; + + virtual Result GetDebounceEnabled(bool *out, Pad *pad) const = 0; + virtual Result SetDebounceEnabled(Pad *pad, bool en) = 0; + + virtual Result GetDebounceTime(s32 *out_ms, Pad *pad) const = 0; + virtual Result SetDebounceTime(Pad *pad, s32 ms) = 0; + + virtual Result GetUnknown22(u32 *out) = 0; + virtual void Unknown23(); + + virtual Result SetValueForSleepState(Pad *pad, GpioValue value) = 0; + virtual Result IsWakeEventActive(bool *out, Pad *pad) const = 0; + virtual Result SetWakeEventActiveFlagSetForDebug(Pad *pad, bool en) = 0; + virtual Result SetWakePinDebugMode(WakePinDebugMode mode) = 0; + + virtual Result Suspend() = 0; + virtual Result SuspendLow() = 0; + virtual Result Resume() = 0; + virtual Result ResumeLow() = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_pad.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_pad.hpp new file mode 100644 index 000000000..42258a2f1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_pad.hpp @@ -0,0 +1,61 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::gpio::driver { + + class Pad : public ::ams::ddsf::IDevice { + NON_COPYABLE(Pad); + NON_MOVEABLE(Pad); + AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::Pad, ::ams::ddsf::IDevice); + private: + int pad_number; + bool is_interrupt_enabled; + public: + explicit Pad(int pad) : IDevice(true), pad_number(pad), is_interrupt_enabled(false) { /* ... */ } + + Pad() : Pad(0) { /* ... */ } + + virtual ~Pad() { /* ... */ } + + int GetPadNumber() const { + return this->pad_number; + } + + void SetPadNumber(int p) { + this->pad_number = p; + } + + bool IsInterruptEnabled() const { + return this->is_interrupt_enabled; + } + + void SetInterruptEnabled(bool en) { + this->is_interrupt_enabled = en; + } + + bool IsInterruptRequiredForDriver() const { + return this->IsInterruptEnabled() && this->IsAnySessionBoundToInterrupt(); + } + + bool IsAnySessionBoundToInterrupt() const; + void SignalInterruptBoundEvent(); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_pad_accessor.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_pad_accessor.hpp new file mode 100644 index 000000000..e060f8a57 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_pad_accessor.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::gpio::driver { + + namespace impl { + + constexpr inline size_t GpioPadSessionSize = 0x60; + constexpr inline size_t GpioPadSessionAlign = 8; + struct alignas(GpioPadSessionAlign) GpioPadSessionImplPadded; + + } + + struct GpioPadSession { + util::TypedStorage _impl; + }; + + Result OpenSession(GpioPadSession *out, DeviceCode device_code, ddsf::AccessMode access_mode); + void CloseSession(GpioPadSession *session); + + Result SetDirection(GpioPadSession *session, gpio::Direction direction); + Result GetDirection(gpio::Direction *out, GpioPadSession *session); + + Result SetValue(GpioPadSession *session, gpio::GpioValue value); + Result GetValue(gpio::GpioValue *out, GpioPadSession *session); + + /* TODO */ + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_select_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_select_driver_api.hpp new file mode 100644 index 000000000..602571623 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/gpio_select_driver_api.hpp @@ -0,0 +1,38 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include + + namespace ams::gpio::driver::board { + + using namespace ams::gpio::driver::board::nintendo_nx; + + } + +#else + + #error "Unknown board for ams::gpio::driver::" + +#endif + diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/impl/gpio_event_holder.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/impl/gpio_event_holder.hpp new file mode 100644 index 000000000..6284bdca0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/impl/gpio_event_holder.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::gpio::driver::impl { + + class EventHolder { + NON_COPYABLE(EventHolder); + NON_MOVEABLE(EventHolder); + private: + os::SystemEventType *event; + public: + constexpr EventHolder() : event(nullptr) { /* ... */ } + + void AttachEvent(os::SystemEventType *event) { + this->event = event; + } + + os::SystemEventType *DetachEvent() { + auto ev = this->event; + this->event = nullptr; + return ev; + } + + os::SystemEventType *GetSystemEvent() { + return this->event; + } + + bool IsBound() const { + return this->event != nullptr; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/driver/impl/gpio_pad_session_impl.hpp b/libraries/libstratosphere/include/stratosphere/gpio/driver/impl/gpio_pad_session_impl.hpp new file mode 100644 index 000000000..6f37c8213 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/driver/impl/gpio_pad_session_impl.hpp @@ -0,0 +1,90 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include +#include + +namespace ams::gpio::driver { + + class Pad; + +} + +namespace ams::gpio::driver::impl { + + class PadSessionImpl : public ::ams::ddsf::ISession { + NON_COPYABLE(PadSessionImpl); + NON_MOVEABLE(PadSessionImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::impl::PadSessionImpl, ::ams::ddsf::ISession); + private: + EventHolder event_holder; + private: + Result UpdateDriverInterruptEnabled(); + public: + PadSessionImpl() : event_holder() { /* ... */ } + + ~PadSessionImpl() { + this->Close(); + } + + bool IsInterruptBound() const { + return this->event_holder.IsBound(); + } + + Result Open(Pad *pad, ddsf::AccessMode access_mode); + void Close(); + + Result BindInterrupt(os::SystemEventType *event); + void UnbindInterrupt(); + + Result GetInterruptEnabled(bool *out) const; + Result SetInterruptEnabled(bool en); + void SignalInterruptBoundEvent(); + }; + static_assert( sizeof(PadSessionImpl) <= GpioPadSessionSize); + static_assert(alignof(PadSessionImpl) <= GpioPadSessionAlign); + + struct alignas(GpioPadSessionAlign) GpioPadSessionImplPadded { + PadSessionImpl _impl; + u8 _padding[GpioPadSessionSize - sizeof(PadSessionImpl)]; + }; + static_assert( sizeof(GpioPadSessionImplPadded) == GpioPadSessionSize); + static_assert(alignof(GpioPadSessionImplPadded) == GpioPadSessionAlign); + + ALWAYS_INLINE PadSessionImpl &GetPadSessionImpl(GpioPadSession &session) { + return GetReference(session._impl)._impl; + } + + ALWAYS_INLINE const PadSessionImpl &GetPadSessionImpl(const GpioPadSession &session) { + return GetReference(session._impl)._impl; + } + + ALWAYS_INLINE PadSessionImpl &GetOpenPadSessionImpl(GpioPadSession &session) { + auto &ref = GetReference(session._impl)._impl; + AMS_ASSERT(ref.IsOpen()); + return ref; + } + + ALWAYS_INLINE const PadSessionImpl &GetOpenPadSessionImpl(const GpioPadSession &session) { + const auto &ref = GetReference(session._impl)._impl; + AMS_ASSERT(ref.IsOpen()); + return ref; + } + +} diff --git a/stratosphere/boot/source/gpio/gpio_utils.hpp b/libraries/libstratosphere/include/stratosphere/gpio/gpio_api.hpp similarity index 74% rename from stratosphere/boot/source/gpio/gpio_utils.hpp rename to libraries/libstratosphere/include/stratosphere/gpio/gpio_api.hpp index ad1418e40..a88cd2edf 100644 --- a/stratosphere/boot/source/gpio/gpio_utils.hpp +++ b/libraries/libstratosphere/include/stratosphere/gpio/gpio_api.hpp @@ -13,16 +13,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once -#include -#include +#include +#include +#include namespace ams::gpio { - /* GPIO Utilities. */ - u32 Configure(u32 gpio_pad_name); - u32 SetDirection(u32 gpio_pad_name, GpioDirection dir); - u32 SetValue(u32 gpio_pad_name, GpioValue val); + void Initialize(); + void Finalize(); + + void InitializeWith(std::shared_ptr &&sp); } diff --git a/libraries/libstratosphere/include/stratosphere/gpio/gpio_pad_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/gpio_pad_api.hpp new file mode 100644 index 000000000..600de33ed --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/gpio_pad_api.hpp @@ -0,0 +1,59 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include +#include + +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); + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/gpio_pad_name.board.nintendo_nx.hpp b/libraries/libstratosphere/include/stratosphere/gpio/gpio_pad_name.board.nintendo_nx.hpp new file mode 100644 index 000000000..98208c5f2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/gpio_pad_name.board.nintendo_nx.hpp @@ -0,0 +1,416 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::gpio { + + enum GpioPadName : u32 { + GpioPadName_CodecLdoEnTemp = 1, + GpioPadName_PowSdEn = 2, + GpioPadName_BtRst = 3, + GpioPadName_RamCode3 = 4, + GpioPadName_GameCardReset = 5, + GpioPadName_CodecAlert = 6, + GpioPadName_PowGc = 7, + GpioPadName_DebugControllerDet = 8, + GpioPadName_BattChgStatus = 9, + GpioPadName_BattChgEnableN = 10, + GpioPadName_FanTach = 11, + GpioPadName_ExtconDetS = 12, + GpioPadName_Vdd50AEn = 13, + GpioPadName_SdevCoaxSel1 = 14, + GpioPadName_GameCardCd = 15, + GpioPadName_ProdType0 = 16, + GpioPadName_ProdType1 = 17, + GpioPadName_ProdType2 = 18, + GpioPadName_ProdType3 = 19, + GpioPadName_TempAlert = 20, + GpioPadName_CodecHpDetIrq = 21, + GpioPadName_MotionInt = 22, + GpioPadName_TpIrq = 23, + GpioPadName_ButtonSleep2 = 24, + GpioPadName_ButtonVolUp = 25, + GpioPadName_ButtonVolDn = 26, + GpioPadName_BattMgicIrq = 27, + GpioPadName_RecoveryKey = 28, + GpioPadName_PowLcdBlEn = 29, + GpioPadName_LcdReset = 30, + GpioPadName_PdVconnEn = 31, + GpioPadName_PdRstN = 32, + GpioPadName_Bq24190Irq = 33, + GpioPadName_SdevCoaxSel0 = 34, + GpioPadName_SdWp = 35, + GpioPadName_TpReset = 36, + GpioPadName_BtGpio2 = 37, + GpioPadName_BtGpio3 = 38, + GpioPadName_BtGpio4 = 39, + GpioPadName_CradleIrq = 40, + GpioPadName_PowVcpuInt = 41, + GpioPadName_Max77621GpuInt = 42, + GpioPadName_ExtconChgU = 43, + GpioPadName_ExtconChgS = 44, + GpioPadName_WifiRfDisable = 45, + GpioPadName_WifiReset = 46, + GpioPadName_ApWakeBt = 47, + GpioPadName_BtWakeAp = 48, + GpioPadName_BtGpio5 = 49, + GpioPadName_PowLcdVddPEn = 50, + GpioPadName_PowLcdVddNEn = 51, + GpioPadName_ExtconDetU = 52, + GpioPadName_RamCode2 = 53, + GpioPadName_Vdd50BEn = 54, + GpioPadName_WifiWakeHost = 55, + GpioPadName_SdCd = 56, + GpioPadName_OtgFet1ForSdev = 57, + GpioPadName_OtgFet2ForSdev = 58, + GpioPadName_ExtConWakeU = 59, + GpioPadName_ExtConWakeS = 60, + GpioPadName_PmuIrq = 61, + GpioPadName_ExtUart2Cts = 62, + GpioPadName_ExtUart3Cts = 63, + GpioPadName_5VStepDownEn = 64, + GpioPadName_UsbSwitchB2Oc = 65, + GpioPadName_5VStepDownPg = 66, + GpioPadName_UsbSwitchAEn = 67, + GpioPadName_UsbSwitchAFlag = 68, + GpioPadName_UsbSwitchB3Oc = 69, + GpioPadName_UsbSwitchB3En = 70, + GpioPadName_UsbSwitchB2En = 71, + GpioPadName_Hdmi5VEn = 72, + GpioPadName_UsbSwitchB1En = 73, + GpioPadName_HdmiPdTrEn = 74, + GpioPadName_FanEn = 75, + GpioPadName_UsbSwitchB1Oc = 76, + GpioPadName_PwmFan = 77, + GpioPadName_HdmiHpd = 78, + GpioPadName_Max77812Irq = 79, + GpioPadName_Debug0 = 80, + GpioPadName_Debug1 = 81, + GpioPadName_Debug2 = 82, + GpioPadName_Debug3 = 83, + GpioPadName_NfcIrq = 84, + GpioPadName_NfcRst = 85, + GpioPadName_McuIrq = 86, + GpioPadName_McuBoot = 87, + GpioPadName_McuRst = 88, + GpioPadName_Vdd5V3En = 89, + GpioPadName_McuPor = 90, + GpioPadName_LcdGpio1 = 91, + GpioPadName_NfcEn = 92, + }; + + /* TODO: Better place for this? */ + constexpr inline const DeviceCode DeviceCode_CodecLdoEnTemp = 0x33000002; + constexpr inline const DeviceCode DeviceCode_PowSdEn = 0x3C000001; + constexpr inline const DeviceCode DeviceCode_BtRst = 0x37000002; + constexpr inline const DeviceCode DeviceCode_RamCode3 = 0xC9000402; + constexpr inline const DeviceCode DeviceCode_GameCardReset = 0x3C000402; + constexpr inline const DeviceCode DeviceCode_CodecAlert = 0x33000003; + constexpr inline const DeviceCode DeviceCode_PowGc = 0x3C000401; + constexpr inline const DeviceCode DeviceCode_DebugControllerDet = 0x350000CA; + constexpr inline const DeviceCode DeviceCode_BattChgStatus = 0x39000407; + constexpr inline const DeviceCode DeviceCode_BattChgEnableN = 0x39000003; + constexpr inline const DeviceCode DeviceCode_FanTach = 0x3D000002; + constexpr inline const DeviceCode DeviceCode_ExtconDetS = 0x3500040B; + constexpr inline const DeviceCode DeviceCode_Vdd50AEn = 0x39000401; + constexpr inline const DeviceCode DeviceCode_SdevCoaxSel1 = 0xCA000402; + constexpr inline const DeviceCode DeviceCode_GameCardCd = 0x3C000403; + constexpr inline const DeviceCode DeviceCode_ProdType0 = 0xC900040B; + constexpr inline const DeviceCode DeviceCode_ProdType1 = 0xC900040C; + constexpr inline const DeviceCode DeviceCode_ProdType2 = 0xC900040D; + constexpr inline const DeviceCode DeviceCode_ProdType3 = 0xC900040E; + constexpr inline const DeviceCode DeviceCode_TempAlert = 0x3E000002; + constexpr inline const DeviceCode DeviceCode_CodecHpDetIrq = 0x33000004; + constexpr inline const DeviceCode DeviceCode_MotionInt = 0x35000041; + constexpr inline const DeviceCode DeviceCode_TpIrq = 0x35000036; + constexpr inline const DeviceCode DeviceCode_ButtonSleep2 = 0x35000001; + constexpr inline const DeviceCode DeviceCode_ButtonVolUp = 0x35000002; + constexpr inline const DeviceCode DeviceCode_ButtonVolDn = 0x35000003; + constexpr inline const DeviceCode DeviceCode_BattMgicIrq = 0x39000034; + constexpr inline const DeviceCode DeviceCode_RecoveryKey = 0x35000004; + constexpr inline const DeviceCode DeviceCode_PowLcdBlEn = 0x3400003E; + constexpr inline const DeviceCode DeviceCode_LcdReset = 0x34000033; + constexpr inline const DeviceCode DeviceCode_PdVconnEn = 0x040000CC; + constexpr inline const DeviceCode DeviceCode_PdRstN = 0x040000CA; + constexpr inline const DeviceCode DeviceCode_Bq24190Irq = 0x39000002; + constexpr inline const DeviceCode DeviceCode_SdevCoaxSel0 = 0xCA000401; + constexpr inline const DeviceCode DeviceCode_SdWp = 0x3C000003; + constexpr inline const DeviceCode DeviceCode_TpReset = 0x35000035; + constexpr inline const DeviceCode DeviceCode_BtGpio2 = 0x37000401; + constexpr inline const DeviceCode DeviceCode_BtGpio3 = 0x37000402; + constexpr inline const DeviceCode DeviceCode_BtGpio4 = 0x37000403; + constexpr inline const DeviceCode DeviceCode_CradleIrq = 0x040000CB; + constexpr inline const DeviceCode DeviceCode_PowVcpuInt = 0x3E000003; + constexpr inline const DeviceCode DeviceCode_Max77621GpuInt = 0x3E000004; + constexpr inline const DeviceCode DeviceCode_ExtconChgU = 0x35000402; + constexpr inline const DeviceCode DeviceCode_ExtconChgS = 0x3500040C; + constexpr inline const DeviceCode DeviceCode_WifiRfDisable = 0x38000003; + constexpr inline const DeviceCode DeviceCode_WifiReset = 0x38000002; + constexpr inline const DeviceCode DeviceCode_ApWakeBt = 0x37000003; + constexpr inline const DeviceCode DeviceCode_BtWakeAp = 0x37000004; + constexpr inline const DeviceCode DeviceCode_BtGpio5 = 0x37000404; + constexpr inline const DeviceCode DeviceCode_PowLcdVddPEn = 0x34000034; + constexpr inline const DeviceCode DeviceCode_PowLcdVddNEn = 0x34000035; + constexpr inline const DeviceCode DeviceCode_ExtconDetU = 0x35000401; + constexpr inline const DeviceCode DeviceCode_RamCode2 = 0xC9000401; + constexpr inline const DeviceCode DeviceCode_Vdd50BEn = 0x39000402; + constexpr inline const DeviceCode DeviceCode_WifiWakeHost = 0x38000004; + constexpr inline const DeviceCode DeviceCode_SdCd = 0x3C000002; + constexpr inline const DeviceCode DeviceCode_OtgFet1ForSdev = 0x39000404; + constexpr inline const DeviceCode DeviceCode_OtgFet2ForSdev = 0x39000405; + constexpr inline const DeviceCode DeviceCode_ExtConWakeU = 0x35000403; + constexpr inline const DeviceCode DeviceCode_ExtConWakeS = 0x3500040D; + constexpr inline const DeviceCode DeviceCode_PmuIrq = 0x39000406; + constexpr inline const DeviceCode DeviceCode_ExtUart2Cts = 0x35000404; + constexpr inline const DeviceCode DeviceCode_ExtUart3Cts = 0x3500040E; + constexpr inline const DeviceCode DeviceCode_5VStepDownEn = 0x39000408; + constexpr inline const DeviceCode DeviceCode_UsbSwitchB2Oc = 0x04000401; + constexpr inline const DeviceCode DeviceCode_5VStepDownPg = 0x39000409; + constexpr inline const DeviceCode DeviceCode_UsbSwitchAEn = 0x04000402; + constexpr inline const DeviceCode DeviceCode_UsbSwitchAFlag = 0x04000403; + constexpr inline const DeviceCode DeviceCode_UsbSwitchB3Oc = 0x04000404; + constexpr inline const DeviceCode DeviceCode_UsbSwitchB3En = 0x04000405; + constexpr inline const DeviceCode DeviceCode_UsbSwitchB2En = 0x04000406; + constexpr inline const DeviceCode DeviceCode_Hdmi5VEn = 0x34000004; + constexpr inline const DeviceCode DeviceCode_UsbSwitchB1En = 0x04000407; + constexpr inline const DeviceCode DeviceCode_HdmiPdTrEn = 0x34000005; + constexpr inline const DeviceCode DeviceCode_FanEn = 0x3D000003; + constexpr inline const DeviceCode DeviceCode_UsbSwitchB1Oc = 0x04000408; + constexpr inline const DeviceCode DeviceCode_PwmFan = 0x3D000001; + constexpr inline const DeviceCode DeviceCode_HdmiHpd = 0x34000006; + constexpr inline const DeviceCode DeviceCode_Max77812Irq = 0x3E000003; + constexpr inline const DeviceCode DeviceCode_Debug0 = 0xCA000001; + constexpr inline const DeviceCode DeviceCode_Debug1 = 0xCA000002; + constexpr inline const DeviceCode DeviceCode_Debug2 = 0xCA000003; + constexpr inline const DeviceCode DeviceCode_Debug3 = 0xCA000004; + constexpr inline const DeviceCode DeviceCode_NfcIrq = 0x36000004; + constexpr inline const DeviceCode DeviceCode_NfcRst = 0x36000003; + constexpr inline const DeviceCode DeviceCode_McuIrq = 0x35000415; + constexpr inline const DeviceCode DeviceCode_McuBoot = 0x35000416; + constexpr inline const DeviceCode DeviceCode_McuRst = 0x35000417; + constexpr inline const DeviceCode DeviceCode_Vdd5V3En = 0x39000403; + constexpr inline const DeviceCode DeviceCode_McuPor = 0x35000418; + constexpr inline const DeviceCode DeviceCode_LcdGpio1 = 0x35000005; + constexpr inline const DeviceCode DeviceCode_NfcEn = 0x36000002; + constexpr inline const DeviceCode DeviceCode_ExtUart2Rts = 0x35000406; + constexpr inline const DeviceCode DeviceCode_ExtUart3Rts = 0x35000410; + constexpr inline const DeviceCode DeviceCode_GpioPortC7 = 0x3500041B; + constexpr inline const DeviceCode DeviceCode_GpioPortD0 = 0x3500041C; + constexpr inline const DeviceCode DeviceCode_GpioPortC5 = 0x3500041D; + constexpr inline const DeviceCode DeviceCode_GpioPortC6 = 0x3500041E; + constexpr inline const DeviceCode DeviceCode_GpioPortY7 = 0x35000065; + constexpr inline const DeviceCode DeviceCode_GpioPortF1 = 0x04000409; + constexpr inline const DeviceCode DeviceCode_GpioPortH0 = 0x34000401; + + constexpr inline GpioPadName ConvertToGpioPadName(DeviceCode dc) { + switch (dc.GetInternalValue()) { + case DeviceCode_CodecLdoEnTemp .GetInternalValue(): return GpioPadName_CodecLdoEnTemp; + case DeviceCode_PowSdEn .GetInternalValue(): return GpioPadName_PowSdEn; + case DeviceCode_BtRst .GetInternalValue(): return GpioPadName_BtRst; + case DeviceCode_RamCode3 .GetInternalValue(): return GpioPadName_RamCode3; + case DeviceCode_GameCardReset .GetInternalValue(): return GpioPadName_GameCardReset; + case DeviceCode_CodecAlert .GetInternalValue(): return GpioPadName_CodecAlert; + case DeviceCode_PowGc .GetInternalValue(): return GpioPadName_PowGc; + case DeviceCode_DebugControllerDet.GetInternalValue(): return GpioPadName_DebugControllerDet; + case DeviceCode_BattChgStatus .GetInternalValue(): return GpioPadName_BattChgStatus; + case DeviceCode_BattChgEnableN .GetInternalValue(): return GpioPadName_BattChgEnableN; + case DeviceCode_FanTach .GetInternalValue(): return GpioPadName_FanTach; + case DeviceCode_ExtconDetS .GetInternalValue(): return GpioPadName_ExtconDetS; + case DeviceCode_Vdd50AEn .GetInternalValue(): return GpioPadName_Vdd50AEn; + case DeviceCode_SdevCoaxSel1 .GetInternalValue(): return GpioPadName_SdevCoaxSel1; + case DeviceCode_GameCardCd .GetInternalValue(): return GpioPadName_GameCardCd; + case DeviceCode_ProdType0 .GetInternalValue(): return GpioPadName_ProdType0; + case DeviceCode_ProdType1 .GetInternalValue(): return GpioPadName_ProdType1; + case DeviceCode_ProdType2 .GetInternalValue(): return GpioPadName_ProdType2; + case DeviceCode_ProdType3 .GetInternalValue(): return GpioPadName_ProdType3; + case DeviceCode_TempAlert .GetInternalValue(): return GpioPadName_TempAlert; + case DeviceCode_CodecHpDetIrq .GetInternalValue(): return GpioPadName_CodecHpDetIrq; + case DeviceCode_MotionInt .GetInternalValue(): return GpioPadName_MotionInt; + case DeviceCode_TpIrq .GetInternalValue(): return GpioPadName_TpIrq; + case DeviceCode_ButtonSleep2 .GetInternalValue(): return GpioPadName_ButtonSleep2; + case DeviceCode_ButtonVolUp .GetInternalValue(): return GpioPadName_ButtonVolUp; + case DeviceCode_ButtonVolDn .GetInternalValue(): return GpioPadName_ButtonVolDn; + case DeviceCode_BattMgicIrq .GetInternalValue(): return GpioPadName_BattMgicIrq; + case DeviceCode_RecoveryKey .GetInternalValue(): return GpioPadName_RecoveryKey; + case DeviceCode_PowLcdBlEn .GetInternalValue(): return GpioPadName_PowLcdBlEn; + case DeviceCode_LcdReset .GetInternalValue(): return GpioPadName_LcdReset; + case DeviceCode_PdVconnEn .GetInternalValue(): return GpioPadName_PdVconnEn; + case DeviceCode_PdRstN .GetInternalValue(): return GpioPadName_PdRstN; + case DeviceCode_Bq24190Irq .GetInternalValue(): return GpioPadName_Bq24190Irq; + case DeviceCode_SdevCoaxSel0 .GetInternalValue(): return GpioPadName_SdevCoaxSel0; + case DeviceCode_SdWp .GetInternalValue(): return GpioPadName_SdWp; + case DeviceCode_TpReset .GetInternalValue(): return GpioPadName_TpReset; + case DeviceCode_BtGpio2 .GetInternalValue(): return GpioPadName_BtGpio2; + case DeviceCode_BtGpio3 .GetInternalValue(): return GpioPadName_BtGpio3; + case DeviceCode_BtGpio4 .GetInternalValue(): return GpioPadName_BtGpio4; + case DeviceCode_CradleIrq .GetInternalValue(): return GpioPadName_CradleIrq; + /* case DeviceCode_PowVcpuInt .GetInternalValue(): return GpioPadName_PowVcpuInt; */ + case DeviceCode_Max77621GpuInt .GetInternalValue(): return GpioPadName_Max77621GpuInt; + case DeviceCode_ExtconChgU .GetInternalValue(): return GpioPadName_ExtconChgU; + case DeviceCode_ExtconChgS .GetInternalValue(): return GpioPadName_ExtconChgS; + case DeviceCode_WifiRfDisable .GetInternalValue(): return GpioPadName_WifiRfDisable; + case DeviceCode_WifiReset .GetInternalValue(): return GpioPadName_WifiReset; + case DeviceCode_ApWakeBt .GetInternalValue(): return GpioPadName_ApWakeBt; + case DeviceCode_BtWakeAp .GetInternalValue(): return GpioPadName_BtWakeAp; + case DeviceCode_BtGpio5 .GetInternalValue(): return GpioPadName_BtGpio5; + case DeviceCode_PowLcdVddPEn .GetInternalValue(): return GpioPadName_PowLcdVddPEn; + case DeviceCode_PowLcdVddNEn .GetInternalValue(): return GpioPadName_PowLcdVddNEn; + case DeviceCode_ExtconDetU .GetInternalValue(): return GpioPadName_ExtconDetU; + case DeviceCode_RamCode2 .GetInternalValue(): return GpioPadName_RamCode2; + case DeviceCode_Vdd50BEn .GetInternalValue(): return GpioPadName_Vdd50BEn; + case DeviceCode_WifiWakeHost .GetInternalValue(): return GpioPadName_WifiWakeHost; + case DeviceCode_SdCd .GetInternalValue(): return GpioPadName_SdCd; + case DeviceCode_OtgFet1ForSdev .GetInternalValue(): return GpioPadName_OtgFet1ForSdev; + case DeviceCode_OtgFet2ForSdev .GetInternalValue(): return GpioPadName_OtgFet2ForSdev; + case DeviceCode_ExtConWakeU .GetInternalValue(): return GpioPadName_ExtConWakeU; + case DeviceCode_ExtConWakeS .GetInternalValue(): return GpioPadName_ExtConWakeS; + case DeviceCode_PmuIrq .GetInternalValue(): return GpioPadName_PmuIrq; + case DeviceCode_ExtUart2Cts .GetInternalValue(): return GpioPadName_ExtUart2Cts; + case DeviceCode_ExtUart3Cts .GetInternalValue(): return GpioPadName_ExtUart3Cts; + case DeviceCode_5VStepDownEn .GetInternalValue(): return GpioPadName_5VStepDownEn; + case DeviceCode_UsbSwitchB2Oc .GetInternalValue(): return GpioPadName_UsbSwitchB2Oc; + case DeviceCode_5VStepDownPg .GetInternalValue(): return GpioPadName_5VStepDownPg; + case DeviceCode_UsbSwitchAEn .GetInternalValue(): return GpioPadName_UsbSwitchAEn; + case DeviceCode_UsbSwitchAFlag .GetInternalValue(): return GpioPadName_UsbSwitchAFlag; + case DeviceCode_UsbSwitchB3Oc .GetInternalValue(): return GpioPadName_UsbSwitchB3Oc; + case DeviceCode_UsbSwitchB3En .GetInternalValue(): return GpioPadName_UsbSwitchB3En; + case DeviceCode_UsbSwitchB2En .GetInternalValue(): return GpioPadName_UsbSwitchB2En; + case DeviceCode_Hdmi5VEn .GetInternalValue(): return GpioPadName_Hdmi5VEn; + case DeviceCode_UsbSwitchB1En .GetInternalValue(): return GpioPadName_UsbSwitchB1En; + case DeviceCode_HdmiPdTrEn .GetInternalValue(): return GpioPadName_HdmiPdTrEn; + case DeviceCode_FanEn .GetInternalValue(): return GpioPadName_FanEn; + case DeviceCode_UsbSwitchB1Oc .GetInternalValue(): return GpioPadName_UsbSwitchB1Oc; + case DeviceCode_PwmFan .GetInternalValue(): return GpioPadName_PwmFan; + case DeviceCode_HdmiHpd .GetInternalValue(): return GpioPadName_HdmiHpd; + case DeviceCode_Max77812Irq .GetInternalValue(): return GpioPadName_Max77812Irq; + case DeviceCode_Debug0 .GetInternalValue(): return GpioPadName_Debug0; + case DeviceCode_Debug1 .GetInternalValue(): return GpioPadName_Debug1; + case DeviceCode_Debug2 .GetInternalValue(): return GpioPadName_Debug2; + case DeviceCode_Debug3 .GetInternalValue(): return GpioPadName_Debug3; + case DeviceCode_NfcIrq .GetInternalValue(): return GpioPadName_NfcIrq; + case DeviceCode_NfcRst .GetInternalValue(): return GpioPadName_NfcRst; + case DeviceCode_McuIrq .GetInternalValue(): return GpioPadName_McuIrq; + case DeviceCode_McuBoot .GetInternalValue(): return GpioPadName_McuBoot; + case DeviceCode_McuRst .GetInternalValue(): return GpioPadName_McuRst; + case DeviceCode_Vdd5V3En .GetInternalValue(): return GpioPadName_Vdd5V3En; + case DeviceCode_McuPor .GetInternalValue(): return GpioPadName_McuPor; + case DeviceCode_LcdGpio1 .GetInternalValue(): return GpioPadName_LcdGpio1; + case DeviceCode_NfcEn .GetInternalValue(): return GpioPadName_NfcEn; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr inline DeviceCode ConvertToDeviceCode(GpioPadName gpn) { + switch (gpn) { + case GpioPadName_CodecLdoEnTemp: return DeviceCode_CodecLdoEnTemp; + case GpioPadName_PowSdEn: return DeviceCode_PowSdEn; + case GpioPadName_BtRst: return DeviceCode_BtRst; + case GpioPadName_RamCode3: return DeviceCode_RamCode3; + case GpioPadName_GameCardReset: return DeviceCode_GameCardReset; + case GpioPadName_CodecAlert: return DeviceCode_CodecAlert; + case GpioPadName_PowGc: return DeviceCode_PowGc; + case GpioPadName_DebugControllerDet: return DeviceCode_DebugControllerDet; + case GpioPadName_BattChgStatus: return DeviceCode_BattChgStatus; + case GpioPadName_BattChgEnableN: return DeviceCode_BattChgEnableN; + case GpioPadName_FanTach: return DeviceCode_FanTach; + case GpioPadName_ExtconDetS: return DeviceCode_ExtconDetS; + case GpioPadName_Vdd50AEn: return DeviceCode_Vdd50AEn; + case GpioPadName_SdevCoaxSel1: return DeviceCode_SdevCoaxSel1; + case GpioPadName_GameCardCd: return DeviceCode_GameCardCd; + case GpioPadName_ProdType0: return DeviceCode_ProdType0; + case GpioPadName_ProdType1: return DeviceCode_ProdType1; + case GpioPadName_ProdType2: return DeviceCode_ProdType2; + case GpioPadName_ProdType3: return DeviceCode_ProdType3; + case GpioPadName_TempAlert: return DeviceCode_TempAlert; + case GpioPadName_CodecHpDetIrq: return DeviceCode_CodecHpDetIrq; + case GpioPadName_MotionInt: return DeviceCode_MotionInt; + case GpioPadName_TpIrq: return DeviceCode_TpIrq; + case GpioPadName_ButtonSleep2: return DeviceCode_ButtonSleep2; + case GpioPadName_ButtonVolUp: return DeviceCode_ButtonVolUp; + case GpioPadName_ButtonVolDn: return DeviceCode_ButtonVolDn; + case GpioPadName_BattMgicIrq: return DeviceCode_BattMgicIrq; + case GpioPadName_RecoveryKey: return DeviceCode_RecoveryKey; + case GpioPadName_PowLcdBlEn: return DeviceCode_PowLcdBlEn; + case GpioPadName_LcdReset: return DeviceCode_LcdReset; + case GpioPadName_PdVconnEn: return DeviceCode_PdVconnEn; + case GpioPadName_PdRstN: return DeviceCode_PdRstN; + case GpioPadName_Bq24190Irq: return DeviceCode_Bq24190Irq; + case GpioPadName_SdevCoaxSel0: return DeviceCode_SdevCoaxSel0; + case GpioPadName_SdWp: return DeviceCode_SdWp; + case GpioPadName_TpReset: return DeviceCode_TpReset; + case GpioPadName_BtGpio2: return DeviceCode_BtGpio2; + case GpioPadName_BtGpio3: return DeviceCode_BtGpio3; + case GpioPadName_BtGpio4: return DeviceCode_BtGpio4; + case GpioPadName_CradleIrq: return DeviceCode_CradleIrq; + case GpioPadName_PowVcpuInt: return DeviceCode_PowVcpuInt; + case GpioPadName_Max77621GpuInt: return DeviceCode_Max77621GpuInt; + case GpioPadName_ExtconChgU: return DeviceCode_ExtconChgU; + case GpioPadName_ExtconChgS: return DeviceCode_ExtconChgS; + case GpioPadName_WifiRfDisable: return DeviceCode_WifiRfDisable; + case GpioPadName_WifiReset: return DeviceCode_WifiReset; + case GpioPadName_ApWakeBt: return DeviceCode_ApWakeBt; + case GpioPadName_BtWakeAp: return DeviceCode_BtWakeAp; + case GpioPadName_BtGpio5: return DeviceCode_BtGpio5; + case GpioPadName_PowLcdVddPEn: return DeviceCode_PowLcdVddPEn; + case GpioPadName_PowLcdVddNEn: return DeviceCode_PowLcdVddNEn; + case GpioPadName_ExtconDetU: return DeviceCode_ExtconDetU; + case GpioPadName_RamCode2: return DeviceCode_RamCode2; + case GpioPadName_Vdd50BEn: return DeviceCode_Vdd50BEn; + case GpioPadName_WifiWakeHost: return DeviceCode_WifiWakeHost; + case GpioPadName_SdCd: return DeviceCode_SdCd; + case GpioPadName_OtgFet1ForSdev: return DeviceCode_OtgFet1ForSdev; + case GpioPadName_OtgFet2ForSdev: return DeviceCode_OtgFet2ForSdev; + case GpioPadName_ExtConWakeU: return DeviceCode_ExtConWakeU; + case GpioPadName_ExtConWakeS: return DeviceCode_ExtConWakeS; + case GpioPadName_PmuIrq: return DeviceCode_PmuIrq; + case GpioPadName_ExtUart2Cts: return DeviceCode_ExtUart2Cts; + case GpioPadName_ExtUart3Cts: return DeviceCode_ExtUart3Cts; + case GpioPadName_5VStepDownEn: return DeviceCode_5VStepDownEn; + case GpioPadName_UsbSwitchB2Oc: return DeviceCode_UsbSwitchB2Oc; + case GpioPadName_5VStepDownPg: return DeviceCode_5VStepDownPg; + case GpioPadName_UsbSwitchAEn: return DeviceCode_UsbSwitchAEn; + case GpioPadName_UsbSwitchAFlag: return DeviceCode_UsbSwitchAFlag; + case GpioPadName_UsbSwitchB3Oc: return DeviceCode_UsbSwitchB3Oc; + case GpioPadName_UsbSwitchB3En: return DeviceCode_UsbSwitchB3En; + case GpioPadName_UsbSwitchB2En: return DeviceCode_UsbSwitchB2En; + case GpioPadName_Hdmi5VEn: return DeviceCode_Hdmi5VEn; + case GpioPadName_UsbSwitchB1En: return DeviceCode_UsbSwitchB1En; + case GpioPadName_HdmiPdTrEn: return DeviceCode_HdmiPdTrEn; + case GpioPadName_FanEn: return DeviceCode_FanEn; + case GpioPadName_UsbSwitchB1Oc: return DeviceCode_UsbSwitchB1Oc; + case GpioPadName_PwmFan: return DeviceCode_PwmFan; + case GpioPadName_HdmiHpd: return DeviceCode_HdmiHpd; + case GpioPadName_Max77812Irq: return DeviceCode_Max77812Irq; + case GpioPadName_Debug0: return DeviceCode_Debug0; + case GpioPadName_Debug1: return DeviceCode_Debug1; + case GpioPadName_Debug2: return DeviceCode_Debug2; + case GpioPadName_Debug3: return DeviceCode_Debug3; + case GpioPadName_NfcIrq: return DeviceCode_NfcIrq; + case GpioPadName_NfcRst: return DeviceCode_NfcRst; + case GpioPadName_McuIrq: return DeviceCode_McuIrq; + case GpioPadName_McuBoot: return DeviceCode_McuBoot; + case GpioPadName_McuRst: return DeviceCode_McuRst; + case GpioPadName_Vdd5V3En: return DeviceCode_Vdd5V3En; + case GpioPadName_McuPor: return DeviceCode_McuPor; + case GpioPadName_LcdGpio1: return DeviceCode_LcdGpio1; + case GpioPadName_NfcEn: return DeviceCode_NfcEn; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/gpio_select_pad_name.hpp b/libraries/libstratosphere/include/stratosphere/gpio/gpio_select_pad_name.hpp new file mode 100644 index 000000000..25e665375 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/gpio_select_pad_name.hpp @@ -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 . + */ +#pragma once +#include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + #include +#else + /* Error? */ +#endif diff --git a/libraries/libstratosphere/include/stratosphere/gpio/gpio_types.hpp b/libraries/libstratosphere/include/stratosphere/gpio/gpio_types.hpp new file mode 100644 index 000000000..76f1768e5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/gpio_types.hpp @@ -0,0 +1,51 @@ +/* + * 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 . + */ +#pragma once +#include + +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, + }; + + enum WakePinDebugMode { + WakePinDebugMode_AutoImmediateWake = 1, + WakePinDebugMode_NoWake = 2, + }; + + using WakeBitFlag = util::BitFlagSet<128>; + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/server/gpio_server_api.hpp b/libraries/libstratosphere/include/stratosphere/gpio/server/gpio_server_api.hpp new file mode 100644 index 000000000..07f20c6c0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/server/gpio_server_api.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::gpio::server { + + std::shared_ptr GetServiceObject(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/sf/gpio_sf_i_manager.hpp b/libraries/libstratosphere/include/stratosphere/gpio/sf/gpio_sf_i_manager.hpp new file mode 100644 index 000000000..e54dc7b1b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/sf/gpio_sf_i_manager.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include +#include +#include + +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> out, s32 pad_descriptor) ) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, OpenSession, (ams::sf::Out> out, gpio::GpioPadName pad_name) ) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, OpenSessionForTest, (ams::sf::Out> out, gpio::GpioPadName pad_name) ) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, IsWakeEventActive, (ams::sf::Out 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 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> 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 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) + +} diff --git a/libraries/libstratosphere/include/stratosphere/gpio/sf/gpio_sf_i_pad_session.hpp b/libraries/libstratosphere/include/stratosphere/gpio/sf/gpio_sf_i_pad_session.hpp new file mode 100644 index 000000000..ec4791b1b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/gpio/sf/gpio_sf_i_pad_session.hpp @@ -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 . + */ + +#pragma once +#include +#include + +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 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 out) ) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, SetInterruptEnable, (bool enable) ) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, GetInterruptEnable, (ams::sf::Out out) ) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, GetInterruptStatus, (ams::sf::Out 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 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 out) ) \ + AMS_SF_METHOD_INFO(C, H, 14, Result, SetDebounceTime, (s32 ms) ) \ + AMS_SF_METHOD_INFO(C, H, 15, Result, GetDebounceTime, (ams::sf::Out 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 out), hos::Version_6_0_0) + + AMS_SF_DEFINE_INTERFACE(IPadSession, AMS_GPIO_I_PAD_SESSION_INTERFACE_INFO) + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c.hpp b/libraries/libstratosphere/include/stratosphere/i2c.hpp new file mode 100644 index 000000000..fc65c1a83 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c.hpp @@ -0,0 +1,31 @@ +/* + * 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 . + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo_nx/i2c_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo_nx/i2c_driver_api.hpp new file mode 100644 index 000000000..67bf0821d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/board/nintendo_nx/i2c_driver_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::i2c::driver::board::nintendo_nx { + + void Initialize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_bus_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_bus_api.hpp new file mode 100644 index 000000000..e6f198246 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_bus_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::i2c::driver { + + namespace impl { + + constexpr inline size_t I2cSessionSize = 0x60; + constexpr inline size_t I2cSessionAlign = 8; + struct alignas(I2cSessionAlign) I2cSessionImplPadded; + + } + + struct I2cSession { + util::TypedStorage _impl; + }; + + Result OpenSession(I2cSession *out, DeviceCode device_code); + void CloseSession(I2cSession &session); + + Result Send(I2cSession &session, const void *src, size_t src_size, TransactionOption option); + Result Receive(void *dst, size_t dst_size, I2cSession &session, TransactionOption option); + + Result ExecuteCommandList(void *dst, size_t dst_size, I2cSession &session, const void *src, size_t src_size); + + Result SetRetryPolicy(I2cSession &session, int max_retry_count, int retry_interval_us); + +} + diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_client_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_client_api.hpp new file mode 100644 index 000000000..d73f04a8b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_client_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::i2c::driver { + + void Initialize(); + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_service_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_service_api.hpp new file mode 100644 index 000000000..08c3662e7 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_driver_service_api.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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::i2c::driver { + + void RegisterDriver(II2cDriver *driver); + void UnregisterDriver(II2cDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, I2cDeviceProperty *device); + bool UnregisterDeviceCode(DeviceCode device_code); + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_i2c_device_property.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_i2c_device_property.hpp new file mode 100644 index 000000000..42e4efd7f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_i2c_device_property.hpp @@ -0,0 +1,50 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::i2c::driver { + + class I2cDeviceProperty : public ::ams::ddsf::IDevice { + NON_COPYABLE(I2cDeviceProperty); + NON_MOVEABLE(I2cDeviceProperty); + AMS_DDSF_CASTABLE_TRAITS(ams::i2c::driver::I2cDeviceProperty, ::ams::ddsf::IDevice); + private: + u16 address; + AddressingMode addressing_mode; + util::IntrusiveListNode device_property_list_node; + public: + using DevicePropertyListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&I2cDeviceProperty::device_property_list_node>; + using DevicePropertyList = typename DevicePropertyListTraits::ListType; + friend class util::IntrusiveList>; + public: + I2cDeviceProperty() : IDevice(false), address(0), addressing_mode(AddressingMode_SevenBit), device_property_list_node() { /* ... */ } + I2cDeviceProperty(u16 addr, AddressingMode m) : IDevice(false), address(addr), addressing_mode(m), device_property_list_node() { /* ... */ } + + virtual ~I2cDeviceProperty() { /* ... */ } + + u16 GetAddress() const { + return this->address; + } + + AddressingMode GetAddressingMode() const { + return this->addressing_mode; + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_i_i2c_driver.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_i_i2c_driver.hpp new file mode 100644 index 000000000..7afdde3f4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_i_i2c_driver.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::i2c::driver { + + class I2cDeviceProperty; + + class II2cDriver : public ::ams::ddsf::IDriver { + NON_COPYABLE(II2cDriver); + NON_MOVEABLE(II2cDriver); + AMS_DDSF_CASTABLE_TRAITS(ams::i2c::driver::II2cDriver, ::ams::ddsf::IDriver); + public: + II2cDriver() : IDriver() { /* ... */ } + virtual ~II2cDriver() { /* ... */ } + + virtual void InitializeDriver() = 0; + virtual void FinalizeDriver() = 0; + + virtual Result InitializeDevice(I2cDeviceProperty *device) = 0; + virtual void FinalizeDevice(I2cDeviceProperty *device) = 0; + + virtual Result Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) = 0; + virtual Result Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) = 0; + + virtual os::SdkMutex &GetTransactionOrderMutex() = 0; + + virtual void SuspendBus() = 0; + virtual void SuspendPowerBus() = 0; + + virtual void ResumeBus() = 0; + virtual void ResumePowerBus() = 0; + + virtual const DeviceCode &GetDeviceCode() const = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_select_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_select_driver_api.hpp new file mode 100644 index 000000000..ce5f587bd --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/i2c_select_driver_api.hpp @@ -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 . + */ +#pragma once +#include +#include +#include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include + + namespace ams::i2c::driver::board { + + using namespace ams::i2c::driver::board::nintendo_nx; + + } + +#else + + #error "Unknown board for ams::i2c::driver::" + +#endif + diff --git a/libraries/libstratosphere/include/stratosphere/i2c/driver/impl/i2c_i2c_session_impl.hpp b/libraries/libstratosphere/include/stratosphere/i2c/driver/impl/i2c_i2c_session_impl.hpp new file mode 100644 index 000000000..2bb24c70a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/driver/impl/i2c_i2c_session_impl.hpp @@ -0,0 +1,93 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::i2c::driver { + + class I2cDeviceProperty; + +} + +namespace ams::i2c::driver::impl { + + class I2cSessionImpl : public ::ams::ddsf::ISession { + NON_COPYABLE(I2cSessionImpl); + NON_MOVEABLE(I2cSessionImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::i2c::driver::impl::I2cSessionImpl, ::ams::ddsf::ISession); + private: + enum class Command { + Send = 0, + Receive = 1, + }; + private: + TimeSpan retry_interval; + int max_retry_count; + private: + Result SendHandler(const u8 **cur_cmd, u8 **cur_dst); + Result ReceiveHandler(const u8 **cur_cmd, u8 **cur_dst); + Result ExtensionHandler(const u8 **cur_cmd, u8 **cur_dst); + + Result ExecuteTransactionWithRetry(void *dst, Command command, const void *src, size_t size, TransactionOption option); + public: + I2cSessionImpl(int mr, TimeSpan rt) : retry_interval(rt), max_retry_count(mr) { /* ... */ } + + ~I2cSessionImpl() { + this->Close(); + } + + Result Open(I2cDeviceProperty *device, ddsf::AccessMode access_mode); + void Close(); + + Result Send(const void *src, size_t src_size, TransactionOption option); + Result Receive(void *dst, size_t dst_size, TransactionOption option); + Result ExecuteCommandList(void *dst, size_t dst_size, const void *src, size_t src_size); + Result SetRetryPolicy(int mr, int interval_us); + }; + static_assert( sizeof(I2cSessionImpl) <= I2cSessionSize); + static_assert(alignof(I2cSessionImpl) <= I2cSessionAlign); + + struct alignas(I2cSessionAlign) I2cSessionImplPadded { + I2cSessionImpl _impl; + u8 _padding[I2cSessionSize - sizeof(I2cSessionImpl)]; + }; + static_assert( sizeof(I2cSessionImplPadded) == I2cSessionSize); + static_assert(alignof(I2cSessionImplPadded) == I2cSessionAlign); + + ALWAYS_INLINE I2cSessionImpl &GetI2cSessionImpl(I2cSession &session) { + return GetReference(session._impl)._impl; + } + + ALWAYS_INLINE const I2cSessionImpl &GetI2cSessionImpl(const I2cSession &session) { + return GetReference(session._impl)._impl; + } + + ALWAYS_INLINE I2cSessionImpl &GetOpenI2cSessionImpl(I2cSession &session) { + auto &ref = GetReference(session._impl)._impl; + AMS_ASSERT(ref.IsOpen()); + return ref; + } + + ALWAYS_INLINE const I2cSessionImpl &GetOpenI2cSessionImpl(const I2cSession &session) { + const auto &ref = GetReference(session._impl)._impl; + AMS_ASSERT(ref.IsOpen()); + return ref; + } + +} + diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_api.hpp new file mode 100644 index 000000000..43cdb23aa --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_api.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::i2c { + + void InitializeWith(std::shared_ptr &&sp, std::shared_ptr &&sp_pcv); + void InitializeEmpty(); + + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_bus_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_bus_api.hpp new file mode 100644 index 000000000..005ba9884 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_bus_api.hpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::i2c { + + struct I2cSession { + void *_session; + }; + + Result OpenSession(I2cSession *out, DeviceCode device_code); + void CloseSession(I2cSession &session); + + Result Send(const I2cSession &session, const void *src, size_t src_size, TransactionOption option); + Result Receive(void *dst, size_t dst_size, const I2cSession &session, TransactionOption option); + + Result ExecuteCommandList(void *dst, size_t dst_size, const I2cSession &session, const void *src, size_t src_size); + + void SetRetryPolicy(const I2cSession &session, int max_retry_count, int retry_interval_us); + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp new file mode 100644 index 000000000..8a0e83c2c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_command_list_formatter.hpp @@ -0,0 +1,51 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::i2c { + + constexpr inline size_t CommandListLengthMax = 0x100; + constexpr inline size_t CommandListReceiveCommandSize = 2; + constexpr inline size_t CommandListSendCommandSize = 2; + constexpr inline size_t CommandListSleepCommandSize = 2; + + class CommandListFormatter { + NON_COPYABLE(CommandListFormatter); + NON_MOVEABLE(CommandListFormatter); + private: + size_t current_index; + size_t command_list_length; + void *command_list; + private: + Result IsEnqueueAble(size_t sz) const; + public: + CommandListFormatter(void *p, size_t sz) : current_index(0), command_list_length(sz), command_list(p) { + AMS_ABORT_UNLESS(this->command_list_length <= CommandListLengthMax); + } + + ~CommandListFormatter() { this->command_list = nullptr; } + + size_t GetCurrentLength() const { return this->current_index; } + const void *GetListHead() const { return this->command_list; } + + Result EnqueueReceiveCommand(i2c::TransactionOption option, size_t size); + Result EnqueueSendCommand(i2c::TransactionOption option, const void *src, size_t size); + Result EnqueueSleepCommand(int us); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_device_name.board.nintendo_nx.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_device_name.board.nintendo_nx.hpp new file mode 100644 index 000000000..7039f3904 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_device_name.board.nintendo_nx.hpp @@ -0,0 +1,250 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::i2c { + + enum I2cBus { + I2cBus_I2c1 = 0, + I2cBus_I2c2 = 1, + I2cBus_I2c3 = 2, + I2cBus_I2c4 = 3, + I2cBus_I2c5 = 4, + I2cBus_I2c6 = 5, + }; + + constexpr inline const DeviceCode DeviceCode_I2c1 = 0x02000001; + constexpr inline const DeviceCode DeviceCode_I2c2 = 0x02000002; + constexpr inline const DeviceCode DeviceCode_I2c3 = 0x02000003; + constexpr inline const DeviceCode DeviceCode_I2c4 = 0x02000004; + constexpr inline const DeviceCode DeviceCode_I2c5 = 0x02000005; + constexpr inline const DeviceCode DeviceCode_I2c6 = 0x02000006; + + constexpr inline DeviceCode ConvertToDeviceCode(I2cBus bus) { + switch (bus) { + case I2cBus_I2c1: return DeviceCode_I2c1; + case I2cBus_I2c2: return DeviceCode_I2c2; + case I2cBus_I2c3: return DeviceCode_I2c3; + case I2cBus_I2c4: return DeviceCode_I2c4; + case I2cBus_I2c5: return DeviceCode_I2c5; + case I2cBus_I2c6: return DeviceCode_I2c6; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr inline DeviceCode ConvertToI2cBus(DeviceCode dc) { + switch (dc.GetInternalValue()) { + case DeviceCode_I2c1.GetInternalValue(): return I2cBus_I2c1; + case DeviceCode_I2c2.GetInternalValue(): return I2cBus_I2c2; + case DeviceCode_I2c3.GetInternalValue(): return I2cBus_I2c3; + case DeviceCode_I2c4.GetInternalValue(): return I2cBus_I2c4; + case DeviceCode_I2c5.GetInternalValue(): return I2cBus_I2c5; + case DeviceCode_I2c6.GetInternalValue(): return I2cBus_I2c6; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + enum I2cDevice : u32 { + I2cDevice_ClassicController = 0, + I2cDevice_Ftm3bd56 = 1, + I2cDevice_Tmp451 = 2, + I2cDevice_Nct72 = 3, + I2cDevice_Alc5639 = 4, + I2cDevice_Max77620Rtc = 5, + I2cDevice_Max77620Pmic = 6, + I2cDevice_Max77621Cpu = 7, + I2cDevice_Max77621Gpu = 8, + I2cDevice_Bq24193 = 9, + I2cDevice_Max17050 = 10, + I2cDevice_Bm92t30mwv = 11, + I2cDevice_Ina226Vdd15v0Hb = 12, + + I2cDevice_Ina226VsysCpuDs = 13, + I2cDevice_Ina226VddCpuAp = 13, + + I2cDevice_Ina226VsysGpuDs = 14, + I2cDevice_Ina226VddGpuAp = 14, + + I2cDevice_Ina226VsysDdrDs = 15, + I2cDevice_Ina226VddDdr1V1Pmic = 15, + + I2cDevice_Ina226VsysAp = 16, + I2cDevice_Ina226VsysBlDs = 17, + I2cDevice_Bh1730 = 18, + + I2cDevice_Ina226VsysCore = 19, + I2cDevice_Ina226VddCoreAp = 19, + + I2cDevice_Ina226Soc1V8 = 20, + I2cDevice_Ina226VddSoc1V8 = 20, + + I2cDevice_Ina226Lpddr1V8 = 21, + I2cDevice_Ina226Vdd1V8 = 21, + + I2cDevice_Ina226Reg1V32 = 22, + I2cDevice_Ina226Vdd3V3Sys = 23, + I2cDevice_HdmiDdc = 24, + I2cDevice_HdmiScdc = 25, + I2cDevice_HdmiHdcp = 26, + I2cDevice_Fan53528 = 27, + I2cDevice_Max77812_3 = 28, + I2cDevice_Max77812_2 = 29, + I2cDevice_Ina226VddDdr0V6 = 30, + I2cDevice_HoagNfcIc = 31, /* TODO */ + }; + + /* TODO: Better place for this? */ + constexpr inline const DeviceCode DeviceCode_ClassicController = 0x350000C9; + constexpr inline const DeviceCode DeviceCode_Ftm3bd56 = 0x35000033; + constexpr inline const DeviceCode DeviceCode_Tmp451 = 0x3E000001; + constexpr inline const DeviceCode DeviceCode_Nct72 = 0x3E000001; + constexpr inline const DeviceCode DeviceCode_Alc5639 = 0x33000001; + constexpr inline const DeviceCode DeviceCode_Max77620Rtc = 0x3B000001; + constexpr inline const DeviceCode DeviceCode_Max77620Pmic = 0x3A000001; + constexpr inline const DeviceCode DeviceCode_Max77621Cpu = 0x3A000003; + constexpr inline const DeviceCode DeviceCode_Max77621Gpu = 0x3A000004; + constexpr inline const DeviceCode DeviceCode_Bq24193 = 0x39000001; + constexpr inline const DeviceCode DeviceCode_Max17050 = 0x39000033; + constexpr inline const DeviceCode DeviceCode_Bm92t30mwv = 0x040000C9; + constexpr inline const DeviceCode DeviceCode_Ina226Vdd15v0Hb = 0x3F000401; + + constexpr inline const DeviceCode DeviceCode_Ina226VsysCpuDs = 0x3F000001; + constexpr inline const DeviceCode DeviceCode_Ina226VddCpuAp = 0x3F000001; + + constexpr inline const DeviceCode DeviceCode_Ina226VsysGpuDs = 0x3F000002; + constexpr inline const DeviceCode DeviceCode_Ina226VddGpuAp = 0x3F000002; + + constexpr inline const DeviceCode DeviceCode_Ina226VsysDdrDs = 0x3F000003; + constexpr inline const DeviceCode DeviceCode_Ina226VddDdr1V1Pmi = 0x3F000003; + + constexpr inline const DeviceCode DeviceCode_Ina226VsysAp = 0x3F000402; + constexpr inline const DeviceCode DeviceCode_Ina226VsysBlDs = 0x3F000403; + constexpr inline const DeviceCode DeviceCode_Bh1730 = 0x35000047; + + constexpr inline const DeviceCode DeviceCode_Ina226VsysCore = 0x3F000404; + constexpr inline const DeviceCode DeviceCode_Ina226VddCoreAp = 0x3F000404; + + constexpr inline const DeviceCode DeviceCode_Ina226Soc1V8 = 0x3F000405; + constexpr inline const DeviceCode DeviceCode_Ina226VddSoc1V8 = 0x3F000405; + + constexpr inline const DeviceCode DeviceCode_Ina226Lpddr1V8 = 0x3F000406; + constexpr inline const DeviceCode DeviceCode_Ina226Vdd1V8 = 0x3F000406; + + constexpr inline const DeviceCode DeviceCode_Ina226Reg1V32 = 0x3F000407; + constexpr inline const DeviceCode DeviceCode_Ina226Vdd3V3Sys = 0x3F000408; + constexpr inline const DeviceCode DeviceCode_HdmiDdc = 0x34000001; + constexpr inline const DeviceCode DeviceCode_HdmiScdc = 0x34000002; + constexpr inline const DeviceCode DeviceCode_HdmiHdcp = 0x34000003; + constexpr inline const DeviceCode DeviceCode_Fan53528 = 0x3A000005; + constexpr inline const DeviceCode DeviceCode_Max77812_3 = 0x3A000002; + constexpr inline const DeviceCode DeviceCode_Max77812_2 = 0x3A000006; + constexpr inline const DeviceCode DeviceCode_Ina226VddDdr0V6 = 0x3F000409; + constexpr inline const DeviceCode DeviceCode_HoagNfcIc = 0x36000001; + + constexpr inline DeviceCode ConvertToDeviceCode(I2cDevice dv) { + switch (dv) { + case I2cDevice_ClassicController: return DeviceCode_ClassicController; + case I2cDevice_Ftm3bd56: return DeviceCode_Ftm3bd56; + case I2cDevice_Tmp451: return DeviceCode_Tmp451; + case I2cDevice_Nct72: return DeviceCode_Nct72; + case I2cDevice_Alc5639: return DeviceCode_Alc5639; + case I2cDevice_Max77620Rtc: return DeviceCode_Max77620Rtc; + case I2cDevice_Max77620Pmic: return DeviceCode_Max77620Pmic; + case I2cDevice_Max77621Cpu: return DeviceCode_Max77621Cpu; + case I2cDevice_Max77621Gpu: return DeviceCode_Max77621Gpu; + case I2cDevice_Bq24193: return DeviceCode_Bq24193; + case I2cDevice_Max17050: return DeviceCode_Max17050; + case I2cDevice_Bm92t30mwv: return DeviceCode_Bm92t30mwv; + case I2cDevice_Ina226Vdd15v0Hb: return DeviceCode_Ina226Vdd15v0Hb; + case I2cDevice_Ina226VsysCpuDs: return DeviceCode_Ina226VsysCpuDs; + case I2cDevice_Ina226VsysGpuDs: return DeviceCode_Ina226VsysGpuDs; + case I2cDevice_Ina226VsysDdrDs: return DeviceCode_Ina226VsysDdrDs; + case I2cDevice_Ina226VsysAp: return DeviceCode_Ina226VsysAp; + case I2cDevice_Ina226VsysBlDs: return DeviceCode_Ina226VsysBlDs; + case I2cDevice_Bh1730: return DeviceCode_Bh1730; + case I2cDevice_Ina226VsysCore: return DeviceCode_Ina226VsysCore; + case I2cDevice_Ina226Soc1V8: return DeviceCode_Ina226Soc1V8; + case I2cDevice_Ina226Lpddr1V8: return DeviceCode_Ina226Lpddr1V8; + case I2cDevice_Ina226Reg1V32: return DeviceCode_Ina226Reg1V32; + case I2cDevice_Ina226Vdd3V3Sys: return DeviceCode_Ina226Vdd3V3Sys; + case I2cDevice_HdmiDdc: return DeviceCode_HdmiDdc; + case I2cDevice_HdmiScdc: return DeviceCode_HdmiScdc; + case I2cDevice_HdmiHdcp: return DeviceCode_HdmiHdcp; + case I2cDevice_Fan53528: return DeviceCode_Fan53528; + case I2cDevice_Max77812_3: return DeviceCode_Max77812_3; + case I2cDevice_Max77812_2: return DeviceCode_Max77812_2; + case I2cDevice_Ina226VddDdr0V6: return DeviceCode_Ina226VddDdr0V6; + case I2cDevice_HoagNfcIc: return DeviceCode_HoagNfcIc; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr inline I2cDevice ConvertToI2cDevice(DeviceCode dc) { + switch (dc.GetInternalValue()) { + case DeviceCode_ClassicController.GetInternalValue(): return I2cDevice_ClassicController; + case DeviceCode_Ftm3bd56 .GetInternalValue(): return I2cDevice_Ftm3bd56; + case DeviceCode_Tmp451 .GetInternalValue(): return I2cDevice_Tmp451; + /* case DeviceCode_Nct72 .GetInternalValue(): return I2cDevice_Nct72; */ + case DeviceCode_Alc5639 .GetInternalValue(): return I2cDevice_Alc5639; + case DeviceCode_Max77620Rtc .GetInternalValue(): return I2cDevice_Max77620Rtc; + case DeviceCode_Max77620Pmic .GetInternalValue(): return I2cDevice_Max77620Pmic; + case DeviceCode_Max77621Cpu .GetInternalValue(): return I2cDevice_Max77621Cpu; + case DeviceCode_Max77621Gpu .GetInternalValue(): return I2cDevice_Max77621Gpu; + case DeviceCode_Bq24193 .GetInternalValue(): return I2cDevice_Bq24193; + case DeviceCode_Max17050 .GetInternalValue(): return I2cDevice_Max17050; + case DeviceCode_Bm92t30mwv .GetInternalValue(): return I2cDevice_Bm92t30mwv; + case DeviceCode_Ina226Vdd15v0Hb .GetInternalValue(): return I2cDevice_Ina226Vdd15v0Hb; + case DeviceCode_Ina226VsysCpuDs .GetInternalValue(): return I2cDevice_Ina226VsysCpuDs; + case DeviceCode_Ina226VsysGpuDs .GetInternalValue(): return I2cDevice_Ina226VsysGpuDs; + case DeviceCode_Ina226VsysDdrDs .GetInternalValue(): return I2cDevice_Ina226VsysDdrDs; + case DeviceCode_Ina226VsysAp .GetInternalValue(): return I2cDevice_Ina226VsysAp; + case DeviceCode_Ina226VsysBlDs .GetInternalValue(): return I2cDevice_Ina226VsysBlDs; + case DeviceCode_Bh1730 .GetInternalValue(): return I2cDevice_Bh1730; + case DeviceCode_Ina226VsysCore .GetInternalValue(): return I2cDevice_Ina226VsysCore; + case DeviceCode_Ina226Soc1V8 .GetInternalValue(): return I2cDevice_Ina226Soc1V8; + case DeviceCode_Ina226Lpddr1V8 .GetInternalValue(): return I2cDevice_Ina226Lpddr1V8; + case DeviceCode_Ina226Reg1V32 .GetInternalValue(): return I2cDevice_Ina226Reg1V32; + case DeviceCode_Ina226Vdd3V3Sys .GetInternalValue(): return I2cDevice_Ina226Vdd3V3Sys; + case DeviceCode_HdmiDdc .GetInternalValue(): return I2cDevice_HdmiDdc; + case DeviceCode_HdmiScdc .GetInternalValue(): return I2cDevice_HdmiScdc; + case DeviceCode_HdmiHdcp .GetInternalValue(): return I2cDevice_HdmiHdcp; + case DeviceCode_Fan53528 .GetInternalValue(): return I2cDevice_Fan53528; + case DeviceCode_Max77812_3 .GetInternalValue(): return I2cDevice_Max77812_3; + case DeviceCode_Max77812_2 .GetInternalValue(): return I2cDevice_Max77812_2; + case DeviceCode_Ina226VddDdr0V6 .GetInternalValue(): return I2cDevice_Ina226VddDdr0V6; + case DeviceCode_HoagNfcIc .GetInternalValue(): return I2cDevice_HoagNfcIc; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr bool IsPowerBusDeviceCode(DeviceCode device_code) { + switch (device_code.GetInternalValue()) { + case DeviceCode_Max77620Pmic.GetInternalValue(): + case DeviceCode_Max77812_3 .GetInternalValue(): + case DeviceCode_Max77621Cpu .GetInternalValue(): + case DeviceCode_Max77621Gpu .GetInternalValue(): + case DeviceCode_Fan53528 .GetInternalValue(): + case DeviceCode_Max77812_2 .GetInternalValue(): + case DeviceCode_Max77620Rtc .GetInternalValue(): + return true; + default: + return false; + } + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_register_accessor.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_register_accessor.hpp new file mode 100644 index 000000000..6ca145c56 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_register_accessor.hpp @@ -0,0 +1,56 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::i2c { + + template requires std::unsigned_integral + Result ReadSingleRegister(const I2cSession &session, u8 address, RegType *out) { + /* Check pre-conditions. */ + AMS_ABORT_UNLESS(out != nullptr); + + constexpr i2c::TransactionOption StartOption = i2c::TransactionOption_StartCondition; + constexpr i2c::TransactionOption StopOption = static_cast(i2c::TransactionOption_StartCondition | i2c::TransactionOption_StopCondition); + + u8 cmd_list[CommandListLengthMax]; + i2c::CommandListFormatter formatter(cmd_list, sizeof(cmd_list)); + + R_TRY(formatter.EnqueueSendCommand(StartOption, std::addressof(address), sizeof(address))); + R_TRY(formatter.EnqueueReceiveCommand(StopOption, sizeof(*out))); + + R_TRY(i2c::ExecuteCommandList(out, sizeof(*out), session, cmd_list, formatter.GetCurrentLength())); + + return ResultSuccess(); + } + + template requires std::unsigned_integral + Result WriteSingleRegister(const I2cSession &session, u8 address, RegType value) { + /* Prepare buffer. */ + u8 buf[sizeof(address) + sizeof(value)]; + std::memcpy(buf + 0, std::addressof(address), sizeof(address)); + std::memcpy(buf + sizeof(address), std::addressof(value), sizeof(value)); + + constexpr i2c::TransactionOption StopOption = static_cast(i2c::TransactionOption_StartCondition | i2c::TransactionOption_StopCondition); + R_TRY(i2c::Send(session, buf, sizeof(buf), StopOption)); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_select_device_name.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_select_device_name.hpp new file mode 100644 index 000000000..90c7db197 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_select_device_name.hpp @@ -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 . + */ +#pragma once +#include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + #include +#else + /* Error? */ +#endif diff --git a/libraries/libstratosphere/include/stratosphere/i2c/i2c_types.hpp b/libraries/libstratosphere/include/stratosphere/i2c/i2c_types.hpp new file mode 100644 index 000000000..17bfc429d --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/i2c_types.hpp @@ -0,0 +1,40 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::i2c { + + enum TransactionOption : u32 { + TransactionOption_StartCondition = (1u << 0), + TransactionOption_StopCondition = (1u << 1), + TransactionOption_MaxBits = (1u << 30), + }; + + enum AddressingMode : u32 { + AddressingMode_SevenBit = 0, + }; + + enum SpeedMode : u32 { + SpeedMode_Standard = 100000, + SpeedMode_Fast = 400000, + SpeedMode_FastPlus = 1000000, + SpeedMode_HighSpeed = 3400000, + }; + + using I2cCommand = u8; + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/server/i2c_server_api.hpp b/libraries/libstratosphere/include/stratosphere/i2c/server/i2c_server_api.hpp new file mode 100644 index 000000000..d307cd720 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/server/i2c_server_api.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::i2c::server { + + std::shared_ptr GetServiceObject(); + std::shared_ptr GetServiceObjectPowerBus(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/sf/i2c_sf_i_manager.hpp b/libraries/libstratosphere/include/stratosphere/i2c/sf/i2c_sf_i_manager.hpp new file mode 100644 index 000000000..38da3df7e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/sf/i2c_sf_i_manager.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include +#include + +namespace ams::i2c::sf { + + #define AMS_I2C_I_MANAGER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, OpenSessionForDev, (ams::sf::Out> out, s32 bus_idx, u16 slave_address, i2c::AddressingMode addressing_mode, i2c::SpeedMode speed_mode) ) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, OpenSession, (ams::sf::Out> out, i2c::I2cDevice device) ) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, HasDevice, (ams::sf::Out out, i2c::I2cDevice device), hos::Version_Min, hos::Version_5_1_0) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, HasDeviceForDev, (ams::sf::Out out, i2c::I2cDevice device), hos::Version_Min, hos::Version_5_1_0) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, OpenSession2, (ams::sf::Out> out, DeviceCode device_code), hos::Version_6_0_0 ) + + AMS_SF_DEFINE_INTERFACE(IManager, AMS_I2C_I_MANAGER_INTERFACE_INFO) + +} diff --git a/libraries/libstratosphere/include/stratosphere/i2c/sf/i2c_sf_i_session.hpp b/libraries/libstratosphere/include/stratosphere/i2c/sf/i2c_sf_i_session.hpp new file mode 100644 index 000000000..605f4e2ba --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/i2c/sf/i2c_sf_i_session.hpp @@ -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 . + */ + +#pragma once +#include +#include + +namespace ams::i2c::sf { + + #define AMS_I2C_I_SESSION_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, SendOld, (const ams::sf::InBuffer &in_data, i2c::TransactionOption option), hos::Version_Min, hos::Version_5_1_0) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, ReceiveOld, (const ams::sf::OutBuffer &out_data, i2c::TransactionOption option), hos::Version_Min, hos::Version_5_1_0) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, ExecuteCommandListOld, (const ams::sf::OutBuffer &rcv_buf, const ams::sf::InPointerArray &command_list), hos::Version_Min, hos::Version_5_1_0) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, Send, (const ams::sf::InAutoSelectBuffer &in_data, i2c::TransactionOption option) ) \ + AMS_SF_METHOD_INFO(C, H, 11, Result, Receive, (const ams::sf::OutAutoSelectBuffer &out_data, i2c::TransactionOption option) ) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, ExecuteCommandList, (const ams::sf::OutAutoSelectBuffer &rcv_buf, const ams::sf::InPointerArray &command_list) ) \ + AMS_SF_METHOD_INFO(C, H, 13, Result, SetRetryPolicy, (s32 max_retry_count, s32 retry_interval_us), hos::Version_6_0_0 ) + + AMS_SF_DEFINE_INTERFACE(ISession, AMS_I2C_I_SESSION_INTERFACE_INFO) + +} diff --git a/libraries/libstratosphere/include/stratosphere/os.hpp b/libraries/libstratosphere/include/stratosphere/os.hpp index ea37812ac..59d5e34e1 100644 --- a/libraries/libstratosphere/include/stratosphere/os.hpp +++ b/libraries/libstratosphere/include/stratosphere/os.hpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libstratosphere/include/stratosphere/os/os_sdk_condition_variable.hpp b/libraries/libstratosphere/include/stratosphere/os/os_sdk_condition_variable.hpp new file mode 100644 index 000000000..3d5e38b8a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/os/os_sdk_condition_variable.hpp @@ -0,0 +1,74 @@ +/* + * 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 . + */ + +#pragma once +#include +#include +#include + +namespace ams::os { + + struct SdkConditionVariableType { + union { + s32 _arr[sizeof(impl::InternalConditionVariableStorage) / sizeof(s32)]; + impl::InternalConditionVariableStorage _storage; + }; + + void Initialize() { + GetReference(this->_storage).Initialize(); + } + + void Wait(SdkMutexType &mutex); + bool TimedWait(SdkMutexType &mutex, TimeSpan timeout); + + /* TODO: SdkRecursiveMutexType */ + + void Signal() { + GetReference(this->_storage).Signal(); + } + + void Broadcast() { + GetReference(this->_storage).Broadcast(); + } + }; + static_assert(std::is_trivial::value); + + class SdkConditionVariable { + private: + SdkConditionVariableType cv; + public: + constexpr SdkConditionVariable() : cv{{0}} { /* ... */ } + + void Wait(SdkMutex &m) { + return this->cv.Wait(m.mutex); + } + + bool TimedWait(SdkMutex &m, TimeSpan timeout) { + return this->cv.TimedWait(m.mutex, timeout); + } + + /* TODO: SdkRecursiveMutexType */ + + void Signal() { + return this->cv.Signal(); + } + + void Broadcast() { + return this->cv.Broadcast(); + } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/pcv.hpp b/libraries/libstratosphere/include/stratosphere/pcv.hpp new file mode 100644 index 000000000..a7c59ad8c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pcv.hpp @@ -0,0 +1,20 @@ +/* + * 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 . + */ + +#pragma once + +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/pcv/pcv_api.hpp b/libraries/libstratosphere/include/stratosphere/pcv/pcv_api.hpp new file mode 100644 index 000000000..01643d09c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pcv/pcv_api.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 . + */ +#pragma once +#include +#include + +namespace ams::pcv { + + void Initialize(); + void Finalize(); + + Result SetClockEnabled(Module module, bool en); + Result SetClockRate(Module module, ClockHz hz); + + Result SetReset(Module module, bool en); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pcv/pcv_types.hpp b/libraries/libstratosphere/include/stratosphere/pcv/pcv_types.hpp new file mode 100644 index 000000000..114976da2 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pcv/pcv_types.hpp @@ -0,0 +1,116 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::pcv { + + using ClockHz = u32; + using MicroVolt = s32; + using MilliC = s32; + + /* TODO: Device codes? */ + enum Module { + Module_Cpu = 0, + Module_Gpu = 1, + Module_I2s1 = 2, + Module_I2s2 = 3, + Module_I2s3 = 4, + Module_Pwm = 5, + Module_I2c1 = 6, + Module_I2c2 = 7, + Module_I2c3 = 8, + Module_I2c4 = 9, + Module_I2c5 = 10, + Module_I2c6 = 11, + Module_Spi1 = 12, + Module_Spi2 = 13, + Module_Spi3 = 14, + Module_Spi4 = 15, + Module_Disp1 = 16, + Module_Disp2 = 17, + Module_Isp = 18, + Module_Vi = 19, + Module_Sdmmc1 = 20, + Module_Sdmmc2 = 21, + Module_Sdmmc3 = 22, + Module_Sdmmc4 = 23, + Module_Owr = 24, + Module_Csite = 25, + Module_Tsec = 26, + Module_Mselect = 27, + Module_Hda2codec2x = 28, + Module_Actmon = 29, + Module_I2cSlow = 30, + Module_Sor1 = 31, + Module_Sata = 32, + Module_Hda = 33, + Module_XusbCoreHostSrc = 34, + Module_XusbFalconSrc = 35, + Module_XusbFsSrc = 36, + Module_XusbCoreDevSrc = 37, + Module_XusbSsSrc = 38, + Module_UartA = 39, + Module_UartB = 40, + Module_UartC = 41, + Module_UartD = 42, + Module_Host1x = 43, + Module_Entropy = 44, + Module_SocTherm = 45, + Module_Vic = 46, + Module_Nvenc = 47, + Module_Nvjpg = 48, + Module_Nvdec = 49, + Module_Qspi = 50, + Module_ViI2c = 51, + Module_Tsecb = 52, + Module_Ape = 53, + Module_AudioDsp = 54, + Module_AudioUart = 55, + Module_Emc = 56, + Module_Plle = 57, + Module_PlleHwSeq = 58, + Module_Dsi = 59, + Module_Maud = 60, + Module_Dpaux1 = 61, + Module_MipiCal = 62, + Module_UartFstMipiCal = 63, + Module_Osc = 64, + Module_SysBus = 65, + Module_SorSafe = 66, + Module_XusbSs = 67, + Module_XusbHost = 68, + Module_XusbDevice = 69, + Module_Extperiph1 = 70, + Module_Ahub = 71, + Module_Hda2hdmicodec = 72, + Module_Gpuaux = 73, + Module_UsbD = 74, + Module_Usb2 = 75, + Module_Pcie = 76, + Module_Afi = 77, + Module_PciExClk = 78, + Module_PExUsbPhy = 79, + Module_XUsbPadCtl = 80, + Module_Apbdma = 81, + Module_Usb2TrkClk = 82, + Module_XUsbIoPll = 83, + Module_XUsbIoPllHwSeq = 84, + Module_Cec = 85, + Module_Extperiph2 = 86, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/pinmux.hpp b/libraries/libstratosphere/include/stratosphere/pinmux.hpp new file mode 100644 index 000000000..e4de62cf1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pinmux.hpp @@ -0,0 +1,20 @@ +/* + * 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 . + */ + +#pragma once + +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/pinmux/driver/pinmux_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/pinmux/driver/pinmux_driver_api.hpp new file mode 100644 index 000000000..25618ffc3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pinmux/driver/pinmux_driver_api.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::pinmux::driver { + + void Initialize(); + void Finalize(); + + void SetInitialConfig(); + void SetInitialDrivePadConfig(); + +} diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration.hpp b/libraries/libstratosphere/include/stratosphere/pinmux/pinmux_api.hpp similarity index 91% rename from stratosphere/boot/source/pinmux/pinmux_initial_configuration.hpp rename to libraries/libstratosphere/include/stratosphere/pinmux/pinmux_api.hpp index e3052e157..d81fbca7a 100644 --- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration.hpp +++ b/libraries/libstratosphere/include/stratosphere/pinmux/pinmux_api.hpp @@ -14,10 +14,10 @@ * along with this program. If not, see . */ #pragma once -#include +#include namespace ams::pinmux { - void SetInitialConfiguration(); + /* ... */ } diff --git a/libraries/libstratosphere/include/stratosphere/powctl.hpp b/libraries/libstratosphere/include/stratosphere/powctl.hpp new file mode 100644 index 000000000..3f293ccd5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_charge_arbiter.hpp b/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_charge_arbiter.hpp new file mode 100644 index 000000000..5aad9f116 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_charge_arbiter.hpp @@ -0,0 +1,161 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::powctl::driver::impl { + + class ChargeArbiter { + private: + const ChargeParametersRule *rules; + size_t num_rules; + int charge_voltage_limit; + BatteryTemperatureLevel temperature_level; + int avg_v_cell; + int open_circuit_voltage; + bool has_battery_done_current; + int battery_done_current; + PowerState power_state; + const ChargeParametersRule *selected_rule; + bool check_battery_done_current; + private: + static constexpr bool IsInRange(int value, int min, int max) { + if (!(min <= value)) { + return false; + } + + if (max == std::numeric_limits::max()) { + return value <= max; + } else { + return value < max; + } + } + + bool IsAcceptablePowerState(const PowerState *acceptable, size_t num_acceptable) const { + for (size_t i = 0; i < num_acceptable; ++i) { + if (this->power_state == acceptable[i]) { + return true; + } + } + return false; + } + public: + ChargeArbiter(const ChargeParametersRule *r, size_t nr, int cvl) + : rules(r), num_rules(nr), charge_voltage_limit(cvl), temperature_level(BatteryTemperatureLevel::Medium), + avg_v_cell(4080), open_circuit_voltage(4001), has_battery_done_current(false), battery_done_current(0), + power_state(PowerState::FullAwake), selected_rule(nullptr), check_battery_done_current(false) + { + this->UpdateSelectedRule(); + } + + void SetBatteryTemperatureLevel(BatteryTemperatureLevel btl) { + this->temperature_level = btl; + this->UpdateSelectedRule(); + } + + void SetBatteryAverageVCell(int avg) { + this->avg_v_cell = avg; + this->UpdateSelectedRule(); + } + + void SetBatteryOpenCircuitVoltage(int ocv) { + this->open_circuit_voltage = ocv; + this->UpdateSelectedRule(); + } + + void SetBatteryDoneCurrent(int current) { + this->battery_done_current = current; + this->has_battery_done_current = true; + this->UpdateSelectedRule(); + } + + void SetPowerState(PowerState ps) { + this->power_state = ps; + this->UpdateSelectedRule(); + } + + int GetChargeVoltageLimit() const { + return this->charge_voltage_limit; + } + + bool IsBatteryDoneCurrentAcceptable(int current) const { + const auto *rule = this->GetSelectedRule(); + AMS_ASSERT(rule != nullptr); + + return IsInRange(0, rule->min_battery_done_current, rule->max_battery_done_current); + } + + const ChargeParametersRule *GetSelectedRule() const { + return this->selected_rule; + } + + void UpdateSelectedRule() { + /* Try to find an entry that fits our current requirements. */ + const ChargeParametersRule *best_rule = nullptr; + for (size_t i = 0; i < this->num_rules; ++i) { + /* Get the current rule. */ + const ChargeParametersRule &cur_rule = this->rules[i]; + + /* Check the temperature level. */ + if (this->temperature_level != cur_rule.temperature_level) { + continue; + } + + /* Check that average voltage is in range. */ + if (!IsInRange(this->avg_v_cell, cur_rule.min_avg_v_cell, cur_rule.max_avg_v_cell)) { + continue; + } + + /* Check that open circuit voltage is in range. */ + if (!IsInRange(this->open_circuit_voltage, cur_rule.min_open_circuit_voltage, cur_rule.max_open_circuit_voltage)) { + continue; + } + + /* Check if our power state is acceptable. */ + if (!this->IsAcceptablePowerState(cur_rule.acceptable_power_states, cur_rule.num_acceptable_power_states)) { + continue; + } + + /* The limit is probably acceptable. */ + if (this->selected_rule != std::addressof(cur_rule)) { + /* We're selecting a new rule. Check if our need to deal with battery current is acceptable. */ + if (cur_rule.check_battery_current && this->check_battery_done_current) { + continue; + } + + /* Set whether we need to check the battery done current. */ + this->has_battery_done_current = false; + this->check_battery_done_current |= cur_rule.check_battery_current; + } else { + /* We're selecting the currently selected rule. Make sure the battery done current is acceptable if we have one. */ + if (this->has_battery_done_current && !IsInRange(this->battery_done_current, cur_rule.min_battery_done_current, cur_rule.max_battery_done_current)) { + continue; + } + } + + /* Select the current rule. */ + best_rule = std::addressof(cur_rule); + break; + } + + /* Update our selected rule. */ + this->selected_rule = best_rule; + } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.hpp b/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.hpp new file mode 100644 index 000000000..ed517587a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.hpp @@ -0,0 +1,64 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::powctl::driver::impl { + + struct ChargeParametersRule { + BatteryTemperatureLevel temperature_level; + int min_avg_v_cell; + int max_avg_v_cell; + int min_open_circuit_voltage; + int max_open_circuit_voltage; + int min_battery_done_current; + int max_battery_done_current; + const PowerState *acceptable_power_states; + size_t num_acceptable_power_states; + bool check_battery_current; + bool reinitialize_charger; + int charge_voltage_limit; + int fast_charge_current_limit; + int battery_compensation; + int voltage_clamp; + }; + + struct UnknownParameterX { + int _00; + int _04; + double _08; + double _10; + }; + + struct ChargeParameters { + int temp_min; + int temp_low; + int temp_high; + int temp_max; + int low_voltage_fast_charge_current_limit; + int default_charge_voltage_limit; + const UnknownParameterX *unknown_x_table; + size_t x_table_size; + double _28; + double _30; + const ChargeParametersRule *rules; + size_t num_rules; + }; + + const ChargeParameters &GetChargeParameters(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_select_charger_parameters.hpp b/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_select_charger_parameters.hpp new file mode 100644 index 000000000..80ba52636 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/driver/impl/powctl_select_charger_parameters.hpp @@ -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 . + */ +#pragma once +#include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include + +#else + #error "unknown board for powctl::driver::impl::ChargerParameters" +#endif \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/powctl/driver/powctl_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/powctl/driver/powctl_driver_api.hpp new file mode 100644 index 000000000..7ed561a78 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/driver/powctl_driver_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::powctl { + + void Initialize(bool enable_interrupt_handlers); + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/powctl/impl/powctl_battery_charge_percentage.hpp b/libraries/libstratosphere/include/stratosphere/powctl/impl/powctl_battery_charge_percentage.hpp new file mode 100644 index 000000000..0ca9951ea --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/impl/powctl_battery_charge_percentage.hpp @@ -0,0 +1,65 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::powctl::impl { + + constexpr inline const double MinRawDefaultPercentage = 3.0; + constexpr inline const double MaxRawDefaultPercentage = 99.0; + + constexpr inline const double MinRawThresholdPercentage = 11.0; + + constexpr inline const int MinDisplayPercentage = 1; + constexpr inline const int MaxDisplayPercentage = 100; + + constexpr inline void CalculateMarginatedRawPercentage(double *out_marginated_min, double *out_marginated_max, double min, double max) { + /* Ensure minimum is in correct range. */ + min = std::max(std::min(min, MinRawThresholdPercentage), MinRawDefaultPercentage); + + /* Calculate the marginated values. */ + constexpr const double MinMarginPercentage = 0.93359375; + constexpr const double MaxMarginPercentage = -0.83593750; + + const auto margin_factor = (max - min) / (MaxRawDefaultPercentage - MinRawDefaultPercentage); + *out_marginated_min = min + MinMarginPercentage * margin_factor; + *out_marginated_max = max + MaxMarginPercentage * margin_factor; + } + + constexpr inline int GetDisplayPercentage(double raw_percentage, double min, double max) { + /* Calculate the display percentage. */ + constexpr const double BaseDisplayPercentage = 2.0; + const auto display_percentage = BaseDisplayPercentage + ((static_cast(MaxDisplayPercentage - MinDisplayPercentage) * (raw_percentage - min)) / (max - min)); + + /* Clamp the display percentage within bounds. */ + return std::max(std::min(static_cast(display_percentage), MaxDisplayPercentage), MinDisplayPercentage); + } + + constexpr inline int ConvertBatteryChargePercentage(double raw_percentage, double min, double max) { + /* Marginate the min/max. */ + double marginated_min = 0.0, marginated_max = 0.0; + CalculateMarginatedRawPercentage(std::addressof(marginated_min), std::addressof(marginated_max), min, max); + + /* Convert to display percentage. */ + return GetDisplayPercentage(raw_percentage, marginated_min, marginated_max); + } + + constexpr inline int ConvertBatteryChargePercentage(double raw_percentage) { + return ConvertBatteryChargePercentage(raw_percentage, MinRawDefaultPercentage, MaxRawDefaultPercentage); + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/powctl/powctl_battery_api.hpp b/libraries/libstratosphere/include/stratosphere/powctl/powctl_battery_api.hpp new file mode 100644 index 000000000..60dd0788f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/powctl_battery_api.hpp @@ -0,0 +1,70 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::powctl { + + /* Battery API. */ + Result GetBatterySocRep(float *out_percent, Session &session); + + Result GetBatterySocVf(float *out_percent, Session &session); + + Result GetBatteryFullCapacity(int *out_mah, Session &session); + Result GetBatteryRemainingCapacity(int *out_mah, Session &session); + + Result SetBatteryPercentageMinimumAlertThreshold(Session &session, float percentage); + Result SetBatteryPercentageMaximumAlertThreshold(Session &session, float percentage); + Result SetBatteryPercentageFullThreshold(Session &session, float percentage); + + Result GetBatteryAverageCurrent(int *out_ma, Session &session); + Result GetBatteryCurrent(int *out_ma, Session &session); + + Result GetBatteryInternalState(void *dst, size_t *out_size, Session &session, size_t dst_size); + Result SetBatteryInternalState(Session &session, const void *src, size_t src_size); + + Result GetBatteryNeedToRestoreParameters(bool *out, Session &session); + Result SetBatteryNeedToRestoreParameters(Session &session, bool en); + + Result IsBatteryI2cShutdownEnabled(bool *out, Session &session); + Result SetBatteryI2cShutdownEnabled(Session &session, bool en); + + Result IsBatteryPresent(bool *out, Session &session); + + Result GetBatteryCycles(int *out, Session &session); + Result SetBatteryCycles(Session &session, int cycles); + + Result GetBatteryAge(float *out_percent, Session &session); + + Result GetBatteryTemperature(float *out_c, Session &session); + Result GetBatteryMaximumTemperature(float *out_c, Session &session); + + Result SetBatteryTemperatureMinimumAlertThreshold(Session &session, float c); + Result SetBatteryTemperatureMaximumAlertThreshold(Session &session, float c); + + Result GetBatteryVCell(int *out_mv, Session &session); + Result GetBatteryAverageVCell(int *out_mv, Session &session); + + Result GetBatteryAverageVCellTime(TimeSpan *out, Session &session); + + Result GetBatteryOpenCircuitVoltage(int *out_mv, Session &session); + + Result SetBatteryVoltageMinimumAlertThreshold(Session &session, int mv); + Result SetBatteryVoltageMaximumAlertThreshold(Session &session, int mv); + +} diff --git a/libraries/libstratosphere/include/stratosphere/powctl/powctl_charger_api.hpp b/libraries/libstratosphere/include/stratosphere/powctl/powctl_charger_api.hpp new file mode 100644 index 000000000..e69afb0ef --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/powctl_charger_api.hpp @@ -0,0 +1,59 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::powctl { + + /* Charger API. */ + Result GetChargerChargeCurrentState(ChargeCurrentState *out, Session &session); + Result SetChargerChargeCurrentState(Session &session, ChargeCurrentState state); + + Result GetChargerFastChargeCurrentLimit(int *out_ma, Session &session); + Result SetChargerFastChargeCurrentLimit(Session &session, int ma); + + Result GetChargerChargeVoltageLimit(int *out_mv, Session &session); + Result SetChargerChargeVoltageLimit(Session &session, int mv); + + Result SetChargerChargerConfiguration(Session &session, ChargerConfiguration cfg); + + Result IsChargerHiZEnabled(bool *out, Session &session); + Result SetChargerHiZEnabled(Session &session, bool en); + + Result GetChargerInputCurrentLimit(int *out_ma, Session &session); + Result SetChargerInputCurrentLimit(Session &session, int ma); + + Result SetChargerInputVoltageLimit(Session &session, int mv); + + Result SetChargerBoostModeCurrentLimit(Session &session, int ma); + + Result GetChargerChargerStatus(ChargerStatus *out, Session &session); + + Result IsChargerWatchdogTimerEnabled(bool *out, Session &session); + Result SetChargerWatchdogTimerEnabled(Session &session, bool en); + + Result SetChargerWatchdogTimerTimeout(Session &session, TimeSpan timeout); + Result ResetChargerWatchdogTimer(Session &session); + + Result GetChargerBatteryCompensation(int *out_mo, Session &session); + Result SetChargerBatteryCompensation(Session &session, int mo); + + Result GetChargerVoltageClamp(int *out_mv, Session &session); + Result SetChargerVoltageClamp(Session &session, int mv); + +} diff --git a/libraries/libstratosphere/include/stratosphere/powctl/powctl_devices.board.nintendo_nx.hpp b/libraries/libstratosphere/include/stratosphere/powctl/powctl_devices.board.nintendo_nx.hpp new file mode 100644 index 000000000..c2236fbfe --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/powctl_devices.board.nintendo_nx.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::powctl { + + /* Fuel Gauge. */ + constexpr inline const DeviceCode DeviceCode_Max17050 = i2c::DeviceCode_Max17050; + + /* Charger. */ + constexpr inline const DeviceCode DeviceCode_Bq24193 = i2c::DeviceCode_Bq24193; + +} diff --git a/libraries/libstratosphere/include/stratosphere/powctl/powctl_select_devices.hpp b/libraries/libstratosphere/include/stratosphere/powctl/powctl_select_devices.hpp new file mode 100644 index 000000000..611312f80 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/powctl_select_devices.hpp @@ -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 . + */ +#pragma once +#include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + #include +#else + /* Error? */ +#endif diff --git a/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp b/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp new file mode 100644 index 000000000..7c0e3fbd6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/powctl_session_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::powctl { + + namespace impl { + + class SessionImpl : public ::ams::ddsf::ISession { + NON_COPYABLE(SessionImpl); + NON_MOVEABLE(SessionImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::SessionImpl, ::ams::ddsf::ISession); + public: + SessionImpl() : ISession() { /* ... */ } + + ~SessionImpl() { ddsf::CloseSession(this); } + }; + + } + + struct Session { + bool has_session; + TYPED_STORAGE(impl::SessionImpl) impl_storage; + + Session() : has_session(false) { /* ... */ } + }; + + Result OpenSession(Session *out, DeviceCode device_code, ddsf::AccessMode access_mode); + void CloseSession(Session &session); + +} diff --git a/libraries/libstratosphere/include/stratosphere/powctl/powctl_types.hpp b/libraries/libstratosphere/include/stratosphere/powctl/powctl_types.hpp new file mode 100644 index 000000000..ebb26ec2c --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/powctl/powctl_types.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::powctl { + + /* Charger types. */ + enum ChargerStatus { + ChargerStatus_Charging = 1, + + ChargerStatus_NotCharging = 3, + ChargerStatus_ChargeTerminationDone = 4, + }; + + enum ChargerConfiguration { + ChargerConfiguration_ChargeDisable = 1, + ChargerConfiguration_ChargeBattery = 2, + ChargerConfiguration_Otg = 3, + }; + + enum ChargeCurrentState { + ChargeCurrentState_Unknown = 0x0, + ChargeCurrentState_NotCharging = 0x1, + ChargeCurrentState_ChargingForce20Percent = 0x2, + ChargeCurrentState_Charging = 0x3, + }; + + enum class BatteryTemperatureLevel { + TooLow = 0, + Low = 1, + Medium = 2, + High = 3, + TooHigh = 4, + }; + + enum class PowerState { + FullAwake = 0, + MinimumAwake = 1, + SleepCharge = 2, + SleepDischarge = 3, + ShutdownChargeMain = 4, + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/pwm.hpp b/libraries/libstratosphere/include/stratosphere/pwm.hpp new file mode 100644 index 000000000..99aefb4b3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo_nx/pwm_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo_nx/pwm_driver_api.hpp new file mode 100644 index 000000000..0a9b0030e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/board/nintendo_nx/pwm_driver_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::pwm::driver::board::nintendo_nx { + + void Initialize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_channel_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_channel_api.hpp new file mode 100644 index 000000000..312ecd224 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_channel_api.hpp @@ -0,0 +1,50 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::pwm::driver { + + namespace impl { + + constexpr inline size_t ChannelSessionSize = 0x60; + constexpr inline size_t ChannelSessionAlign = 8; + struct alignas(ChannelSessionAlign) ChannelSessionImplPadded; + + } + + struct ChannelSession { + util::TypedStorage _impl; + }; + + Result OpenSession(ChannelSession *out, DeviceCode device_code); + void CloseSession(ChannelSession &session); + + void SetPeriod(ChannelSession &session, TimeSpan period); + TimeSpan GetPeriod(ChannelSession &session); + + void SetDuty(ChannelSession &session, int duty); + int GetDuty(ChannelSession &session); + + void SetEnabled(ChannelSession &session, bool en); + bool GetEnabled(ChannelSession &session); + + void SetScale(ChannelSession &session, double scale); + double GetScale(ChannelSession &session); + +} + diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_driver_client_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_driver_client_api.hpp new file mode 100644 index 000000000..6499bf7b0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_driver_client_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::pwm::driver { + + void Initialize(); + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_driver_service_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_driver_service_api.hpp new file mode 100644 index 000000000..715fff353 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_driver_service_api.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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::pwm::driver { + + void RegisterDriver(IPwmDriver *driver); + void UnregisterDriver(IPwmDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, IPwmDevice *device); + bool UnregisterDeviceCode(DeviceCode device_code); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_i_pwm_device.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_i_pwm_device.hpp new file mode 100644 index 000000000..74d41bdcc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_i_pwm_device.hpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::pwm::driver { + + class IPwmDevice : public ::ams::ddsf::IDevice { + NON_COPYABLE(IPwmDevice); + NON_MOVEABLE(IPwmDevice); + AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::IPwmDevice, ::ams::ddsf::IDevice); + private: + int channel_index; + public: + IPwmDevice(int id) : IDevice(false), channel_index(id) { /* ... */ } + virtual ~IPwmDevice() { /* ... */ } + + constexpr int GetChannelIndex() const { return this->channel_index; } + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_i_pwm_driver.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_i_pwm_driver.hpp new file mode 100644 index 000000000..355359758 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_i_pwm_driver.hpp @@ -0,0 +1,54 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include + +namespace ams::pwm::driver { + + class IPwmDriver : public ::ams::ddsf::IDriver { + NON_COPYABLE(IPwmDriver); + NON_MOVEABLE(IPwmDriver); + AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::IPwmDriver, ::ams::ddsf::IDriver); + public: + IPwmDriver() : IDriver() { /* ... */ } + virtual ~IPwmDriver() { /* ... */ } + + virtual void InitializeDriver() = 0; + virtual void FinalizeDriver() = 0; + + virtual Result InitializeDevice(IPwmDevice *device) = 0; + virtual void FinalizeDevice(IPwmDevice *device) = 0; + + virtual Result SetPeriod(IPwmDevice *device, TimeSpan period) = 0; + virtual Result GetPeriod(TimeSpan *out, IPwmDevice *device) = 0; + + virtual Result SetDuty(IPwmDevice *device, int duty) = 0; + virtual Result GetDuty(int *out, IPwmDevice *device) = 0; + + virtual Result SetScale(IPwmDevice *device, double scale) = 0; + virtual Result GetScale(double *out, IPwmDevice *device) = 0; + + virtual Result SetEnabled(IPwmDevice *device, bool en) = 0; + virtual Result GetEnabled(bool *out, IPwmDevice *device) = 0; + + virtual Result Suspend() = 0; + virtual void Resume() = 0; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_select_driver_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_select_driver_api.hpp new file mode 100644 index 000000000..c9fbec53f --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/driver/pwm_select_driver_api.hpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include + + namespace ams::pwm::driver::board { + + using namespace ams::pwm::driver::board::nintendo_nx; + + } + +#else + + #error "Unknown board for ams::pwm::driver::" + +#endif + diff --git a/libraries/libstratosphere/include/stratosphere/pwm/pwm_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/pwm_api.hpp new file mode 100644 index 000000000..44544fbdf --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/pwm_api.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::pwm { + + void InitializeWith(std::shared_ptr &&sp); + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/pwm_channel_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/pwm_channel_api.hpp new file mode 100644 index 000000000..da10659d1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/pwm_channel_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::pwm { + + struct ChannelSession { + void *_session; + }; + + Result OpenSession(ChannelSession *out, DeviceCode device_code); + void CloseSession(ChannelSession &session); + + void SetPeriod(ChannelSession &session, TimeSpan period); + TimeSpan GetPeriod(ChannelSession &session); + + void SetDuty(ChannelSession &session, int duty); + int GetDuty(ChannelSession &session); + + void SetEnabled(ChannelSession &session, bool en); + bool GetEnabled(ChannelSession &session); + + void SetScale(ChannelSession &session, double scale); + double GetScale(ChannelSession &session); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/pwm_channel_name.board.nintendo_nx.hpp b/libraries/libstratosphere/include/stratosphere/pwm/pwm_channel_name.board.nintendo_nx.hpp new file mode 100644 index 000000000..a6c3c7e9b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/pwm_channel_name.board.nintendo_nx.hpp @@ -0,0 +1,51 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::pwm { + + enum ChannelName { + ChannelName_Invalid = 0, + ChannelName_CpuFan = 1, + ChannelName_LcdBacklight = 2, + ChannelName_BlinkLed = 3, + }; + + constexpr inline const DeviceCode DeviceCode_CpuFan = 0x3D000001; + constexpr inline const DeviceCode DeviceCode_LcdBacklight = 0x3400003D; + constexpr inline const DeviceCode DeviceCode_BlinkLed = 0x35000065; + + constexpr inline DeviceCode ConvertToDeviceCode(ChannelName cn) { + switch (cn) { + case ChannelName_CpuFan: return DeviceCode_CpuFan; + case ChannelName_LcdBacklight: return DeviceCode_LcdBacklight; + case ChannelName_BlinkLed: return DeviceCode_BlinkLed; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr inline ChannelName ConvertToChannelName(DeviceCode dc) { + switch (dc.GetInternalValue()) { + case DeviceCode_CpuFan .GetInternalValue(): return ChannelName_CpuFan; + case DeviceCode_LcdBacklight.GetInternalValue(): return ChannelName_LcdBacklight; + case DeviceCode_BlinkLed .GetInternalValue(): return ChannelName_BlinkLed; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/pwm_select_channel_name.hpp b/libraries/libstratosphere/include/stratosphere/pwm/pwm_select_channel_name.hpp new file mode 100644 index 000000000..58c63028b --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/pwm_select_channel_name.hpp @@ -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 . + */ +#pragma once +#include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + #include +#else + /* Error? */ +#endif diff --git a/libraries/libstratosphere/include/stratosphere/pwm/pwm_types.hpp b/libraries/libstratosphere/include/stratosphere/pwm/pwm_types.hpp new file mode 100644 index 000000000..9130e123a --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/pwm_types.hpp @@ -0,0 +1,23 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::pwm { + + /* ... */ + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/server/pwm_server_api.hpp b/libraries/libstratosphere/include/stratosphere/pwm/server/pwm_server_api.hpp new file mode 100644 index 000000000..0024a54a8 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/server/pwm_server_api.hpp @@ -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 . + */ +#pragma once +#include +#include +#include + +namespace ams::pwm::server { + + std::shared_ptr GetServiceObject(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/sf/pwm_sf_i_channel_session.hpp b/libraries/libstratosphere/include/stratosphere/pwm/sf/pwm_sf_i_channel_session.hpp new file mode 100644 index 000000000..f61c15ea0 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/sf/pwm_sf_i_channel_session.hpp @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +#pragma once +#include +#include + +namespace ams::pwm::sf { + + #define AMS_PWM_I_CHANNEL_SESSION_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, SetPeriod, (TimeSpanType period) ) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, GetPeriod, (ams::sf::Out out) ) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, SetDuty, (int duty) ) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, GetDuty, (ams::sf::Out out) ) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, SetEnabled, (bool enabled) ) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, GetEnabled, (ams::sf::Out out) ) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, SetScale, (double scale), hos::Version_6_0_0) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, GetScale, (ams::sf::Out out), hos::Version_6_0_0) + + AMS_SF_DEFINE_INTERFACE(IChannelSession, AMS_PWM_I_CHANNEL_SESSION_INTERFACE_INFO) + +} diff --git a/libraries/libstratosphere/include/stratosphere/pwm/sf/pwm_sf_i_manager.hpp b/libraries/libstratosphere/include/stratosphere/pwm/sf/pwm_sf_i_manager.hpp new file mode 100644 index 000000000..5c4e47f82 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/pwm/sf/pwm_sf_i_manager.hpp @@ -0,0 +1,32 @@ +/* + * 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 . + */ + +#pragma once +#include +#include +#include +#include + +namespace ams::pwm::sf { + + #define AMS_PWM_I_MANAGER_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, OpenSessionForDev, (ams::sf::Out> out, int channel) ) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, OpenSession, (ams::sf::Out> out, pwm::ChannelName channel_name) ) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, OpenSession2, (ams::sf::Out> out, DeviceCode device_code), hos::Version_6_0_0) + + AMS_SF_DEFINE_INTERFACE(IManager, AMS_PWM_I_MANAGER_INTERFACE_INFO) + +} diff --git a/libraries/libstratosphere/include/stratosphere/reg.hpp b/libraries/libstratosphere/include/stratosphere/reg.hpp deleted file mode 100644 index 6b5076f93..000000000 --- a/libraries/libstratosphere/include/stratosphere/reg.hpp +++ /dev/null @@ -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 . - */ - -#pragma once -#include - -namespace ams::reg { - - inline void Write(volatile u32 *reg, u32 val) { - *reg = val; - } - - inline void Write(uintptr_t reg, u32 val) { - Write(reinterpret_cast(reg), val); - } - - inline u32 Read(volatile u32 *reg) { - return *reg; - } - - inline u32 Read(uintptr_t reg) { - return Read(reinterpret_cast(reg)); - } - - inline void SetBits(volatile u32 *reg, u32 mask) { - *reg = *reg | mask; - } - - inline void SetBits(uintptr_t reg, u32 mask) { - SetBits(reinterpret_cast(reg), mask); - } - - inline void ClearBits(volatile u32 *reg, u32 mask) { - *reg = *reg & ~mask; - } - - inline void ClearBits(uintptr_t reg, u32 mask) { - ClearBits(reinterpret_cast(reg), mask); - } - - inline void MaskBits(volatile u32 *reg, u32 mask) { - *reg = *reg & mask; - } - - inline void MaskBits(uintptr_t reg, u32 mask) { - MaskBits(reinterpret_cast(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(reg), val, mask); - } - -} \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/regulator.hpp b/libraries/libstratosphere/include/stratosphere/regulator.hpp new file mode 100644 index 000000000..fbd6c24dc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/regulator.hpp @@ -0,0 +1,21 @@ +/* + * 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 . + */ + +#pragma once + +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/regulator/regulator_api.hpp b/libraries/libstratosphere/include/stratosphere/regulator/regulator_api.hpp new file mode 100644 index 000000000..36623c8fc --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/regulator/regulator_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::regulator { + + void Initialize(); + void Finalize(); + +} diff --git a/libraries/libstratosphere/include/stratosphere/regulator/regulator_session_api.hpp b/libraries/libstratosphere/include/stratosphere/regulator/regulator_session_api.hpp new file mode 100644 index 000000000..cdd278ad4 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/regulator/regulator_session_api.hpp @@ -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 . + */ +#pragma once +#include +#include + +namespace ams::regulator { + + struct RegulatorSession { + void *_session; + }; + + Result OpenSession(RegulatorSession *out, DeviceCode device_code); + void CloseSession(RegulatorSession *session); + + bool GetVoltageEnabled(RegulatorSession *session); + Result SetVoltageEnabled(RegulatorSession *session, bool enabled); + + Result SetVoltageValue(RegulatorSession *session, u32 micro_volts); + +} diff --git a/libraries/libstratosphere/include/stratosphere/regulator/regulator_types.hpp b/libraries/libstratosphere/include/stratosphere/regulator/regulator_types.hpp new file mode 100644 index 000000000..ad3894176 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/regulator/regulator_types.hpp @@ -0,0 +1,23 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::regulator { + + /* ... */ + +} diff --git a/libraries/libstratosphere/include/stratosphere/ro/impl/ro_ro_interface.hpp b/libraries/libstratosphere/include/stratosphere/ro/impl/ro_ro_interface.hpp index 624d1e5d8..185293c16 100644 --- a/libraries/libstratosphere/include/stratosphere/ro/impl/ro_ro_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/ro/impl/ro_ro_interface.hpp @@ -27,7 +27,7 @@ namespace ams::ro::impl { AMS_SF_METHOD_INFO(C, H, 2, Result, RegisterModuleInfo, (const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size)) \ AMS_SF_METHOD_INFO(C, H, 3, Result, UnregisterModuleInfo, (const sf::ClientProcessId &client_pid, u64 nrr_address)) \ AMS_SF_METHOD_INFO(C, H, 4, Result, RegisterProcessHandle, (const sf::ClientProcessId &client_pid, sf::CopyHandle process_h)) \ - AMS_SF_METHOD_INFO(C, H, 10, Result, RegisterModuleInfoEx, (const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle process_h), hos::Version_7_0_0) + AMS_SF_METHOD_INFO(C, H, 10, Result, RegisterProcessModuleInfo, (const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle process_h), hos::Version_7_0_0) AMS_SF_DEFINE_INTERFACE(IRoInterface, AMS_RO_I_RO_INTERFACE_INTERFACE_INFO) diff --git a/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp b/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp index eafde873c..73aca6e33 100644 --- a/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ro/ro_types.hpp @@ -20,10 +20,11 @@ namespace ams::ro { - enum class ModuleType : u8 { - ForSelf = 0, - ForOthers = 1, - Count + enum NrrKind : u8 { + NrrKind_User = 0, + NrrKind_JitPlugin = 1, + + NrrKind_Count, }; struct ModuleId { @@ -54,7 +55,7 @@ namespace ams::ro { u8 signature[0x100]; ncm::ProgramId program_id; u32 size; - u8 type; /* 7.0.0+ */ + u8 nrr_kind; /* 7.0.0+ */ u8 reserved_33D[3]; u32 hashes_offset; u32 num_hashes; @@ -68,10 +69,10 @@ namespace ams::ro { return (static_cast(this->program_id) & this->certification.program_id_mask) == this->certification.program_id_pattern; } - ModuleType GetType() const { - const ModuleType type = static_cast(this->type); - AMS_ABORT_UNLESS(type < ModuleType::Count); - return type; + NrrKind GetNrrKind() const { + const NrrKind kind = static_cast(this->nrr_kind); + AMS_ABORT_UNLESS(kind < NrrKind_Count); + return kind; } ncm::ProgramId GetProgramId() const { diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_lmem_utility.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_lmem_utility.hpp index cb06140a6..31688555a 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_lmem_utility.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_lmem_utility.hpp @@ -24,7 +24,13 @@ namespace ams::sf { private: lmem::HeapHandle handle; public: - explicit ExpHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ } + constexpr ExpHeapMemoryResource() : handle() { /* ... */ } + constexpr explicit ExpHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ } + + void Attach(lmem::HeapHandle h) { + AMS_ABORT_UNLESS(this->handle == lmem::HeapHandle()); + this->handle = h; + } lmem::HeapHandle GetHandle() const { return this->handle; } private: @@ -45,7 +51,13 @@ namespace ams::sf { private: lmem::HeapHandle handle; public: - explicit UnitHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ } + constexpr UnitHeapMemoryResource() : handle() { /* ... */ } + constexpr explicit UnitHeapMemoryResource(lmem::HeapHandle h) : handle(h) { /* ... */ } + + void Attach(lmem::HeapHandle h) { + AMS_ABORT_UNLESS(this->handle == lmem::HeapHandle()); + this->handle = h; + } lmem::HeapHandle GetHandle() const { return this->handle; } private: diff --git a/libraries/libstratosphere/include/stratosphere/sf/sf_service_object.hpp b/libraries/libstratosphere/include/stratosphere/sf/sf_service_object.hpp index bee400c81..6681861b2 100644 --- a/libraries/libstratosphere/include/stratosphere/sf/sf_service_object.hpp +++ b/libraries/libstratosphere/include/stratosphere/sf/sf_service_object.hpp @@ -62,6 +62,51 @@ namespace ams::sf { return std::make_shared>(std::make_shared(std::forward(args)...)); } + template + class ServiceObjectAllocatorImpl { + private: + template + friend class ServiceObjectAllocatorImpl; + public: + using value_type = T; + private: + MemoryResource * const memory_resource; + public: + constexpr ServiceObjectAllocatorImpl(MemoryResource *mr) : memory_resource(mr) { /* ... */ } + + template + constexpr ServiceObjectAllocatorImpl(const ServiceObjectAllocatorImpl &rhs) : memory_resource(rhs.memory_resource) { /* ... */ } + + value_type *allocate(size_t n) const { + void *mem = this->memory_resource->Allocate(n * sizeof(value_type), alignof(value_type)); + AMS_ABORT_UNLESS(mem != nullptr); + return static_cast(mem); + } + + void deallocate(void *p, size_t n) const { + this->memory_resource->Deallocate(p, n * sizeof(value_type), alignof(value_type)); + } + + template + inline bool operator==(const ServiceObjectAllocatorImpl &rhs) const { + return this->memory_resource->is_equal(*rhs->memory_resource); + } + + template + inline bool operator!=(const ServiceObjectAllocatorImpl &rhs) const { + return !(*this == rhs); + } + }; + + template + using ServiceObjectAllocator = ServiceObjectAllocatorImpl>; + + template + requires std::constructible_from + constexpr ALWAYS_INLINE std::shared_ptr> AllocateShared(const Allocator &allocator, Arguments &&... args) { + return std::allocate_shared>(allocator, std::forward(args)...); + } + template constexpr ALWAYS_INLINE std::shared_ptr> GetSharedPointerTo(Impl *impl) { return std::make_shared>(impl); @@ -72,4 +117,4 @@ namespace ams::sf { return GetSharedPointerTo(std::addressof(impl)); } -} \ No newline at end of file +} diff --git a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp index 843162857..6bb7c72f5 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/impl/sm_user_interface.hpp @@ -32,6 +32,7 @@ namespace ams::sm::impl { AMS_SF_METHOD_INFO(C, H, 65004, Result, AtmosphereHasMitm, (sf::Out out, ServiceName service)) \ AMS_SF_METHOD_INFO(C, H, 65005, Result, AtmosphereWaitMitm, (ServiceName service)) \ AMS_SF_METHOD_INFO(C, H, 65006, Result, AtmosphereDeclareFutureMitm, (ServiceName service)) \ + AMS_SF_METHOD_INFO(C, H, 65007, Result, AtmosphereClearFutureMitm, (ServiceName service)) \ AMS_SF_METHOD_INFO(C, H, 65100, Result, AtmosphereHasService, (sf::Out out, ServiceName service)) \ AMS_SF_METHOD_INFO(C, H, 65101, Result, AtmosphereWaitService, (ServiceName service)) diff --git a/libraries/libstratosphere/include/stratosphere/sm/sm_mitm_api.hpp b/libraries/libstratosphere/include/stratosphere/sm/sm_mitm_api.hpp index fcdd46c55..d78f63b1b 100644 --- a/libraries/libstratosphere/include/stratosphere/sm/sm_mitm_api.hpp +++ b/libraries/libstratosphere/include/stratosphere/sm/sm_mitm_api.hpp @@ -24,6 +24,7 @@ namespace ams::sm::mitm { Result InstallMitm(Handle *out_port, Handle *out_query, ServiceName name); Result UninstallMitm(ServiceName name); Result DeclareFutureMitm(ServiceName name); + Result ClearFutureMitm(ServiceName name); Result AcknowledgeSession(Service *out_service, MitmProcessInfo *out_info, ServiceName name); Result HasMitm(bool *out, ServiceName name); Result WaitMitm(ServiceName name); diff --git a/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp b/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp index 38a98d5df..ace91218d 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp @@ -29,7 +29,7 @@ namespace ams::spl::smc { } /* Functions. */ - Result SetConfig(spl::ConfigItem which, const u64 *value, size_t num_qwords); + Result SetConfig(spl::ConfigItem which, const void *address, const u64 *value, size_t num_qwords); Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem which); Result GetResult(Result *out, AsyncOperationKey op); Result GetResultData(Result *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op); @@ -59,8 +59,12 @@ namespace ams::spl::smc { Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id); /* Helpers. */ + inline Result SetConfig(spl::ConfigItem which, const u64 *value, size_t num_qwords) { + return SetConfig(which, nullptr, value, num_qwords); + } + inline Result SetConfig(spl::ConfigItem which, const u64 value) { - return SetConfig(which, &value, 1); + return SetConfig(which, std::addressof(value), 1); } } diff --git a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp index 310fcebc9..1225cd6e7 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp @@ -150,6 +150,15 @@ namespace ams::spl { }; }; static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); + + enum BootReason { + BootReason_Unknown = 0, + BootReason_AcOk = 1, + BootReason_OnKey = 2, + BootReason_RtcAlarm1 = 3, + BootReason_RtcAlarm2 = 4, + }; + #pragma pack(push, 1) struct AesKey { @@ -222,6 +231,7 @@ namespace ams::spl { ExosphereBlankProdInfo = 65005, ExosphereAllowCalWrites = 65006, ExosphereEmummcType = 65007, + ExospherePayloadAddress = 65008, }; } @@ -235,3 +245,4 @@ constexpr inline SplConfigItem SplConfigItem_ExosphereHasRcmBugPatch = static_ca constexpr inline SplConfigItem SplConfigItem_ExosphereBlankProdInfo = static_cast(65005); constexpr inline SplConfigItem SplConfigItem_ExosphereAllowCalWrites = static_cast(65006); constexpr inline SplConfigItem SplConfigItem_ExosphereEmummcType = static_cast(65007); +constexpr inline SplConfigItem SplConfigItem_ExospherePayloadAddress = static_cast(65008); diff --git a/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp b/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp index a98ba09e7..58be481ee 100644 --- a/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp +++ b/libraries/libstratosphere/include/stratosphere/svc/svc_stratosphere_shims.hpp @@ -14,482 +14,495 @@ * along with this program. If not, see . */ -#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) && defined(ATMOSPHERE_ARCH_ARM64) +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) -namespace ams::svc::aarch64::lp64 { + namespace ams::svc { - ALWAYS_INLINE Result SetHeapSize(::ams::svc::Address *out_address, ::ams::svc::Size size) { - return ::svcSetHeapSize(reinterpret_cast(out_address), size); - } + #if defined(ATMOSPHERE_ARCH_ARM64) - ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) { - return ::svcSetMemoryPermission(reinterpret_cast(static_cast(address)), size, static_cast(perm)); - } + namespace aarch64::lp64 { - ALWAYS_INLINE Result SetMemoryAttribute(::ams::svc::Address address, ::ams::svc::Size size, uint32_t mask, uint32_t attr) { - return ::svcSetMemoryAttribute(reinterpret_cast(static_cast(address)), size, mask, attr); - } + ALWAYS_INLINE Result SetHeapSize(::ams::svc::Address *out_address, ::ams::svc::Size size) { + return ::svcSetHeapSize(reinterpret_cast(out_address), size); + } - ALWAYS_INLINE Result MapMemory(::ams::svc::Address dst_address, ::ams::svc::Address src_address, ::ams::svc::Size size) { - return ::svcMapMemory(reinterpret_cast(static_cast(dst_address)), reinterpret_cast(static_cast(src_address)), size); - } + ALWAYS_INLINE Result SetMemoryPermission(::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission perm) { + return ::svcSetMemoryPermission(reinterpret_cast(static_cast(address)), size, static_cast(perm)); + } - ALWAYS_INLINE Result UnmapMemory(::ams::svc::Address dst_address, ::ams::svc::Address src_address, ::ams::svc::Size size) { - return ::svcUnmapMemory(reinterpret_cast(static_cast(dst_address)), reinterpret_cast(static_cast(src_address)), size); - } + ALWAYS_INLINE Result SetMemoryAttribute(::ams::svc::Address address, ::ams::svc::Size size, uint32_t mask, uint32_t attr) { + return ::svcSetMemoryAttribute(reinterpret_cast(static_cast(address)), size, mask, attr); + } - ALWAYS_INLINE Result QueryMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Address address) { - return ::svcQueryMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), address); - } + ALWAYS_INLINE Result MapMemory(::ams::svc::Address dst_address, ::ams::svc::Address src_address, ::ams::svc::Size size) { + return ::svcMapMemory(reinterpret_cast(static_cast(dst_address)), reinterpret_cast(static_cast(src_address)), size); + } - ALWAYS_INLINE void ExitProcess() { - return ::svcExitProcess(); - } + ALWAYS_INLINE Result UnmapMemory(::ams::svc::Address dst_address, ::ams::svc::Address src_address, ::ams::svc::Size size) { + return ::svcUnmapMemory(reinterpret_cast(static_cast(dst_address)), reinterpret_cast(static_cast(src_address)), size); + } - ALWAYS_INLINE Result CreateThread(::ams::svc::Handle *out_handle, ::ams::svc::ThreadFunc func, ::ams::svc::Address arg, ::ams::svc::Address stack_bottom, int32_t priority, int32_t core_id) { - return ::svcCreateThread(out_handle, reinterpret_cast(static_cast(func)), reinterpret_cast(static_cast(arg)), reinterpret_cast(static_cast(stack_bottom)), priority, core_id); - } + ALWAYS_INLINE Result QueryMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Address address) { + return ::svcQueryMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), address); + } - ALWAYS_INLINE Result StartThread(::ams::svc::Handle thread_handle) { - return ::svcStartThread(thread_handle); - } + ALWAYS_INLINE void ExitProcess() { + return ::svcExitProcess(); + } - ALWAYS_INLINE void ExitThread() { - return ::svcExitThread(); - } + ALWAYS_INLINE Result CreateThread(::ams::svc::Handle *out_handle, ::ams::svc::ThreadFunc func, ::ams::svc::Address arg, ::ams::svc::Address stack_bottom, int32_t priority, int32_t core_id) { + return ::svcCreateThread(out_handle, reinterpret_cast(static_cast(func)), reinterpret_cast(static_cast(arg)), reinterpret_cast(static_cast(stack_bottom)), priority, core_id); + } - ALWAYS_INLINE void SleepThread(int64_t ns) { - return ::svcSleepThread(ns); - } + ALWAYS_INLINE Result StartThread(::ams::svc::Handle thread_handle) { + return ::svcStartThread(thread_handle); + } - ALWAYS_INLINE Result GetThreadPriority(int32_t *out_priority, ::ams::svc::Handle thread_handle) { - return ::svcGetThreadPriority(out_priority, thread_handle); - } + ALWAYS_INLINE void ExitThread() { + return ::svcExitThread(); + } - ALWAYS_INLINE Result SetThreadPriority(::ams::svc::Handle thread_handle, int32_t priority) { - return ::svcSetThreadPriority(thread_handle, priority); - } + ALWAYS_INLINE void SleepThread(int64_t ns) { + return ::svcSleepThread(ns); + } - ALWAYS_INLINE Result GetThreadCoreMask(int32_t *out_core_id, uint64_t *out_affinity_mask, ::ams::svc::Handle thread_handle) { - return ::svcGetThreadCoreMask(out_core_id, out_affinity_mask, thread_handle); - } + ALWAYS_INLINE Result GetThreadPriority(int32_t *out_priority, ::ams::svc::Handle thread_handle) { + return ::svcGetThreadPriority(out_priority, thread_handle); + } - ALWAYS_INLINE Result SetThreadCoreMask(::ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) { - return ::svcSetThreadCoreMask(thread_handle, core_id, affinity_mask); - } + ALWAYS_INLINE Result SetThreadPriority(::ams::svc::Handle thread_handle, int32_t priority) { + return ::svcSetThreadPriority(thread_handle, priority); + } - ALWAYS_INLINE int32_t GetCurrentProcessorNumber() { - return ::svcGetCurrentProcessorNumber(); - } + ALWAYS_INLINE Result GetThreadCoreMask(int32_t *out_core_id, uint64_t *out_affinity_mask, ::ams::svc::Handle thread_handle) { + return ::svcGetThreadCoreMask(out_core_id, out_affinity_mask, thread_handle); + } - ALWAYS_INLINE Result SignalEvent(::ams::svc::Handle event_handle) { - return ::svcSignalEvent(event_handle); - } + ALWAYS_INLINE Result SetThreadCoreMask(::ams::svc::Handle thread_handle, int32_t core_id, uint64_t affinity_mask) { + return ::svcSetThreadCoreMask(thread_handle, core_id, affinity_mask); + } - ALWAYS_INLINE Result ClearEvent(::ams::svc::Handle event_handle) { - return ::svcClearEvent(event_handle); - } + ALWAYS_INLINE int32_t GetCurrentProcessorNumber() { + return ::svcGetCurrentProcessorNumber(); + } - ALWAYS_INLINE Result MapSharedMemory(::ams::svc::Handle shmem_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission map_perm) { - return ::svcMapSharedMemory(shmem_handle, reinterpret_cast(static_cast(address)), size, static_cast(map_perm)); - } + ALWAYS_INLINE Result SignalEvent(::ams::svc::Handle event_handle) { + return ::svcSignalEvent(event_handle); + } - ALWAYS_INLINE Result UnmapSharedMemory(::ams::svc::Handle shmem_handle, ::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcUnmapSharedMemory(shmem_handle, reinterpret_cast(static_cast(address)), size); - } + ALWAYS_INLINE Result ClearEvent(::ams::svc::Handle event_handle) { + return ::svcClearEvent(event_handle); + } - ALWAYS_INLINE Result CreateTransferMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission map_perm) { - return ::svcCreateTransferMemory(out_handle, reinterpret_cast(static_cast(address)), size, static_cast(map_perm)); - } + ALWAYS_INLINE Result MapSharedMemory(::ams::svc::Handle shmem_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission map_perm) { + return ::svcMapSharedMemory(shmem_handle, reinterpret_cast(static_cast(address)), size, static_cast(map_perm)); + } - ALWAYS_INLINE Result CloseHandle(::ams::svc::Handle handle) { - return ::svcCloseHandle(handle); - } + ALWAYS_INLINE Result UnmapSharedMemory(::ams::svc::Handle shmem_handle, ::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcUnmapSharedMemory(shmem_handle, reinterpret_cast(static_cast(address)), size); + } - ALWAYS_INLINE Result ResetSignal(::ams::svc::Handle handle) { - return ::svcResetSignal(handle); - } + ALWAYS_INLINE Result CreateTransferMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission map_perm) { + return ::svcCreateTransferMemory(out_handle, reinterpret_cast(static_cast(address)), size, static_cast(map_perm)); + } - ALWAYS_INLINE Result WaitSynchronization(int32_t *out_index, ::ams::svc::UserPointer handles, int32_t num_handles, int64_t timeout_ns) { - return ::svcWaitSynchronization(out_index, handles.GetPointerUnsafe(), num_handles, timeout_ns); - } + ALWAYS_INLINE Result CloseHandle(::ams::svc::Handle handle) { + return ::svcCloseHandle(handle); + } - ALWAYS_INLINE Result CancelSynchronization(::ams::svc::Handle handle) { - return ::svcCancelSynchronization(handle); - } + ALWAYS_INLINE Result ResetSignal(::ams::svc::Handle handle) { + return ::svcResetSignal(handle); + } - ALWAYS_INLINE Result ArbitrateLock(::ams::svc::Handle thread_handle, ::ams::svc::Address address, uint32_t tag) { - return ::svcArbitrateLock(thread_handle, reinterpret_cast(static_cast(address)), tag); - } + ALWAYS_INLINE Result WaitSynchronization(int32_t *out_index, ::ams::svc::UserPointer handles, int32_t num_handles, int64_t timeout_ns) { + return ::svcWaitSynchronization(out_index, handles.GetPointerUnsafe(), num_handles, timeout_ns); + } - ALWAYS_INLINE Result ArbitrateUnlock(::ams::svc::Address address) { - return ::svcArbitrateUnlock(reinterpret_cast(static_cast(address))); - } + ALWAYS_INLINE Result CancelSynchronization(::ams::svc::Handle handle) { + return ::svcCancelSynchronization(handle); + } - ALWAYS_INLINE Result WaitProcessWideKeyAtomic(::ams::svc::Address address, ::ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) { - return ::svcWaitProcessWideKeyAtomic(reinterpret_cast(static_cast(address)), reinterpret_cast(static_cast(cv_key)), tag, timeout_ns); - } + ALWAYS_INLINE Result ArbitrateLock(::ams::svc::Handle thread_handle, ::ams::svc::Address address, uint32_t tag) { + return ::svcArbitrateLock(thread_handle, reinterpret_cast(static_cast(address)), tag); + } - ALWAYS_INLINE void SignalProcessWideKey(::ams::svc::Address cv_key, int32_t count) { - return ::svcSignalProcessWideKey(reinterpret_cast(static_cast(cv_key)), count); - } + ALWAYS_INLINE Result ArbitrateUnlock(::ams::svc::Address address) { + return ::svcArbitrateUnlock(reinterpret_cast(static_cast(address))); + } - ALWAYS_INLINE int64_t GetSystemTick() { - return ::svcGetSystemTick(); - } + ALWAYS_INLINE Result WaitProcessWideKeyAtomic(::ams::svc::Address address, ::ams::svc::Address cv_key, uint32_t tag, int64_t timeout_ns) { + return ::svcWaitProcessWideKeyAtomic(reinterpret_cast(static_cast(address)), reinterpret_cast(static_cast(cv_key)), tag, timeout_ns); + } - ALWAYS_INLINE Result ConnectToNamedPort(::ams::svc::Handle *out_handle, ::ams::svc::UserPointer name) { - return ::svcConnectToNamedPort(out_handle, name.GetPointerUnsafe()); - } + ALWAYS_INLINE void SignalProcessWideKey(::ams::svc::Address cv_key, int32_t count) { + return ::svcSignalProcessWideKey(reinterpret_cast(static_cast(cv_key)), count); + } - ALWAYS_INLINE Result SendSyncRequestLight(::ams::svc::Handle session_handle) { - return ::svcSendSyncRequestLight(session_handle); - } + ALWAYS_INLINE int64_t GetSystemTick() { + return ::svcGetSystemTick(); + } - ALWAYS_INLINE Result SendSyncRequest(::ams::svc::Handle session_handle) { - return ::svcSendSyncRequest(session_handle); - } + ALWAYS_INLINE Result ConnectToNamedPort(::ams::svc::Handle *out_handle, ::ams::svc::UserPointer name) { + return ::svcConnectToNamedPort(out_handle, name.GetPointerUnsafe()); + } - ALWAYS_INLINE Result SendSyncRequestWithUserBuffer(::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) { - return ::svcSendSyncRequestWithUserBuffer(reinterpret_cast(static_cast(message_buffer)), message_buffer_size, session_handle); - } + ALWAYS_INLINE Result SendSyncRequestLight(::ams::svc::Handle session_handle) { + return ::svcSendSyncRequestLight(session_handle); + } - ALWAYS_INLINE Result SendAsyncRequestWithUserBuffer(::ams::svc::Handle *out_event_handle, ::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) { - return ::svcSendAsyncRequestWithUserBuffer(out_event_handle, reinterpret_cast(static_cast(message_buffer)), message_buffer_size, session_handle); - } + ALWAYS_INLINE Result SendSyncRequest(::ams::svc::Handle session_handle) { + return ::svcSendSyncRequest(session_handle); + } - ALWAYS_INLINE Result GetProcessId(uint64_t *out_process_id, ::ams::svc::Handle process_handle) { - return ::svcGetProcessId(out_process_id, process_handle); - } + ALWAYS_INLINE Result SendSyncRequestWithUserBuffer(::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) { + return ::svcSendSyncRequestWithUserBuffer(reinterpret_cast(static_cast(message_buffer)), message_buffer_size, session_handle); + } - ALWAYS_INLINE Result GetThreadId(uint64_t *out_thread_id, ::ams::svc::Handle thread_handle) { - return ::svcGetThreadId(out_thread_id, thread_handle); - } + ALWAYS_INLINE Result SendAsyncRequestWithUserBuffer(::ams::svc::Handle *out_event_handle, ::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::Handle session_handle) { + return ::svcSendAsyncRequestWithUserBuffer(out_event_handle, reinterpret_cast(static_cast(message_buffer)), message_buffer_size, session_handle); + } - ALWAYS_INLINE void Break(::ams::svc::BreakReason break_reason, ::ams::svc::Address arg, ::ams::svc::Size size) { - ::svcBreak(break_reason, arg, size); - } + ALWAYS_INLINE Result GetProcessId(uint64_t *out_process_id, ::ams::svc::Handle process_handle) { + return ::svcGetProcessId(out_process_id, process_handle); + } - ALWAYS_INLINE Result OutputDebugString(::ams::svc::UserPointer debug_str, ::ams::svc::Size len) { - return ::svcOutputDebugString(debug_str.GetPointerUnsafe(), len); - } + ALWAYS_INLINE Result GetThreadId(uint64_t *out_thread_id, ::ams::svc::Handle thread_handle) { + return ::svcGetThreadId(out_thread_id, thread_handle); + } - ALWAYS_INLINE void ReturnFromException(::ams::Result result) { - return ::svcReturnFromException(result.GetValue()); - } + ALWAYS_INLINE void Break(::ams::svc::BreakReason break_reason, ::ams::svc::Address arg, ::ams::svc::Size size) { + ::svcBreak(break_reason, arg, size); + } - ALWAYS_INLINE Result GetInfo(uint64_t *out, ::ams::svc::InfoType info_type, ::ams::svc::Handle handle, uint64_t info_subtype) { - return ::svcGetInfo(out, static_cast(info_type), handle, info_subtype); - } + ALWAYS_INLINE Result OutputDebugString(::ams::svc::UserPointer debug_str, ::ams::svc::Size len) { + return ::svcOutputDebugString(debug_str.GetPointerUnsafe(), len); + } - ALWAYS_INLINE void FlushEntireDataCache() { - return ::svcFlushEntireDataCache(); - } + ALWAYS_INLINE void ReturnFromException(::ams::Result result) { + return ::svcReturnFromException(result.GetValue()); + } - ALWAYS_INLINE Result FlushDataCache(::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcFlushDataCache(reinterpret_cast(static_cast(address)), size); - } + ALWAYS_INLINE Result GetInfo(uint64_t *out, ::ams::svc::InfoType info_type, ::ams::svc::Handle handle, uint64_t info_subtype) { + return ::svcGetInfo(out, static_cast(info_type), handle, info_subtype); + } - ALWAYS_INLINE Result MapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcMapPhysicalMemory(reinterpret_cast(static_cast(address)), size); - } + ALWAYS_INLINE void FlushEntireDataCache() { + return ::svcFlushEntireDataCache(); + } - ALWAYS_INLINE Result UnmapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcUnmapPhysicalMemory(reinterpret_cast(static_cast(address)), size); - } + ALWAYS_INLINE Result FlushDataCache(::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcFlushDataCache(reinterpret_cast(static_cast(address)), size); + } - ALWAYS_INLINE Result GetDebugFutureThreadInfo(::ams::svc::lp64::LastThreadContext *out_context, uint64_t *thread_id, ::ams::svc::Handle debug_handle, int64_t ns) { - return ::svcGetDebugFutureThreadInfo(reinterpret_cast<::LastThreadContext *>(out_context), thread_id, debug_handle, ns); - } + ALWAYS_INLINE Result MapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcMapPhysicalMemory(reinterpret_cast(static_cast(address)), size); + } - ALWAYS_INLINE Result GetLastThreadInfo(::ams::svc::lp64::LastThreadContext *out_context, ::ams::svc::Address *out_tls_address, uint32_t *out_flags) { - return ::svcGetLastThreadInfo(reinterpret_cast<::LastThreadContext *>(out_context), reinterpret_cast(out_tls_address), out_flags); - } + ALWAYS_INLINE Result UnmapPhysicalMemory(::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcUnmapPhysicalMemory(reinterpret_cast(static_cast(address)), size); + } - ALWAYS_INLINE Result GetResourceLimitLimitValue(int64_t *out_limit_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) { - return ::svcGetResourceLimitLimitValue(out_limit_value, resource_limit_handle, static_cast<::LimitableResource>(which)); - } + ALWAYS_INLINE Result GetDebugFutureThreadInfo(::ams::svc::lp64::LastThreadContext *out_context, uint64_t *thread_id, ::ams::svc::Handle debug_handle, int64_t ns) { + return ::svcGetDebugFutureThreadInfo(reinterpret_cast<::LastThreadContext *>(out_context), thread_id, debug_handle, ns); + } - ALWAYS_INLINE Result GetResourceLimitCurrentValue(int64_t *out_current_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) { - return ::svcGetResourceLimitCurrentValue(out_current_value, resource_limit_handle, static_cast<::LimitableResource>(which)); - } + ALWAYS_INLINE Result GetLastThreadInfo(::ams::svc::lp64::LastThreadContext *out_context, ::ams::svc::Address *out_tls_address, uint32_t *out_flags) { + return ::svcGetLastThreadInfo(reinterpret_cast<::LastThreadContext *>(out_context), reinterpret_cast(out_tls_address), out_flags); + } - ALWAYS_INLINE Result SetThreadActivity(::ams::svc::Handle thread_handle, ::ams::svc::ThreadActivity thread_activity) { - return ::svcSetThreadActivity(thread_handle, static_cast<::ThreadActivity>(thread_activity)); - } + ALWAYS_INLINE Result GetResourceLimitLimitValue(int64_t *out_limit_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) { + return ::svcGetResourceLimitLimitValue(out_limit_value, resource_limit_handle, static_cast<::LimitableResource>(which)); + } - ALWAYS_INLINE Result GetThreadContext3(::ams::svc::UserPointer< ::ams::svc::ThreadContext *> out_context, ::ams::svc::Handle thread_handle) { - return ::svcGetThreadContext3(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), thread_handle); - } + ALWAYS_INLINE Result GetResourceLimitCurrentValue(int64_t *out_current_value, ::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which) { + return ::svcGetResourceLimitCurrentValue(out_current_value, resource_limit_handle, static_cast<::LimitableResource>(which)); + } - ALWAYS_INLINE Result WaitForAddress(::ams::svc::Address address, ::ams::svc::ArbitrationType arb_type, int32_t value, int64_t timeout_ns) { - return ::svcWaitForAddress(reinterpret_cast(static_cast(address)), arb_type, value, timeout_ns); - } + ALWAYS_INLINE Result SetThreadActivity(::ams::svc::Handle thread_handle, ::ams::svc::ThreadActivity thread_activity) { + return ::svcSetThreadActivity(thread_handle, static_cast<::ThreadActivity>(thread_activity)); + } - ALWAYS_INLINE Result SignalToAddress(::ams::svc::Address address, ::ams::svc::SignalType signal_type, int32_t value, int32_t count) { - return ::svcSignalToAddress(reinterpret_cast(static_cast(address)), signal_type, value, count); - } + ALWAYS_INLINE Result GetThreadContext3(::ams::svc::UserPointer< ::ams::svc::ThreadContext *> out_context, ::ams::svc::Handle thread_handle) { + return ::svcGetThreadContext3(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), thread_handle); + } - ALWAYS_INLINE void SynchronizePreemptionState() { - return ::svcSynchronizePreemptionState(); - } + ALWAYS_INLINE Result WaitForAddress(::ams::svc::Address address, ::ams::svc::ArbitrationType arb_type, int32_t value, int64_t timeout_ns) { + return ::svcWaitForAddress(reinterpret_cast(static_cast(address)), arb_type, value, timeout_ns); + } - ALWAYS_INLINE void KernelDebug(::ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) { - return ::svcKernelDebug(kern_debug_type, arg0, arg1, arg2); - } + ALWAYS_INLINE Result SignalToAddress(::ams::svc::Address address, ::ams::svc::SignalType signal_type, int32_t value, int32_t count) { + return ::svcSignalToAddress(reinterpret_cast(static_cast(address)), signal_type, value, count); + } - ALWAYS_INLINE void ChangeKernelTraceState(::ams::svc::KernelTraceState kern_trace_state) { - return ::svcChangeKernelTraceState(kern_trace_state); - } + ALWAYS_INLINE void SynchronizePreemptionState() { + return ::svcSynchronizePreemptionState(); + } - ALWAYS_INLINE Result CreateSession(::ams::svc::Handle *out_server_session_handle, ::ams::svc::Handle *out_client_session_handle, bool is_light, ::ams::svc::Address name) { - return ::svcCreateSession(out_server_session_handle, out_client_session_handle, is_light, name); - } + ALWAYS_INLINE void KernelDebug(::ams::svc::KernelDebugType kern_debug_type, uint64_t arg0, uint64_t arg1, uint64_t arg2) { + return ::svcKernelDebug(kern_debug_type, arg0, arg1, arg2); + } - ALWAYS_INLINE Result AcceptSession(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) { - return ::svcAcceptSession(out_handle, port); - } + ALWAYS_INLINE void ChangeKernelTraceState(::ams::svc::KernelTraceState kern_trace_state) { + return ::svcChangeKernelTraceState(kern_trace_state); + } - ALWAYS_INLINE Result ReplyAndReceiveLight(::ams::svc::Handle handle) { - return ::svcReplyAndReceiveLight(handle); - } + ALWAYS_INLINE Result CreateSession(::ams::svc::Handle *out_server_session_handle, ::ams::svc::Handle *out_client_session_handle, bool is_light, ::ams::svc::Address name) { + return ::svcCreateSession(out_server_session_handle, out_client_session_handle, is_light, name); + } - ALWAYS_INLINE Result ReplyAndReceive(int32_t *out_index, ::ams::svc::UserPointer handles, int32_t num_handles, ::ams::svc::Handle reply_target, int64_t timeout_ns) { - return ::svcReplyAndReceive(out_index, handles.GetPointerUnsafe(), num_handles, reply_target, timeout_ns); - } + ALWAYS_INLINE Result AcceptSession(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) { + return ::svcAcceptSession(out_handle, port); + } - ALWAYS_INLINE Result ReplyAndReceiveWithUserBuffer(int32_t *out_index, ::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::UserPointer handles, int32_t num_handles, ::ams::svc::Handle reply_target, int64_t timeout_ns) { - return ::svcReplyAndReceiveWithUserBuffer(out_index, reinterpret_cast(static_cast(message_buffer)), message_buffer_size, handles.GetPointerUnsafe(), num_handles, reply_target, timeout_ns); - } + ALWAYS_INLINE Result ReplyAndReceiveLight(::ams::svc::Handle handle) { + return ::svcReplyAndReceiveLight(handle); + } - ALWAYS_INLINE Result CreateEvent(::ams::svc::Handle *out_write_handle, ::ams::svc::Handle *out_read_handle) { - return ::svcCreateEvent(out_write_handle, out_read_handle); - } + ALWAYS_INLINE Result ReplyAndReceive(int32_t *out_index, ::ams::svc::UserPointer handles, int32_t num_handles, ::ams::svc::Handle reply_target, int64_t timeout_ns) { + return ::svcReplyAndReceive(out_index, handles.GetPointerUnsafe(), num_handles, reply_target, timeout_ns); + } - ALWAYS_INLINE Result MapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcMapPhysicalMemoryUnsafe(reinterpret_cast(static_cast(address)), size); - } + ALWAYS_INLINE Result ReplyAndReceiveWithUserBuffer(int32_t *out_index, ::ams::svc::Address message_buffer, ::ams::svc::Size message_buffer_size, ::ams::svc::UserPointer handles, int32_t num_handles, ::ams::svc::Handle reply_target, int64_t timeout_ns) { + return ::svcReplyAndReceiveWithUserBuffer(out_index, reinterpret_cast(static_cast(message_buffer)), message_buffer_size, handles.GetPointerUnsafe(), num_handles, reply_target, timeout_ns); + } - ALWAYS_INLINE Result UnmapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcUnmapPhysicalMemoryUnsafe(reinterpret_cast(static_cast(address)), size); - } + ALWAYS_INLINE Result CreateEvent(::ams::svc::Handle *out_write_handle, ::ams::svc::Handle *out_read_handle) { + return ::svcCreateEvent(out_write_handle, out_read_handle); + } - ALWAYS_INLINE Result SetUnsafeLimit(::ams::svc::Size limit) { - return ::svcSetUnsafeLimit(limit); - } + ALWAYS_INLINE Result MapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcMapPhysicalMemoryUnsafe(reinterpret_cast(static_cast(address)), size); + } - ALWAYS_INLINE Result CreateCodeMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcCreateCodeMemory(out_handle, reinterpret_cast(static_cast(address)), size); - } + ALWAYS_INLINE Result UnmapPhysicalMemoryUnsafe(::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcUnmapPhysicalMemoryUnsafe(reinterpret_cast(static_cast(address)), size); + } - ALWAYS_INLINE Result ControlCodeMemory(::ams::svc::Handle code_memory_handle, ::ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) { - return ::svcControlCodeMemory(code_memory_handle, static_cast<::CodeMapOperation>(operation), reinterpret_cast(address), size, static_cast(perm)); - } + ALWAYS_INLINE Result SetUnsafeLimit(::ams::svc::Size limit) { + return ::svcSetUnsafeLimit(limit); + } - ALWAYS_INLINE void SleepSystem() { - return ::svcSleepSystem(); - } + ALWAYS_INLINE Result CreateCodeMemory(::ams::svc::Handle *out_handle, ::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcCreateCodeMemory(out_handle, reinterpret_cast(static_cast(address)), size); + } - ALWAYS_INLINE Result ReadWriteRegister(uint32_t *out_value, ::ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) { - return ::svcReadWriteRegister(out_value, address, mask, value); - } + ALWAYS_INLINE Result ControlCodeMemory(::ams::svc::Handle code_memory_handle, ::ams::svc::CodeMemoryOperation operation, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) { + return ::svcControlCodeMemory(code_memory_handle, static_cast<::CodeMapOperation>(operation), reinterpret_cast(address), size, static_cast(perm)); + } - ALWAYS_INLINE Result SetProcessActivity(::ams::svc::Handle process_handle, ::ams::svc::ProcessActivity process_activity) { - return ::svcSetProcessActivity(process_handle, static_cast<::ProcessActivity>(process_activity)); - } + ALWAYS_INLINE void SleepSystem() { + return ::svcSleepSystem(); + } - ALWAYS_INLINE Result CreateSharedMemory(::ams::svc::Handle *out_handle, ::ams::svc::Size size, ::ams::svc::MemoryPermission owner_perm, ::ams::svc::MemoryPermission remote_perm) { - return ::svcCreateSharedMemory(out_handle, size, static_cast(owner_perm), static_cast(remote_perm)); - } + ALWAYS_INLINE Result ReadWriteRegister(uint32_t *out_value, ::ams::svc::PhysicalAddress address, uint32_t mask, uint32_t value) { + return ::svcReadWriteRegister(out_value, address, mask, value); + } - ALWAYS_INLINE Result MapTransferMemory(::ams::svc::Handle trmem_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission owner_perm) { - return ::svcMapTransferMemory(trmem_handle, reinterpret_cast(static_cast(address)), size, static_cast(owner_perm)); - } + ALWAYS_INLINE Result SetProcessActivity(::ams::svc::Handle process_handle, ::ams::svc::ProcessActivity process_activity) { + return ::svcSetProcessActivity(process_handle, static_cast<::ProcessActivity>(process_activity)); + } - ALWAYS_INLINE Result UnmapTransferMemory(::ams::svc::Handle trmem_handle, ::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcUnmapTransferMemory(trmem_handle, reinterpret_cast(static_cast(address)), size); - } + ALWAYS_INLINE Result CreateSharedMemory(::ams::svc::Handle *out_handle, ::ams::svc::Size size, ::ams::svc::MemoryPermission owner_perm, ::ams::svc::MemoryPermission remote_perm) { + return ::svcCreateSharedMemory(out_handle, size, static_cast(owner_perm), static_cast(remote_perm)); + } - ALWAYS_INLINE Result CreateInterruptEvent(::ams::svc::Handle *out_read_handle, int32_t interrupt_id, ::ams::svc::InterruptType interrupt_type) { - return ::svcCreateInterruptEvent(out_read_handle, interrupt_id, static_cast(interrupt_type)); - } + ALWAYS_INLINE Result MapTransferMemory(::ams::svc::Handle trmem_handle, ::ams::svc::Address address, ::ams::svc::Size size, ::ams::svc::MemoryPermission owner_perm) { + return ::svcMapTransferMemory(trmem_handle, reinterpret_cast(static_cast(address)), size, static_cast(owner_perm)); + } - ALWAYS_INLINE Result QueryPhysicalAddress(::ams::svc::lp64::PhysicalMemoryInfo *out_info, ::ams::svc::Address address) { - return ::svcQueryPhysicalAddress(reinterpret_cast<::PhysicalMemoryInfo *>(out_info), address); - } + ALWAYS_INLINE Result UnmapTransferMemory(::ams::svc::Handle trmem_handle, ::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcUnmapTransferMemory(trmem_handle, reinterpret_cast(static_cast(address)), size); + } - ALWAYS_INLINE Result QueryIoMapping(::ams::svc::Address *out_address, ::ams::svc::Size *out_size, ::ams::svc::PhysicalAddress physical_address, ::ams::svc::Size size) { - return ::svcQueryIoMapping(reinterpret_cast(out_address), reinterpret_cast(out_size), physical_address, size); - } + ALWAYS_INLINE Result CreateInterruptEvent(::ams::svc::Handle *out_read_handle, int32_t interrupt_id, ::ams::svc::InterruptType interrupt_type) { + return ::svcCreateInterruptEvent(out_read_handle, interrupt_id, static_cast(interrupt_type)); + } - ALWAYS_INLINE Result LegacyQueryIoMapping(::ams::svc::Address *out_address, ::ams::svc::PhysicalAddress physical_address, ::ams::svc::Size size) { - return ::svcLegacyQueryIoMapping(reinterpret_cast(out_address), physical_address, size); - } + ALWAYS_INLINE Result QueryPhysicalAddress(::ams::svc::lp64::PhysicalMemoryInfo *out_info, ::ams::svc::Address address) { + return ::svcQueryPhysicalAddress(reinterpret_cast<::PhysicalMemoryInfo *>(out_info), address); + } - ALWAYS_INLINE Result CreateDeviceAddressSpace(::ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) { - return ::svcCreateDeviceAddressSpace(out_handle, das_address, das_size); - } + ALWAYS_INLINE Result QueryIoMapping(::ams::svc::Address *out_address, ::ams::svc::Size *out_size, ::ams::svc::PhysicalAddress physical_address, ::ams::svc::Size size) { + return ::svcQueryIoMapping(reinterpret_cast(out_address), reinterpret_cast(out_size), physical_address, size); + } - ALWAYS_INLINE Result AttachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) { - return ::svcAttachDeviceAddressSpace(static_cast(device_name), das_handle); - } + ALWAYS_INLINE Result LegacyQueryIoMapping(::ams::svc::Address *out_address, ::ams::svc::PhysicalAddress physical_address, ::ams::svc::Size size) { + return ::svcLegacyQueryIoMapping(reinterpret_cast(out_address), physical_address, size); + } - ALWAYS_INLINE Result DetachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) { - return ::svcDetachDeviceAddressSpace(static_cast(device_name), das_handle); - } + ALWAYS_INLINE Result CreateDeviceAddressSpace(::ams::svc::Handle *out_handle, uint64_t das_address, uint64_t das_size) { + return ::svcCreateDeviceAddressSpace(out_handle, das_address, das_size); + } - ALWAYS_INLINE Result MapDeviceAddressSpaceByForce(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) { - return ::svcMapDeviceAddressSpaceByForce(das_handle, process_handle, process_address, size, device_address, static_cast(device_perm)); - } + ALWAYS_INLINE Result AttachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) { + return ::svcAttachDeviceAddressSpace(static_cast(device_name), das_handle); + } - ALWAYS_INLINE Result MapDeviceAddressSpaceAligned(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) { - return ::svcMapDeviceAddressSpaceAligned(das_handle, process_handle, process_address, size, device_address, static_cast(device_perm)); - } + ALWAYS_INLINE Result DetachDeviceAddressSpace(::ams::svc::DeviceName device_name, ::ams::svc::Handle das_handle) { + return ::svcDetachDeviceAddressSpace(static_cast(device_name), das_handle); + } - ALWAYS_INLINE Result MapDeviceAddressSpace(::ams::svc::Size *out_mapped_size, ::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) { - return ::svcMapDeviceAddressSpace(reinterpret_cast(out_mapped_size), das_handle, process_handle, process_address, size, device_address, static_cast(device_perm)); - } + ALWAYS_INLINE Result MapDeviceAddressSpaceByForce(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) { + return ::svcMapDeviceAddressSpaceByForce(das_handle, process_handle, process_address, size, device_address, static_cast(device_perm)); + } - ALWAYS_INLINE Result UnmapDeviceAddressSpace(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address) { - return ::svcUnmapDeviceAddressSpace(das_handle, process_handle, process_address, size, device_address); - } + ALWAYS_INLINE Result MapDeviceAddressSpaceAligned(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) { + return ::svcMapDeviceAddressSpaceAligned(das_handle, process_handle, process_address, size, device_address, static_cast(device_perm)); + } - ALWAYS_INLINE Result InvalidateProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) { - return ::svcInvalidateProcessDataCache(process_handle, address, size); - } + ALWAYS_INLINE Result MapDeviceAddressSpace(::ams::svc::Size *out_mapped_size, ::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address, ::ams::svc::MemoryPermission device_perm) { + return ::svcMapDeviceAddressSpace(reinterpret_cast(out_mapped_size), das_handle, process_handle, process_address, size, device_address, static_cast(device_perm)); + } - ALWAYS_INLINE Result StoreProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) { - return ::svcStoreProcessDataCache(process_handle, address, size); - } + ALWAYS_INLINE Result UnmapDeviceAddressSpace(::ams::svc::Handle das_handle, ::ams::svc::Handle process_handle, uint64_t process_address, ::ams::svc::Size size, uint64_t device_address) { + return ::svcUnmapDeviceAddressSpace(das_handle, process_handle, process_address, size, device_address); + } - ALWAYS_INLINE Result FlushProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) { - return ::svcFlushProcessDataCache(process_handle, address, size); - } + ALWAYS_INLINE Result InvalidateProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) { + return ::svcInvalidateProcessDataCache(process_handle, address, size); + } - ALWAYS_INLINE Result DebugActiveProcess(::ams::svc::Handle *out_handle, uint64_t process_id) { - return ::svcDebugActiveProcess(out_handle, process_id); - } + ALWAYS_INLINE Result StoreProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) { + return ::svcStoreProcessDataCache(process_handle, address, size); + } - ALWAYS_INLINE Result BreakDebugProcess(::ams::svc::Handle debug_handle) { - return ::svcBreakDebugProcess(debug_handle); - } + ALWAYS_INLINE Result FlushProcessDataCache(::ams::svc::Handle process_handle, uint64_t address, uint64_t size) { + return ::svcFlushProcessDataCache(process_handle, address, size); + } - ALWAYS_INLINE Result TerminateDebugProcess(::ams::svc::Handle debug_handle) { - return ::svcTerminateDebugProcess(debug_handle); - } + ALWAYS_INLINE Result DebugActiveProcess(::ams::svc::Handle *out_handle, uint64_t process_id) { + return ::svcDebugActiveProcess(out_handle, process_id); + } - ALWAYS_INLINE Result GetDebugEvent(::ams::svc::UserPointer< ::ams::svc::lp64::DebugEventInfo *> out_info, ::ams::svc::Handle debug_handle) { - return ::svcGetDebugEvent(out_info.GetPointerUnsafe(), debug_handle); - } + ALWAYS_INLINE Result BreakDebugProcess(::ams::svc::Handle debug_handle) { + return ::svcBreakDebugProcess(debug_handle); + } - ALWAYS_INLINE Result ContinueDebugEvent(::ams::svc::Handle debug_handle, uint32_t flags, ::ams::svc::UserPointer thread_ids, int32_t num_thread_ids) { - return ::svcContinueDebugEvent(debug_handle, flags, const_cast(thread_ids.GetPointerUnsafe()), num_thread_ids); - } + ALWAYS_INLINE Result TerminateDebugProcess(::ams::svc::Handle debug_handle) { + return ::svcTerminateDebugProcess(debug_handle); + } - ALWAYS_INLINE Result GetProcessList(int32_t *out_num_processes, ::ams::svc::UserPointer out_process_ids, int32_t max_out_count) { - return ::svcGetProcessList(out_num_processes, out_process_ids.GetPointerUnsafe(), max_out_count); - } + ALWAYS_INLINE Result GetDebugEvent(::ams::svc::UserPointer< ::ams::svc::lp64::DebugEventInfo *> out_info, ::ams::svc::Handle debug_handle) { + return ::svcGetDebugEvent(out_info.GetPointerUnsafe(), debug_handle); + } - ALWAYS_INLINE Result GetThreadList(int32_t *out_num_threads, ::ams::svc::UserPointer out_thread_ids, int32_t max_out_count, ::ams::svc::Handle debug_handle) { - return ::svcGetThreadList(out_num_threads, out_thread_ids.GetPointerUnsafe(), max_out_count, debug_handle); - } + ALWAYS_INLINE Result ContinueDebugEvent(::ams::svc::Handle debug_handle, uint32_t flags, ::ams::svc::UserPointer thread_ids, int32_t num_thread_ids) { + return ::svcContinueDebugEvent(debug_handle, flags, const_cast(thread_ids.GetPointerUnsafe()), num_thread_ids); + } - ALWAYS_INLINE Result GetDebugThreadContext(::ams::svc::UserPointer< ::ams::svc::ThreadContext *> out_context, ::ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) { - return ::svcGetDebugThreadContext(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), debug_handle, thread_id, context_flags); - } + ALWAYS_INLINE Result GetProcessList(int32_t *out_num_processes, ::ams::svc::UserPointer out_process_ids, int32_t max_out_count) { + return ::svcGetProcessList(out_num_processes, out_process_ids.GetPointerUnsafe(), max_out_count); + } - ALWAYS_INLINE Result SetDebugThreadContext(::ams::svc::Handle debug_handle, uint64_t thread_id, ::ams::svc::UserPointer context, uint32_t context_flags) { - return ::svcSetDebugThreadContext(debug_handle, thread_id, reinterpret_cast(context.GetPointerUnsafe()), context_flags); - } + ALWAYS_INLINE Result GetThreadList(int32_t *out_num_threads, ::ams::svc::UserPointer out_thread_ids, int32_t max_out_count, ::ams::svc::Handle debug_handle) { + return ::svcGetThreadList(out_num_threads, out_thread_ids.GetPointerUnsafe(), max_out_count, debug_handle); + } - ALWAYS_INLINE Result QueryDebugProcessMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Handle process_handle, ::ams::svc::Address address) { - return ::svcQueryDebugProcessMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), process_handle, address); - } + ALWAYS_INLINE Result GetDebugThreadContext(::ams::svc::UserPointer< ::ams::svc::ThreadContext *> out_context, ::ams::svc::Handle debug_handle, uint64_t thread_id, uint32_t context_flags) { + return ::svcGetDebugThreadContext(reinterpret_cast<::ThreadContext *>(out_context.GetPointerUnsafe()), debug_handle, thread_id, context_flags); + } - ALWAYS_INLINE Result ReadDebugProcessMemory(::ams::svc::Address buffer, ::ams::svc::Handle debug_handle, ::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcReadDebugProcessMemory(reinterpret_cast(static_cast(buffer)), debug_handle, address, size); - } + ALWAYS_INLINE Result SetDebugThreadContext(::ams::svc::Handle debug_handle, uint64_t thread_id, ::ams::svc::UserPointer context, uint32_t context_flags) { + return ::svcSetDebugThreadContext(debug_handle, thread_id, reinterpret_cast(context.GetPointerUnsafe()), context_flags); + } - ALWAYS_INLINE Result WriteDebugProcessMemory(::ams::svc::Handle debug_handle, ::ams::svc::Address buffer, ::ams::svc::Address address, ::ams::svc::Size size) { - return ::svcWriteDebugProcessMemory(debug_handle, reinterpret_cast(static_cast(buffer)), address, size); - } + ALWAYS_INLINE Result QueryDebugProcessMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Handle process_handle, ::ams::svc::Address address) { + return ::svcQueryDebugProcessMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), process_handle, address); + } - ALWAYS_INLINE Result SetHardwareBreakPoint(::ams::svc::HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value) { - return ::svcSetHardwareBreakPoint(static_cast(name), flags, value); - } + ALWAYS_INLINE Result ReadDebugProcessMemory(::ams::svc::Address buffer, ::ams::svc::Handle debug_handle, ::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcReadDebugProcessMemory(reinterpret_cast(static_cast(buffer)), debug_handle, address, size); + } - ALWAYS_INLINE Result GetDebugThreadParam(uint64_t *out_64, uint32_t *out_32, ::ams::svc::Handle debug_handle, uint64_t thread_id, ::ams::svc::DebugThreadParam param) { - return ::svcGetDebugThreadParam(out_64, out_32, debug_handle, thread_id, static_cast<::DebugThreadParam>(param)); - } + ALWAYS_INLINE Result WriteDebugProcessMemory(::ams::svc::Handle debug_handle, ::ams::svc::Address buffer, ::ams::svc::Address address, ::ams::svc::Size size) { + return ::svcWriteDebugProcessMemory(debug_handle, reinterpret_cast(static_cast(buffer)), address, size); + } - ALWAYS_INLINE Result GetSystemInfo(uint64_t *out, ::ams::svc::SystemInfoType info_type, ::ams::svc::Handle handle, uint64_t info_subtype) { - return ::svcGetSystemInfo(out, static_cast(info_type), handle, info_subtype); - } + ALWAYS_INLINE Result SetHardwareBreakPoint(::ams::svc::HardwareBreakPointRegisterName name, uint64_t flags, uint64_t value) { + return ::svcSetHardwareBreakPoint(static_cast(name), flags, value); + } - ALWAYS_INLINE Result CreatePort(::ams::svc::Handle *out_server_handle, ::ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ::ams::svc::Address name) { - return ::svcCreatePort(out_server_handle, out_client_handle, max_sessions, is_light, reinterpret_cast(static_cast(name))); - } + ALWAYS_INLINE Result GetDebugThreadParam(uint64_t *out_64, uint32_t *out_32, ::ams::svc::Handle debug_handle, uint64_t thread_id, ::ams::svc::DebugThreadParam param) { + return ::svcGetDebugThreadParam(out_64, out_32, debug_handle, thread_id, static_cast<::DebugThreadParam>(param)); + } - ALWAYS_INLINE Result ManageNamedPort(::ams::svc::Handle *out_server_handle, ::ams::svc::UserPointer name, int32_t max_sessions) { - return ::svcManageNamedPort(out_server_handle, name.GetPointerUnsafe(), max_sessions); - } + ALWAYS_INLINE Result GetSystemInfo(uint64_t *out, ::ams::svc::SystemInfoType info_type, ::ams::svc::Handle handle, uint64_t info_subtype) { + return ::svcGetSystemInfo(out, static_cast(info_type), handle, info_subtype); + } - ALWAYS_INLINE Result ConnectToPort(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) { - return ::svcConnectToPort(out_handle, port); - } + ALWAYS_INLINE Result CreatePort(::ams::svc::Handle *out_server_handle, ::ams::svc::Handle *out_client_handle, int32_t max_sessions, bool is_light, ::ams::svc::Address name) { + return ::svcCreatePort(out_server_handle, out_client_handle, max_sessions, is_light, reinterpret_cast(static_cast(name))); + } - ALWAYS_INLINE Result SetProcessMemoryPermission(::ams::svc::Handle process_handle, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) { - return ::svcSetProcessMemoryPermission(process_handle, address, size, static_cast(perm)); - } + ALWAYS_INLINE Result ManageNamedPort(::ams::svc::Handle *out_server_handle, ::ams::svc::UserPointer name, int32_t max_sessions) { + return ::svcManageNamedPort(out_server_handle, name.GetPointerUnsafe(), max_sessions); + } - ALWAYS_INLINE Result MapProcessMemory(::ams::svc::Address dst_address, ::ams::svc::Handle process_handle, uint64_t src_address, ::ams::svc::Size size) { - return ::svcMapProcessMemory(reinterpret_cast(static_cast(dst_address)), process_handle, src_address, size); - } + ALWAYS_INLINE Result ConnectToPort(::ams::svc::Handle *out_handle, ::ams::svc::Handle port) { + return ::svcConnectToPort(out_handle, port); + } - ALWAYS_INLINE Result UnmapProcessMemory(::ams::svc::Address dst_address, ::ams::svc::Handle process_handle, uint64_t src_address, ::ams::svc::Size size) { - return ::svcUnmapProcessMemory(reinterpret_cast(static_cast(dst_address)), process_handle, src_address, size); - } + ALWAYS_INLINE Result SetProcessMemoryPermission(::ams::svc::Handle process_handle, uint64_t address, uint64_t size, ::ams::svc::MemoryPermission perm) { + return ::svcSetProcessMemoryPermission(process_handle, address, size, static_cast(perm)); + } - ALWAYS_INLINE Result QueryProcessMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Handle process_handle, uint64_t address) { - return ::svcQueryProcessMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), process_handle, address); - } + ALWAYS_INLINE Result MapProcessMemory(::ams::svc::Address dst_address, ::ams::svc::Handle process_handle, uint64_t src_address, ::ams::svc::Size size) { + return ::svcMapProcessMemory(reinterpret_cast(static_cast(dst_address)), process_handle, src_address, size); + } - ALWAYS_INLINE Result MapProcessCodeMemory(::ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) { - return ::svcMapProcessCodeMemory(process_handle, dst_address, src_address, size); - } + ALWAYS_INLINE Result UnmapProcessMemory(::ams::svc::Address dst_address, ::ams::svc::Handle process_handle, uint64_t src_address, ::ams::svc::Size size) { + return ::svcUnmapProcessMemory(reinterpret_cast(static_cast(dst_address)), process_handle, src_address, size); + } - ALWAYS_INLINE Result UnmapProcessCodeMemory(::ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) { - return ::svcUnmapProcessCodeMemory(process_handle, dst_address, src_address, size); - } + ALWAYS_INLINE Result QueryProcessMemory(::ams::svc::UserPointer< ::ams::svc::lp64::MemoryInfo *> out_memory_info, ::ams::svc::PageInfo *out_page_info, ::ams::svc::Handle process_handle, uint64_t address) { + return ::svcQueryProcessMemory(reinterpret_cast<::MemoryInfo *>(out_memory_info.GetPointerUnsafe()), reinterpret_cast(out_page_info), process_handle, address); + } - ALWAYS_INLINE Result CreateProcess(::ams::svc::Handle *out_handle, ::ams::svc::UserPointer parameters, ::ams::svc::UserPointer caps, int32_t num_caps) { - return ::svcCreateProcess(out_handle, parameters.GetPointerUnsafe(), caps.GetPointerUnsafe(), num_caps); - } + ALWAYS_INLINE Result MapProcessCodeMemory(::ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) { + return ::svcMapProcessCodeMemory(process_handle, dst_address, src_address, size); + } - ALWAYS_INLINE Result StartProcess(::ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) { - return ::svcStartProcess(process_handle, priority, core_id, main_thread_stack_size); - } + ALWAYS_INLINE Result UnmapProcessCodeMemory(::ams::svc::Handle process_handle, uint64_t dst_address, uint64_t src_address, uint64_t size) { + return ::svcUnmapProcessCodeMemory(process_handle, dst_address, src_address, size); + } - ALWAYS_INLINE Result TerminateProcess(::ams::svc::Handle process_handle) { - return ::svcTerminateProcess(process_handle); - } + ALWAYS_INLINE Result CreateProcess(::ams::svc::Handle *out_handle, ::ams::svc::UserPointer parameters, ::ams::svc::UserPointer caps, int32_t num_caps) { + return ::svcCreateProcess(out_handle, parameters.GetPointerUnsafe(), caps.GetPointerUnsafe(), num_caps); + } - ALWAYS_INLINE Result GetProcessInfo(int64_t *out_info, ::ams::svc::Handle process_handle, ::ams::svc::ProcessInfoType info_type) { - return ::svcGetProcessInfo(out_info, process_handle, static_cast<::ProcessInfoType>(info_type)); - } + ALWAYS_INLINE Result StartProcess(::ams::svc::Handle process_handle, int32_t priority, int32_t core_id, uint64_t main_thread_stack_size) { + return ::svcStartProcess(process_handle, priority, core_id, main_thread_stack_size); + } - ALWAYS_INLINE Result CreateResourceLimit(::ams::svc::Handle *out_handle) { - return ::svcCreateResourceLimit(out_handle); - } + ALWAYS_INLINE Result TerminateProcess(::ams::svc::Handle process_handle) { + return ::svcTerminateProcess(process_handle); + } - ALWAYS_INLINE Result SetResourceLimitLimitValue(::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which, int64_t limit_value) { - return ::svcSetResourceLimitLimitValue(resource_limit_handle, static_cast<::LimitableResource>(which), limit_value); - } + ALWAYS_INLINE Result GetProcessInfo(int64_t *out_info, ::ams::svc::Handle process_handle, ::ams::svc::ProcessInfoType info_type) { + return ::svcGetProcessInfo(out_info, process_handle, static_cast<::ProcessInfoType>(info_type)); + } - ALWAYS_INLINE void CallSecureMonitor(::ams::svc::lp64::SecureMonitorArguments *args) { - ::svcCallSecureMonitor(reinterpret_cast<::SecmonArgs *>(args)); - } + ALWAYS_INLINE Result CreateResourceLimit(::ams::svc::Handle *out_handle) { + return ::svcCreateResourceLimit(out_handle); + } + + ALWAYS_INLINE Result SetResourceLimitLimitValue(::ams::svc::Handle resource_limit_handle, ::ams::svc::LimitableResource which, int64_t limit_value) { + return ::svcSetResourceLimitLimitValue(resource_limit_handle, static_cast<::LimitableResource>(which), limit_value); + } + + ALWAYS_INLINE void CallSecureMonitor(::ams::svc::lp64::SecureMonitorArguments *args) { + ::svcCallSecureMonitor(reinterpret_cast<::SecmonArgs *>(args)); + } + + } -} + #endif + + ALWAYS_INLINE bool IsKernelMesosphere() { + uint64_t dummy; + return R_SUCCEEDED(::ams::svc::GetInfo(std::addressof(dummy), ::ams::svc::InfoType_MesosphereMeta, ::ams::svc::InvalidHandle, ::ams::svc::MesosphereMetaInfo_KernelVersion)); + } + + } #endif \ No newline at end of file diff --git a/libraries/libstratosphere/include/stratosphere/wec.hpp b/libraries/libstratosphere/include/stratosphere/wec.hpp new file mode 100644 index 000000000..fb0cda14e --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/wec.hpp @@ -0,0 +1,19 @@ +/* + * 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 . + */ + +#pragma once +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/wec/wec_api.hpp b/libraries/libstratosphere/include/stratosphere/wec/wec_api.hpp new file mode 100644 index 000000000..22b85f845 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/wec/wec_api.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 . + */ +#pragma once +#include +#include + +namespace ams::wec { + + void Initialize(); + void ClearWakeEvents(); + + void WecRestoreForExitSuspend(); + + void SetWakeEventLevel(wec::WakeEvent event, wec::WakeEventLevel level); + void SetWakeEventEnabled(wec::WakeEvent event, bool en); + +} diff --git a/libraries/libstratosphere/include/stratosphere/wec/wec_types.hpp b/libraries/libstratosphere/include/stratosphere/wec/wec_types.hpp new file mode 100644 index 000000000..7185a73b3 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/wec/wec_types.hpp @@ -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 . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include + +#else + + #error "Unknown board for ams::wec::WakeEvent" + +#endif + +namespace ams::wec { + + enum WakeEventLevel { + WakeEventLevel_Low = 0, + WakeEventLevel_High = 1, + WakeEventLevel_Auto = 2, + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/wec/wec_wake_event.board.nintendo_nx.hpp b/libraries/libstratosphere/include/stratosphere/wec/wec_wake_event.board.nintendo_nx.hpp new file mode 100644 index 000000000..80012f9c1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/wec/wec_wake_event.board.nintendo_nx.hpp @@ -0,0 +1,88 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::wec { + + enum WakeEvent { + WakeEvent_PexWakeN = 0x00, + WakeEvent_GpioPortA6 = 0x01, + WakeEvent_QspiCsN = 0x02, + WakeEvent_Spi2Mosi = 0x03, + WakeEvent_ExtconDetS = 0x04, + WakeEvent_McuIrq = 0x05, + WakeEvent_Uart2Cts = 0x06, + WakeEvent_Uart3Cts = 0x07, + WakeEvent_WifiWakeAp = 0x08, + WakeEvent_AoTag2Pmc = 0x09, + WakeEvent_ExtconDetU = 0x0A, + WakeEvent_NfcInt = 0x0B, + WakeEvent_Gen1I2cSda = 0x0C, + WakeEvent_Gen2I2cSda = 0x0D, + WakeEvent_CradleIrq = 0x0E, + WakeEvent_GpioPortK6 = 0x0F, + WakeEvent_RtcIrq = 0x10, + WakeEvent_Sdmmc1Dat1 = 0x11, + WakeEvent_Sdmmc2Dat1 = 0x12, + WakeEvent_HdmiCec = 0x13, + WakeEvent_Gen3I2cSda = 0x14, + WakeEvent_GpioPortL1 = 0x15, + WakeEvent_Clk_32kOut = 0x16, + WakeEvent_PwrI2cSda = 0x17, + WakeEvent_ButtonPowerOn = 0x18, + WakeEvent_ButtonVolUp = 0x19, + WakeEvent_ButtonVolDown = 0x1A, + WakeEvent_ButtonSlideSw = 0x1B, + WakeEvent_ButtonHome = 0x1C, + /* ... */ + WakeEvent_AlsProxInt = 0x20, + WakeEvent_TempAlert = 0x21, + WakeEvent_Bq24190Irq = 0x22, + WakeEvent_SdCd = 0x23, + WakeEvent_GpioPortZ2 = 0x24, + /* ... */ + WakeEvent_Utmip0 = 0x27, + WakeEvent_Utmip1 = 0x28, + WakeEvent_Utmip2 = 0x29, + WakeEvent_Utmip3 = 0x2A, + WakeEvent_Uhsic = 0x2B, + WakeEvent_Wake2PmcXusbSystem = 0x2C, + WakeEvent_Sdmmc3Dat1 = 0x2D, + WakeEvent_Sdmmc4Dat1 = 0x2E, + WakeEvent_CamI2cScl = 0x2F, + WakeEvent_CamI2cSda = 0x30, + WakeEvent_GpioPortZ5 = 0x31, + WakeEvent_DpHpd0 = 0x32, + WakeEvent_PwrIntN = 0x33, + WakeEvent_BtWakeAp = 0x34, + WakeEvent_HdmiIntDpHpd = 0x35, + WakeEvent_UsbVbusEn0 = 0x36, + WakeEvent_UsbVbusEn1 = 0x37, + WakeEvent_LcdRst = 0x38, + WakeEvent_LcdGpio1 = 0x39, + WakeEvent_LcdGpio2 = 0x3A, + WakeEvent_Uart4Cts = 0x3B, + WakeEvent_ModemWakeAp = 0x3D, + WakeEvent_TouchInt = 0x3E, + WakeEvent_MotionInt = 0x3F, + + WakeEvent_Count = 0x40, + }; + + constexpr inline WakeEvent WakeEvent_None = static_cast(-1); + +} diff --git a/libraries/libstratosphere/source/ams/ams_bpc.h b/libraries/libstratosphere/source/ams/ams_bpc.h index 93da63092..f6825c418 100644 --- a/libraries/libstratosphere/source/ams/ams_bpc.h +++ b/libraries/libstratosphere/source/ams/ams_bpc.h @@ -15,9 +15,7 @@ */ #pragma once -#include -#include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/libraries/libstratosphere/source/ams/ams_environment.cpp b/libraries/libstratosphere/source/ams/ams_environment.cpp index 67e60b853..3a0bb3ed9 100644 --- a/libraries/libstratosphere/source/ams/ams_environment.cpp +++ b/libraries/libstratosphere/source/ams/ams_environment.cpp @@ -74,7 +74,10 @@ namespace ams { ams_ctx.pc = ctx->pc.x; ams_ctx.pstate = ctx->pstate; ams_ctx.afsr0 = ctx->afsr0; - ams_ctx.afsr1 = ctx->afsr1; + ams_ctx.afsr1 = (static_cast(::ams::exosphere::GetVersion(ATMOSPHERE_RELEASE_VERSION)) << 32) | static_cast(hos::GetVersion()); + if (svc::IsKernelMesosphere()) { + ams_ctx.afsr1 |= (static_cast('M') << (BITSIZEOF(u64) - BITSIZEOF(u8))); + } ams_ctx.far = ctx->far.x; ams_ctx.report_identifier = armGetSystemTick(); diff --git a/libraries/libstratosphere/source/boot2/boot2_api.cpp b/libraries/libstratosphere/source/boot2/boot2_api.cpp index c2c050f39..ad56ffac0 100644 --- a/libraries/libstratosphere/source/boot2/boot2_api.cpp +++ b/libraries/libstratosphere/source/boot2/boot2_api.cpp @@ -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); } } @@ -353,10 +352,12 @@ namespace ams::boot2 { /* Wait for other atmosphere mitm modules to initialize. */ R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("set:sys"))); - if (hos::GetVersion() >= hos::Version_2_0_0) { - R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("bpc"))); - } else { - R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("bpc:c"))); + if (spl::GetSocType() == spl::SocType_Erista) { + if (hos::GetVersion() >= hos::Version_2_0_0) { + R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("bpc"))); + } else { + R_ABORT_UNLESS(sm::mitm::WaitMitm(sm::ServiceName::Encode("bpc:c"))); + } } /* Launch Atmosphere boot2, using NcmStorageId_None to force SD card boot. */ diff --git a/libraries/libstratosphere/source/cal/cal_battery_api.cpp b/libraries/libstratosphere/source/cal/cal_battery_api.cpp new file mode 100644 index 000000000..3de3dce60 --- /dev/null +++ b/libraries/libstratosphere/source/cal/cal_battery_api.cpp @@ -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 . + */ +#include +#include "cal_fs_utils.hpp" + +namespace ams::cal { + + namespace { + + constexpr inline s64 BatteryLotOffset = 0x2CE0; + constexpr inline size_t BatteryLotSize = 0x20; + + constexpr inline s64 BatteryVersionOffset = 0x4310; + constexpr inline size_t BatteryVersionSize = 0x10; + + constexpr inline size_t BatteryVendorSizeMax = 0x18; + + } + + Result GetBatteryVersion(u8 *out) { + /* Read the battery version. */ + u8 battery_version[BatteryVersionSize]; + R_TRY(cal::impl::ReadCalibrationBlock(BatteryVersionOffset, battery_version, sizeof(battery_version))); + + /* Write the output. */ + *out = battery_version[0]; + return ResultSuccess(); + } + + Result GetBatteryVendor(size_t *out_vendor_size, void *dst, size_t dst_size) { + /* Read the battery lot. */ + char battery_lot[BatteryLotSize]; + R_TRY(cal::impl::ReadCalibrationBlock(BatteryLotOffset, battery_lot, sizeof(battery_lot))); + + /* Copy output. */ + *out_vendor_size = static_cast(util::Strlcpy(static_cast(dst), battery_lot, std::min(dst_size, BatteryVendorSizeMax))); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/cal/cal_crc_utils.cpp b/libraries/libstratosphere/source/cal/cal_crc_utils.cpp new file mode 100644 index 000000000..811aa37db --- /dev/null +++ b/libraries/libstratosphere/source/cal/cal_crc_utils.cpp @@ -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 . + */ +#include +#include "cal_crc_utils.hpp" + +namespace ams::cal::impl { + + namespace { + + constexpr inline const u16 CrcTable[0x10] = { + 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, + 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400 + }; + + } + + u16 CalculateCrc16(const void *data, size_t size) { + AMS_ASSERT(data != nullptr); + + u16 crc = 0x55AA; + const u8 *data_u8 = static_cast(data); + for (size_t i = 0; i < size; ++i) { + crc = (crc >> 4) ^ (CrcTable[crc & 0xF]) ^ (CrcTable[(data_u8[i] >> 0) & 0xF]); + crc = (crc >> 4) ^ (CrcTable[crc & 0xF]) ^ (CrcTable[(data_u8[i] >> 4) & 0xF]); + } + + return crc; + } + + Result ValidateCalibrationCrc(const void *data, size_t size) { + AMS_ASSERT(data != nullptr); + AMS_ASSERT(size >= sizeof(u16)); + + const u16 crc = *reinterpret_cast(reinterpret_cast(data) + size - sizeof(u16)); + R_UNLESS(CalculateCrc16(data, size - sizeof(u16)) == crc, cal::ResultCalibrationDataCrcError()); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/cal/cal_crc_utils.hpp b/libraries/libstratosphere/source/cal/cal_crc_utils.hpp new file mode 100644 index 000000000..2e2c4f857 --- /dev/null +++ b/libraries/libstratosphere/source/cal/cal_crc_utils.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::cal::impl { + + u16 CalculateCrc16(const void *data, size_t size); + Result ValidateCalibrationCrc(const void *data, size_t size); + +} diff --git a/libraries/libstratosphere/source/cal/cal_fs_utils.cpp b/libraries/libstratosphere/source/cal/cal_fs_utils.cpp new file mode 100644 index 000000000..70e58bcb6 --- /dev/null +++ b/libraries/libstratosphere/source/cal/cal_fs_utils.cpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#include +#include "cal_crc_utils.hpp" +#include "cal_fs_utils.hpp" + +namespace ams::cal::impl { + + Result ReadCalibrationBlock(s64 offset, void *dst, size_t block_size) { + /* Open the calibration binary partition. */ + std::unique_ptr storage; + R_TRY(fs::OpenBisPartition(std::addressof(storage), fs::BisPartitionId::CalibrationBinary)); + + /* Read data from the partition. */ + R_TRY(storage->Read(offset, dst, block_size)); + + /* Validate the crc. */ + R_TRY(ValidateCalibrationCrc(dst, block_size)); + + return ResultSuccess(); + } + +} diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration.hpp b/libraries/libstratosphere/source/cal/cal_fs_utils.hpp similarity index 87% rename from stratosphere/boot/source/gpio/gpio_initial_configuration.hpp rename to libraries/libstratosphere/source/cal/cal_fs_utils.hpp index b46f17eea..2bea0fe4d 100644 --- a/stratosphere/boot/source/gpio/gpio_initial_configuration.hpp +++ b/libraries/libstratosphere/source/cal/cal_fs_utils.hpp @@ -13,13 +13,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - #pragma once -#include #include -namespace ams::gpio { +namespace ams::cal::impl { - void SetInitialConfiguration(); + Result ReadCalibrationBlock(s64 offset, void *dst, size_t block_size); } diff --git a/libraries/libstratosphere/source/dd/dd_device_address_space.cpp b/libraries/libstratosphere/source/dd/dd_device_address_space.cpp new file mode 100644 index 000000000..43f6fee04 --- /dev/null +++ b/libraries/libstratosphere/source/dd/dd_device_address_space.cpp @@ -0,0 +1,190 @@ +/* + * 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 . + */ +#include +#include "impl/dd_device_address_space_impl.hpp" + +namespace ams::dd { + + Result CreateDeviceAddressSpace(DeviceAddressSpaceType *das, u64 address, u64 size) { + /* Check pre-conditions. */ + AMS_ASSERT(util::IsAligned(address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize)); + AMS_ASSERT(size > 0); + + /* Ensure we leave in a consistent state. */ + auto state_guard = SCOPE_GUARD { das->state = DeviceAddressSpaceType::State_NotInitialized; }; + + /* Create the address space. */ + DeviceAddressSpaceHandle handle; + R_TRY(impl::DeviceAddressSpaceImpl::Create(std::addressof(handle), address, size)); + + /* Set the values in the das. */ + das->device_handle = handle; + das->is_handle_managed = true; + das->state = DeviceAddressSpaceType::State_Initialized; + + /* We succeeded. */ + state_guard.Cancel(); + return ResultSuccess(); + } + + Result CreateDeviceAddressSpace(DeviceAddressSpaceType *das, u64 size) { + return CreateDeviceAddressSpace(das, 0, size); + } + + void DestroyDeviceAddressSpace(DeviceAddressSpaceType *das) { + /* Check pre-conditions. */ + AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized); + + /* Destroy the handle. */ + if (das->is_handle_managed) { + impl::DeviceAddressSpaceImpl::Close(das->device_handle); + } + + das->device_handle = 0; + das->is_handle_managed = false; + das->state = DeviceAddressSpaceType::State_NotInitialized; + } + + void AttachDeviceAddressSpaceHandle(DeviceAddressSpaceType *das, Handle handle, bool managed) { + /* Check pre-conditions. */ + AMS_ASSERT(handle != svc::InvalidHandle); + + das->device_handle = handle; + das->is_handle_managed = managed; + das->state = DeviceAddressSpaceType::State_Initialized; + } + + Handle GetDeviceAddressSpaceHandle(DeviceAddressSpaceType *das) { + /* Check pre-conditions. */ + AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized); + + return das->device_handle; + } + + Result MapDeviceAddressSpaceAligned(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address, MemoryPermission device_perm) { + /* Check pre-conditions. */ + AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized); + AMS_ASSERT(util::IsAligned(process_address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(device_address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize)); + AMS_ASSERT(process_address + size > process_address); + AMS_ASSERT(device_address + size > device_address); + AMS_ASSERT(size > 0); + AMS_ASSERT((process_address & (4_MB - 1)) == (device_address & (4_MB - 1))); + + return impl::DeviceAddressSpaceImpl::MapAligned(das->device_handle, process_handle, process_address, size, device_address, device_perm); + } + + Result MapDeviceAddressSpaceNotAligned(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address, MemoryPermission device_perm) { + /* Check pre-conditions. */ + AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized); + AMS_ASSERT(util::IsAligned(process_address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(device_address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize)); + AMS_ASSERT(process_address + size > process_address); + AMS_ASSERT(device_address + size > device_address); + AMS_ASSERT(size > 0); + + return impl::DeviceAddressSpaceImpl::MapNotAligned(das->device_handle, process_handle, process_address, size, device_address, device_perm); + } + + void UnmapDeviceAddressSpace(DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address) { + /* Check pre-conditions. */ + AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized); + AMS_ASSERT(util::IsAligned(process_address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(device_address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize)); + AMS_ASSERT(process_address + size > process_address); + AMS_ASSERT(device_address + size > device_address); + AMS_ASSERT(size > 0); + + return impl::DeviceAddressSpaceImpl::Unmap(das->device_handle, process_handle, process_address, size, device_address); + } + + void InitializeDeviceAddressSpaceMapInfo(DeviceAddressSpaceMapInfo *info, DeviceAddressSpaceType *das, ProcessHandle process_handle, u64 process_address, size_t size, DeviceVirtualAddress device_address, MemoryPermission device_perm) { + /* Check pre-conditions. */ + AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized); + AMS_ASSERT(util::IsAligned(process_address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(device_address, os::MemoryPageSize)); + AMS_ASSERT(util::IsAligned(size, os::MemoryPageSize)); + AMS_ASSERT(process_address + size > process_address); + AMS_ASSERT(device_address + size > device_address); + AMS_ASSERT(size > 0); + + info->last_mapped_size = 0; + info->process_address = process_address; + info->size = size; + info->device_start_address = device_address; + info->device_end_address = device_address + size; + info->process_handle = process_handle; + info->device_permission = device_perm; + info->device_address_space = das; + } + + Result MapNextDeviceAddressSpaceRegion(size_t *out_mapped_size, DeviceAddressSpaceMapInfo *info) { + /* Check pre-conditions. */ + AMS_ASSERT(info->last_mapped_size == 0); + + size_t mapped_size = 0; + if (info->device_start_address < info->device_end_address) { + R_TRY(impl::DeviceAddressSpaceImpl::MapPartially(std::addressof(mapped_size), info->device_address_space->device_handle, info->process_handle, info->process_address, info->size, info->device_start_address, info->device_permission)); + } + + info->last_mapped_size = mapped_size; + *out_mapped_size = mapped_size; + return ResultSuccess(); + } + + void UnmapDeviceAddressSpaceRegion(DeviceAddressSpaceMapInfo *info) { + /* Check pre-conditions. */ + const size_t last_mapped_size = info->last_mapped_size; + AMS_ASSERT(last_mapped_size > 0); + + impl::DeviceAddressSpaceImpl::Unmap(info->device_address_space->device_handle, info->process_handle, info->process_address, last_mapped_size, info->device_start_address); + + info->last_mapped_size = 0; + info->process_address += last_mapped_size; + info->device_start_address += last_mapped_size; + } + + u64 GetMappedProcessAddress(DeviceAddressSpaceMapInfo *info) { + return info->process_address; + } + + DeviceVirtualAddress GetMappedDeviceVirtualAddress(DeviceAddressSpaceMapInfo *info) { + return info->device_start_address; + } + + size_t GetMappedSize(DeviceAddressSpaceMapInfo *info) { + return info->last_mapped_size; + } + + Result AttachDeviceAddressSpace(DeviceAddressSpaceType *das, DeviceName device_name) { + /* Check pre-conditions. */ + AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized); + + return impl::DeviceAddressSpaceImpl::Attach(das, device_name); + } + + void DetachDeviceAddressSpace(DeviceAddressSpaceType *das, DeviceName device_name) { + /* Check pre-conditions. */ + AMS_ASSERT(das->state == DeviceAddressSpaceType::State_Initialized); + + return impl::DeviceAddressSpaceImpl::Detach(das, device_name); + } + +} diff --git a/libraries/libstratosphere/source/dd/dd_io_mappings.cpp b/libraries/libstratosphere/source/dd/dd_io_mappings.cpp deleted file mode 100644 index 8bc84442c..000000000 --- a/libraries/libstratosphere/source/dd/dd_io_mappings.cpp +++ /dev/null @@ -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 . - */ -#include - -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(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); - } - -} diff --git a/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.hpp b/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.hpp new file mode 100644 index 000000000..de47bec3f --- /dev/null +++ b/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.hpp @@ -0,0 +1,23 @@ +/* + * 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 . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_OS_HORIZON) + #include "dd_device_address_space_impl.os.horizon.hpp" +#else + #error "Unknown os for dd::DeviceAddressSpaceImpl" +#endif diff --git a/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.os.horizon.cpp b/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.os.horizon.cpp new file mode 100644 index 000000000..e485de164 --- /dev/null +++ b/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.os.horizon.cpp @@ -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 . + */ +#include +#include "dd_device_address_space_impl.os.horizon.hpp" + +namespace ams::dd::impl { + + static_assert(static_cast(dd::MemoryPermission_None) == static_cast(svc::MemoryPermission_None)); + static_assert(static_cast(dd::MemoryPermission_ReadOnly) == static_cast(svc::MemoryPermission_Read)); + static_assert(static_cast(dd::MemoryPermission_WriteOnly) == static_cast(svc::MemoryPermission_Write)); + static_assert(static_cast(dd::MemoryPermission_ReadWrite) == static_cast(svc::MemoryPermission_ReadWrite)); + + Result DeviceAddressSpaceImplByHorizon::Create(DeviceAddressSpaceHandle *out, u64 address, u64 size) { + /* Create the space. */ + svc::Handle handle; + R_TRY_CATCH(svc::CreateDeviceAddressSpace(std::addressof(handle), address, size)) { + R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory()) + R_CONVERT(svc::ResultOutOfResource, dd::ResultOutOfResource()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + *out = static_cast(handle); + return ResultSuccess(); + } + + void DeviceAddressSpaceImplByHorizon::Close(DeviceAddressSpaceHandle handle) { + const auto svc_handle = svc::Handle(handle); + if (svc_handle == svc::PseudoHandle::CurrentThread || svc_handle == svc::PseudoHandle::CurrentProcess) { + return; + } + + R_ABORT_UNLESS(svc::CloseHandle(svc_handle)); + } + + Result DeviceAddressSpaceImplByHorizon::MapAligned(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm) { + /* Check alignment. */ + AMS_ABORT_UNLESS((process_address & (4_MB - 1)) == (device_address & (4_MB - 1))); + + R_TRY_CATCH(svc::MapDeviceAddressSpaceAligned(svc::Handle(handle), svc::Handle(process_handle), process_address, process_size, device_address, static_cast(device_perm))) { + R_CONVERT(svc::ResultInvalidHandle, dd::ResultInvalidHandle()) + R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory()) + R_CONVERT(svc::ResultOutOfResource, dd::ResultOutOfResource()) + R_CONVERT(svc::ResultInvalidCurrentMemory, dd::ResultInvalidMemoryState()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return ResultSuccess(); + } + + Result DeviceAddressSpaceImplByHorizon::MapNotAligned(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm) { + R_TRY_CATCH(svc::MapDeviceAddressSpaceByForce(svc::Handle(handle), svc::Handle(process_handle), process_address, process_size, device_address, static_cast(device_perm))) { + R_CONVERT(svc::ResultInvalidHandle, dd::ResultInvalidHandle()) + R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory()) + R_CONVERT(svc::ResultOutOfResource, dd::ResultOutOfResource()) + R_CONVERT(svc::ResultInvalidCurrentMemory, dd::ResultInvalidMemoryState()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return ResultSuccess(); + } + + Result DeviceAddressSpaceImplByHorizon::MapPartially(size_t *out_mapped_size, DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm) { + ams::svc::Size mapped_size = 0; + R_TRY_CATCH(svc::MapDeviceAddressSpace(std::addressof(mapped_size), svc::Handle(handle), svc::Handle(process_handle), process_address, process_size, device_address, static_cast(device_perm))) { + R_CONVERT(svc::ResultInvalidHandle, dd::ResultInvalidHandle()) + R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory()) + R_CONVERT(svc::ResultOutOfResource, dd::ResultOutOfResource()) + R_CONVERT(svc::ResultInvalidCurrentMemory, dd::ResultInvalidMemoryState()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + *out_mapped_size = mapped_size; + return ResultSuccess(); + } + + void DeviceAddressSpaceImplByHorizon::Unmap(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address) { + R_ABORT_UNLESS(svc::UnmapDeviceAddressSpace(svc::Handle(handle), svc::Handle(process_handle), process_address, process_size, device_address)); + } + + Result DeviceAddressSpaceImplByHorizon::Attach(DeviceAddressSpaceType *das, DeviceName device_name) { + R_TRY_CATCH(svc::AttachDeviceAddressSpace(static_cast(device_name), svc::Handle(das->device_handle))) { + R_CONVERT(svc::ResultOutOfMemory, dd::ResultOutOfMemory()) + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + + return ResultSuccess(); + } + + void DeviceAddressSpaceImplByHorizon::Detach(DeviceAddressSpaceType *das, DeviceName device_name) { + R_ABORT_UNLESS(svc::DetachDeviceAddressSpace(static_cast(device_name), svc::Handle(das->device_handle))); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.os.horizon.hpp b/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.os.horizon.hpp new file mode 100644 index 000000000..86225d430 --- /dev/null +++ b/libraries/libstratosphere/source/dd/impl/dd_device_address_space_impl.os.horizon.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::dd::impl { + + class DeviceAddressSpaceImplByHorizon { + public: + static Result Create(DeviceAddressSpaceHandle *out, u64 address, u64 size); + static void Close(DeviceAddressSpaceHandle handle); + + static Result MapAligned(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm); + static Result MapNotAligned(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm); + static Result MapPartially(size_t *out_mapped_size, DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address, dd::MemoryPermission device_perm); + static void Unmap(DeviceAddressSpaceHandle handle, ProcessHandle process_handle, u64 process_address, size_t process_size, DeviceVirtualAddress device_address); + + static Result Attach(DeviceAddressSpaceType *das, DeviceName device_name); + static void Detach(DeviceAddressSpaceType *das, DeviceName device_name); + }; + + using DeviceAddressSpaceImpl = DeviceAddressSpaceImplByHorizon; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp b/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp new file mode 100644 index 000000000..93f37ea9d --- /dev/null +++ b/libraries/libstratosphere/source/ddsf/ddsf_device_code_entry_manager.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ddsf { + + Result DeviceCodeEntryManager::Add(DeviceCode device_code, IDevice *device) { + /* Check pre-conditions. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(device->IsDriverAttached()); + + /* Acquire exclusive access to the manager. */ + std::scoped_lock lk(this->entry_list_lock); + + /* Check that we don't already have an entry with the code. */ + for (const auto &holder : this->entry_list) { + AMS_ASSERT(holder.IsConstructed()); + AMS_ASSERT(holder.Get().GetDeviceCode() != device_code); + } + + /* Allocate memory for a new device code entry holder. */ + void *holder_storage = this->memory_resource->Allocate(sizeof(DeviceCodeEntryHolder)); + R_UNLESS(holder_storage != nullptr, ddsf::ResultOutOfResource()); + + /* Initialize the new holder. */ + auto *holder = new (static_cast(holder_storage)) DeviceCodeEntryHolder; + holder->Construct(device_code, device); + + /* Link the new holder. */ + holder->AddTo(this->entry_list); + + return ResultSuccess(); + } + + bool DeviceCodeEntryManager::Remove(DeviceCode device_code) { + /* Acquire exclusive access to the manager. */ + std::scoped_lock lk(this->entry_list_lock); + + /* Find and erase the entry. */ + bool erased = false; + for (auto it = this->entry_list.begin(); it != this->entry_list.end(); /* ... */) { + /* Get the current entry, and advance the iterator. */ + DeviceCodeEntryHolder *cur = std::addressof(*(it++)); + + /* If the entry matches the device code, remove it. */ + AMS_ASSERT(cur->IsConstructed()); + if (cur->Get().GetDeviceCode() == device_code) { + /* Destroy and deallocate the holder. */ + cur->Destroy(); + cur->~DeviceCodeEntryHolder(); + this->memory_resource->Deallocate(cur, sizeof(*cur)); + + erased = true; + } + } + + return erased; + } + + Result DeviceCodeEntryManager::FindDeviceCodeEntry(DeviceCodeEntry **out, DeviceCode device_code) { + /* Check arguments. */ + AMS_ASSERT(out != nullptr); + R_UNLESS(out != nullptr, ddsf::ResultInvalidArgument()); + + /* Find the device. */ + bool found = false; + this->ForEachEntry([&](DeviceCodeEntry &entry) -> bool { + if (entry.GetDeviceCode() == device_code) { + found = true; + *out = std::addressof(entry); + return false; + } + return true; + }); + + /* Check that we found the device. */ + R_UNLESS(found, ddsf::ResultDeviceCodeNotFound()); + + return ResultSuccess(); + } + + Result DeviceCodeEntryManager::FindDeviceCodeEntry(const DeviceCodeEntry **out, DeviceCode device_code) const { + /* Check arguments. */ + AMS_ASSERT(out != nullptr); + R_UNLESS(out != nullptr, ddsf::ResultInvalidArgument()); + + /* Find the device. */ + bool found = false; + this->ForEachEntry([&](const DeviceCodeEntry &entry) -> bool { + if (entry.GetDeviceCode() == device_code) { + found = true; + *out = std::addressof(entry); + return false; + } + return true; + }); + + /* Check that we found the device. */ + R_UNLESS(found, ddsf::ResultDeviceCodeNotFound()); + + return ResultSuccess(); + } + + Result DeviceCodeEntryManager::FindDevice(IDevice **out, DeviceCode device_code) { + /* Check pre-conditions. */ + AMS_ASSERT(out != nullptr); + + /* Find the entry. */ + DeviceCodeEntry *entry; + R_TRY(this->FindDeviceCodeEntry(std::addressof(entry), device_code)); + + /* Set the output. */ + if (out != nullptr) { + *out = std::addressof(entry->GetDevice()); + } + + return ResultSuccess(); + } + + Result DeviceCodeEntryManager::FindDevice(const IDevice **out, DeviceCode device_code) const { + /* Check pre-conditions. */ + AMS_ASSERT(out != nullptr); + + /* Find the entry. */ + const DeviceCodeEntry *entry; + R_TRY(this->FindDeviceCodeEntry(std::addressof(entry), device_code)); + + /* Set the output. */ + if (out != nullptr) { + *out = std::addressof(entry->GetDevice()); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp b/libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp new file mode 100644 index 000000000..490d26f2e --- /dev/null +++ b/libraries/libstratosphere/source/ddsf/ddsf_event_handler.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ddsf { + + namespace { + + enum class LoopControlCommand { + None = 0, + Register = 1, + Unregister = 2, + Terminate = 3, + }; + + } + + struct EventHandlerManager::LoopControlCommandParameters { + LoopControlCommand command; + IEventHandler *target; + + LoopControlCommandParameters() : command(LoopControlCommand::None), target(nullptr) { /* ... */ } + LoopControlCommandParameters(LoopControlCommand c, IEventHandler *t) : command(c), target(t) { /* ... */ } + }; + + void EventHandlerManager::Initialize() { + /* Check that we're not already initialized. */ + if (this->is_initialized) { + return; + } + + /* Initialize waitable manager/holder. */ + os::InitializeWaitableManager(std::addressof(this->waitable_manager)); + os::InitializeWaitableHolder(std::addressof(this->loop_control_event_holder), this->loop_control_event.GetBase()); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->loop_control_event_holder)); + + this->is_initialized = true; + } + + void EventHandlerManager::Finalize() { + /* Check that we're initialized and not looping. */ + AMS_ASSERT(!this->is_looping); + AMS_ASSERT(this->is_initialized); + if (!this->is_initialized) { + return; + } + + /* Finalize waitable manager/holder. */ + os::UnlinkWaitableHolder(std::addressof(this->loop_control_event_holder)); + os::FinalizeWaitableHolder(std::addressof(this->loop_control_event_holder)); + os::FinalizeWaitableManager(std::addressof(this->waitable_manager)); + + this->is_initialized = false; + } + + void EventHandlerManager::ProcessControlCommand(LoopControlCommandParameters *params) { + /* Check pre-conditions. */ + AMS_ASSERT(this->is_initialized); + AMS_ASSERT(params != nullptr); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->loop_control_lock); + + /* If we're processing for the loop thread, we can directly handle. */ + if (!this->is_looping || this->IsRunningOnLoopThread()) { + this->ProcessControlCommandImpl(params); + } else { + /* Otherwise, signal to the loop thread. */ + this->loop_control_command_params = params; + this->loop_control_event.Signal(); + this->loop_control_command_done_event.Wait(); + } + } + + void EventHandlerManager::ProcessControlCommandImpl(LoopControlCommandParameters *params) { + /* Check pre-conditions. */ + AMS_ASSERT(this->loop_control_lock.IsLockedByCurrentThread() || !this->loop_control_lock.TryLock()); + AMS_ASSERT(params != nullptr); + AMS_ASSERT(params->target != nullptr); + + /* Process the command. */ + switch (params->command) { + case LoopControlCommand::Register: + params->target->Link(std::addressof(this->waitable_manager)); + break; + case LoopControlCommand::Unregister: + params->target->Unlink(); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void EventHandlerManager::RegisterHandler(IEventHandler *handler) { + /* Check that the handler is valid. */ + AMS_ASSERT(handler != nullptr); + AMS_ASSERT(handler->IsInitialized()); + + /* Send registration command. */ + LoopControlCommandParameters params(LoopControlCommand::Register, handler); + return this->ProcessControlCommand(std::addressof(params)); + } + + void EventHandlerManager::UnregisterHandler(IEventHandler *handler) { + /* Check that the handler is valid. */ + AMS_ASSERT(handler != nullptr); + AMS_ASSERT(handler->IsInitialized()); + + /* Send registration command. */ + LoopControlCommandParameters params(LoopControlCommand::Unregister, handler); + return this->ProcessControlCommand(std::addressof(params)); + } + + void EventHandlerManager::WaitLoopEnter() { + /* Acquire exclusive access. */ + std::scoped_lock lk(this->loop_control_lock); + + /* Wait until we're looping. */ + while (!this->is_looping) { + this->is_looping_cv.Wait(this->loop_control_lock); + } + } + + void EventHandlerManager::WaitLoopExit() { + /* Acquire exclusive access. */ + std::scoped_lock lk(this->loop_control_lock); + + /* Wait until we're not looping. */ + while (this->is_looping) { + this->is_looping_cv.Wait(this->loop_control_lock); + } + } + + void EventHandlerManager::RequestStop() { + /* Check that we're looping and not the loop thread. */ + AMS_ASSERT(this->is_looping); + AMS_ASSERT(!this->IsRunningOnLoopThread()); + + if (this->is_looping) { + /* Acquire exclusive access. */ + std::scoped_lock lk(this->loop_control_lock); + + /* Signal to the loop thread. */ + LoopControlCommandParameters params(LoopControlCommand::Terminate, nullptr); + this->loop_control_command_params = std::addressof(params); + this->loop_control_event.Signal(); + this->loop_control_command_done_event.Wait(); + } + } + + void EventHandlerManager::LoopAuto() { + /* Check that we're not already looping. */ + AMS_ASSERT(!this->is_looping); + + /* Begin looping with the current thread. */ + this->loop_thread = os::GetCurrentThread(); + this->is_looping = true; + this->is_looping_cv.Broadcast(); + + /* Whenever we're done looping, clean up. */ + ON_SCOPE_EXIT { + this->loop_thread = nullptr; + this->is_looping = false; + this->is_looping_cv.Broadcast(); + }; + + /* Loop until we're asked to stop. */ + bool should_terminate = false; + while (!should_terminate) { + /* Wait for a holder to be signaled. */ + os::WaitableHolderType *event_holder = os::WaitAny(std::addressof(this->waitable_manager)); + AMS_ASSERT(event_holder != nullptr); + + /* Check if we have a request to handle. */ + if (event_holder == std::addressof(this->loop_control_event_holder)) { + /* Check that the request hasn't already been handled. */ + if (this->loop_control_event.TryWait()) { + /* Handle the request. */ + AMS_ASSERT(this->loop_control_command_params != nullptr); + switch (this->loop_control_command_params->command) { + case LoopControlCommand::Register: + case LoopControlCommand::Unregister: + this->ProcessControlCommandImpl(this->loop_control_command_params); + break; + case LoopControlCommand::Terminate: + should_terminate = true; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Clear the request, and signal that it's done. */ + this->loop_control_command_params = nullptr; + this->loop_control_command_done_event.Signal(); + } + } else { + /* Handle the event. */ + IEventHandler::ToEventHandler(event_holder).HandleEvent(); + } + } + } + +} diff --git a/libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp b/libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp new file mode 100644 index 000000000..d81155306 --- /dev/null +++ b/libraries/libstratosphere/source/ddsf/ddsf_memory_api.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ddsf { + + namespace { + + constinit ams::MemoryResource *g_memory_resource = nullptr; + constinit ams::MemoryResource *g_device_code_entry_holder_memory_resource = nullptr; + + } + + void SetMemoryResource(ams::MemoryResource *mr) { + AMS_ASSERT(g_memory_resource == nullptr); + g_memory_resource = mr; + AMS_ASSERT(g_memory_resource != nullptr); + } + + ams::MemoryResource *GetMemoryResource() { + AMS_ASSERT(g_memory_resource != nullptr); + return g_memory_resource; + } + + void SetDeviceCodeEntryHolderMemoryResource(ams::MemoryResource *mr) { + AMS_ASSERT(g_device_code_entry_holder_memory_resource == nullptr); + g_device_code_entry_holder_memory_resource = mr; + AMS_ASSERT(g_device_code_entry_holder_memory_resource != nullptr); + } + + ams::MemoryResource *GetDeviceCodeEntryHolderMemoryResource() { + AMS_ASSERT(g_device_code_entry_holder_memory_resource != nullptr); + return g_device_code_entry_holder_memory_resource; + } + +} diff --git a/libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp b/libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp new file mode 100644 index 000000000..48a265f62 --- /dev/null +++ b/libraries/libstratosphere/source/ddsf/ddsf_session_api.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::ddsf { + + Result OpenSession(IDevice *device, ISession *session, AccessMode access_mode) { + /* Check pre-conditions. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(session != nullptr); + AMS_ASSERT(!session->IsOpen()); + + /* Attack the session to the device. */ + session->AttachDevice(device, access_mode); + auto session_guard = SCOPE_GUARD { session->DetachDevice(); }; + + /* Attach the device to the session. */ + R_TRY(device->AttachSession(session)); + + /* We succeeded. */ + session_guard.Cancel(); + return ResultSuccess(); + } + + void CloseSession(ISession *session) { + /* Check pre-conditions. */ + AMS_ASSERT(session != nullptr); + + /* Detach the device from the session. */ + session->GetDevice().DetachSession(session); + + /* Detach the session from the device. */ + session->DetachDevice(); + } + +} diff --git a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp index 4108343ab..11f6f4d20 100644 --- a/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp +++ b/libraries/libstratosphere/source/diag/diag_assertion_impl.cpp @@ -43,7 +43,7 @@ namespace ams::diag { inline void DebugLog(const char *format, ...) __attribute__((format(printf, 1, 2))); -#ifdef AMS_ENABLE_DEBUG_PRINT +#ifdef AMS_ENABLE_DETAILED_ASSERTIONS os::Mutex g_debug_log_lock(true); char g_debug_buffer[0x400]; @@ -73,9 +73,9 @@ namespace ams::diag { DebugLog(" Location: %s:%d\n", file, line); DebugLog(" Function: %s\n", func); DebugLog(" Expression: %s\n", expr); - DebugLog(" Value: %016lx\n", value); + DebugLog(" Value: %016" PRIx64 "\n", value); DebugLog("\n"); -#ifdef AMS_ENABLE_DEBUG_PRINT +#ifdef AMS_ENABLE_DETAILED_ASSERTIONS { ::std::va_list vl; va_start(vl, format); @@ -93,7 +93,7 @@ namespace ams::diag { DebugLog(" Location: %s:%d\n", file, line); DebugLog(" Function: %s\n", func); DebugLog(" Expression: %s\n", expr); - DebugLog(" Value: %016lx\n", value); + DebugLog(" Value: %016" PRIx64 "\n", value); DebugLog("\n"); DebugLog("\n"); @@ -105,9 +105,9 @@ namespace ams::diag { DebugLog(" Location: %s:%d\n", file, line); DebugLog(" Function: %s\n", func); DebugLog(" Expression: %s\n", expr); - DebugLog(" Value: %016lx\n", value); + DebugLog(" Value: %016" PRIx64 "\n", value); DebugLog("\n"); -#ifdef AMS_ENABLE_DEBUG_PRINT +#ifdef AMS_ENABLE_DETAILED_ASSERTIONS { ::std::va_list vl; va_start(vl, format); @@ -125,7 +125,7 @@ namespace ams::diag { DebugLog(" Location: %s:%d\n", file, line); DebugLog(" Function: %s\n", func); DebugLog(" Expression: %s\n", expr); - DebugLog(" Value: %016lx\n", value); + DebugLog(" Value: %016" PRIx64 "\n", value); DebugLog("\n"); DebugLog("\n"); diff --git a/libraries/libstratosphere/source/dmnt/dmntcht.h b/libraries/libstratosphere/source/dmnt/dmntcht.h index ef19824e0..62f895736 100644 --- a/libraries/libstratosphere/source/dmnt/dmntcht.h +++ b/libraries/libstratosphere/source/dmnt/dmntcht.h @@ -15,9 +15,7 @@ */ #pragma once -#include -#include -#include +#include #ifdef __cplusplus extern "C" { diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.cpp index 8eb35a6ae..67d585ecd 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_attachment.cpp @@ -24,7 +24,7 @@ namespace ams::erpt::srv { attachment_id.uuid.ToString(uuid_str, sizeof(uuid_str)); AttachmentFileName attachment_name; - std::snprintf(attachment_name.name, sizeof(attachment_name.name), "%s/%s.att", ReportStoragePath, uuid_str); + std::snprintf(attachment_name.name, sizeof(attachment_name.name), "%s:/%s.att", ReportStoragePath, uuid_str); return attachment_name; } diff --git a/libraries/libstratosphere/source/fs/fs_code.cpp b/libraries/libstratosphere/source/fs/fs_code.cpp index c17245ef2..ddd363779 100644 --- a/libraries/libstratosphere/source/fs/fs_code.cpp +++ b/libraries/libstratosphere/source/fs/fs_code.cpp @@ -20,15 +20,15 @@ namespace ams::fs { namespace { - Result OpenCodeFileSystemImpl(CodeInfo *out_code_info, std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { + Result OpenCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { /* Print a path suitable for the remote service. */ fssrv::sf::Path sf_path; R_TRY(FspPathPrintf(std::addressof(sf_path), "%s", path)); /* Open the filesystem using libnx bindings. */ - static_assert(sizeof(CodeInfo) == sizeof(::FsCodeInfo)); + static_assert(sizeof(CodeVerificationData) == sizeof(::FsCodeInfo)); ::FsFileSystem fs; - R_TRY(fsldrOpenCodeFileSystem(reinterpret_cast<::FsCodeInfo *>(out_code_info), program_id.value, sf_path.str, std::addressof(fs))); + R_TRY(fsldrOpenCodeFileSystem(reinterpret_cast<::FsCodeInfo *>(out_verification_data), program_id.value, sf_path.str, std::addressof(fs))); /* Allocate a new filesystem wrapper. */ auto fsa = std::make_unique(fs); @@ -62,12 +62,12 @@ namespace ams::fs { return OpenPackageFileSystemImpl(out, sf_path.str); } - Result OpenSdCardCodeOrCodeFileSystemImpl(CodeInfo *out_code_info, std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { + Result OpenSdCardCodeOrCodeFileSystemImpl(CodeVerificationData *out_verification_data, std::unique_ptr *out, const char *path, ncm::ProgramId program_id) { /* If we can open an sd card code fs, use it. */ R_SUCCEED_IF(R_SUCCEEDED(OpenSdCardCodeFileSystemImpl(out, program_id))); /* Otherwise, fall back to a normal code fs. */ - return OpenCodeFileSystemImpl(out_code_info, out, path, program_id); + return OpenCodeFileSystemImpl(out_verification_data, out, path, program_id); } Result OpenHblCodeFileSystemImpl(std::unique_ptr *out) { @@ -227,7 +227,7 @@ namespace ams::fs { public: AtmosphereCodeFileSystem() : initialized(false) { /* ... */ } - Result Initialize(CodeInfo *out_code_info, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific) { + Result Initialize(CodeVerificationData *out_verification_data, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific) { AMS_ABORT_UNLESS(!this->initialized); /* If we're hbl, we need to open a hbl fs. */ @@ -239,7 +239,7 @@ namespace ams::fs { /* Open the code filesystem. */ std::unique_ptr fsa; - R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(out_code_info, std::addressof(fsa), path, program_id)); + R_TRY(OpenSdCardCodeOrCodeFileSystemImpl(out_verification_data, std::addressof(fsa), path, program_id)); this->code_fs.emplace(std::move(fsa), program_id, is_specific); this->program_id = program_id; @@ -275,7 +275,7 @@ namespace ams::fs { } - Result MountCode(CodeInfo *out, const char *name, const char *path, ncm::ProgramId program_id) { + Result MountCode(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id) { /* Clear the output. */ std::memset(out, 0, sizeof(*out)); @@ -293,7 +293,7 @@ namespace ams::fs { return fsa::Register(name, std::move(fsa)); } - Result MountCodeForAtmosphereWithRedirection(CodeInfo *out, const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific) { + Result MountCodeForAtmosphereWithRedirection(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id, bool is_hbl, bool is_specific) { /* Clear the output. */ std::memset(out, 0, sizeof(*out)); @@ -314,7 +314,7 @@ namespace ams::fs { return fsa::Register(name, std::move(ams_code_fs)); } - Result MountCodeForAtmosphere(CodeInfo *out, const char *name, const char *path, ncm::ProgramId program_id) { + Result MountCodeForAtmosphere(CodeVerificationData *out, const char *name, const char *path, ncm::ProgramId program_id) { /* Clear the output. */ std::memset(out, 0, sizeof(*out)); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/gpio_driver_api.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/gpio_driver_api.cpp new file mode 100644 index 000000000..513add686 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/gpio_driver_api.cpp @@ -0,0 +1,88 @@ +/* + * 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 . + */ +#include +#include "impl/gpio_driver_impl.hpp" +#include "impl/gpio_initial_config.hpp" +#include "impl/gpio_tegra_pad.hpp" + +namespace ams::gpio::driver::board::nintendo_nx { + + namespace { + + ams::gpio::driver::board::nintendo_nx::impl::DriverImpl *g_driver_impl = nullptr; + + } + + void Initialize(bool enable_interrupt_handlers) { + /* Check that we haven't previously initialized. */ + AMS_ABORT_UNLESS(g_driver_impl == nullptr); + + /* Get the device driver subsystem framework memory resource. */ + auto *memory_resource = ddsf::GetMemoryResource(); + + /* Allocate a new driver. */ + auto *driver_storage = static_cast(memory_resource->Allocate(sizeof(*g_driver_impl))); + AMS_ABORT_UNLESS(driver_storage != nullptr); + + /* Construct the new driver. */ + g_driver_impl = new (driver_storage) ams::gpio::driver::board::nintendo_nx::impl::DriverImpl(impl::GpioRegistersPhysicalAddress, impl::GpioRegistersSize); + + /* Register the driver. */ + gpio::driver::RegisterDriver(g_driver_impl); + + /* Register interrupt handlers, if we should. */ + if (enable_interrupt_handlers) { + for (size_t i = 0; i < util::size(impl::InterruptNameTable); ++i) { + /* Allocate a handler. */ + impl::InterruptEventHandler *handler_storage = static_cast(memory_resource->Allocate(sizeof(impl::InterruptEventHandler))); + AMS_ABORT_UNLESS(handler_storage != nullptr); + + /* Initialize the handler. */ + impl::InterruptEventHandler *handler = new (handler_storage) impl::InterruptEventHandler; + handler->Initialize(g_driver_impl, impl::InterruptNameTable[i], static_cast(i)); + + /* Register the handler. */ + gpio::driver::RegisterInterruptHandler(handler); + } + } + + /* Create and register all pads. */ + for (const auto &entry : impl::PadMapCombinationList) { + /* Allocate a pad for our device. */ + impl::TegraPad *pad_storage = static_cast(memory_resource->Allocate(sizeof(impl::TegraPad))); + AMS_ABORT_UNLESS(pad_storage != nullptr); + + /* Create a pad for our device. */ + impl::TegraPad *pad = new (pad_storage) impl::TegraPad; + pad->SetParameters(entry.internal_number, impl::PadInfo{entry.wake_event}); + + /* Register the pad with our driver. */ + g_driver_impl->RegisterDevice(pad); + + /* Register the device code with our driver. */ + R_ABORT_UNLESS(gpio::driver::RegisterDeviceCode(entry.device_code, pad)); + } + } + + void SetInitialGpioConfig() { + return impl::SetInitialGpioConfig(); + } + + void SetInitialWakePinConfig() { + return impl::SetInitialWakePinConfig(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.cpp new file mode 100644 index 000000000..aa152fb7b --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.cpp @@ -0,0 +1,342 @@ +/* + * 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 . + */ +#include +#include "gpio_driver_impl.hpp" +#include "gpio_register_accessor.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + void InterruptEventHandler::Initialize(DriverImpl *drv, os::InterruptName intr, int ctlr) { + /* Set fields. */ + this->driver = drv; + this->interrupt_name = intr; + this->controller_number = ctlr; + + /* Initialize interrupt event. */ + os::InitializeInterruptEvent(std::addressof(this->interrupt_event), intr, os::EventClearMode_ManualClear); + + /* Initialize base. */ + IEventHandler::Initialize(std::addressof(this->interrupt_event)); + } + + void InterruptEventHandler::HandleEvent() { + /* Lock the driver's interrupt mutex. */ + std::scoped_lock lk(this->driver->interrupt_control_mutex); + + /* Check each pad. */ + bool found = false; + for (auto it = this->driver->interrupt_pad_list.begin(); !found && it != this->driver->interrupt_pad_list.end(); ++it) { + found = this->CheckAndHandleInterrupt(*it); + } + + /* If we didn't find a pad, clear the interrupt event. */ + if (!found) { + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + } + } + + bool InterruptEventHandler::CheckAndHandleInterrupt(TegraPad &pad) { + /* Get the pad's number. */ + const InternalGpioPadNumber pad_number = static_cast(pad.GetPadNumber()); + + /* Check if the pad matches our controller number. */ + if (this->controller_number != ConvertInternalGpioPadNumberToController(pad_number)) { + return false; + } + + /* Get the addresses of INT_STA, INT_ENB. */ + const uintptr_t sta_address = GetGpioRegisterAddress(this->driver->gpio_virtual_address, GpioRegisterType_GPIO_INT_STA, pad_number); + const uintptr_t enb_address = GetGpioRegisterAddress(this->driver->gpio_virtual_address, GpioRegisterType_GPIO_INT_STA, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + /* Check if both STA and ENB are set. */ + if (reg::Read(sta_address, 1u << pad_index) == 0 || reg::Read(enb_address, 1u << pad_index) == 0) { + return false; + } + + /* The pad is signaled. First, clear the enb bit. */ + SetMaskedBit(enb_address, pad_index, 0); + reg::Read(enb_address); + + /* Disable the interrupt on the pad. */ + pad.SetInterruptEnabled(false); + this->driver->RemoveInterruptPad(std::addressof(pad)); + + /* Clear the interrupt event. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + + /* Signal the pad's bound event. */ + pad.SignalInterruptBoundEvent(); + + return true; + } + + DriverImpl::DriverImpl(dd::PhysicalAddress reg_paddr, size_t size) : gpio_physical_address(reg_paddr), gpio_virtual_address(), suspend_handler(this), interrupt_pad_list(), interrupt_control_mutex() { + /* Get the corresponding virtual address for our physical address. */ + this->gpio_virtual_address = dd::QueryIoMapping(reg_paddr, size); + AMS_ABORT_UNLESS(this->gpio_virtual_address != 0); + } + + void DriverImpl::InitializeDriver() { + /* Initialize our suspend handler. */ + this->suspend_handler.Initialize(this->gpio_virtual_address); + } + + void DriverImpl::FinalizeDriver() { + /* ... */ + } + + Result DriverImpl::InitializePad(Pad *pad) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Configure the pad as GPIO by modifying the appropriate bit in CNF. */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_CNF, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, 1); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + + return ResultSuccess(); + } + + void DriverImpl::FinalizePad(Pad *pad) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Nothing to do. */ + AMS_UNUSED(pad); + } + + Result DriverImpl::GetDirection(Direction *out, Pad *pad) const { + /* Validate arguments. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Get the pad direction by reading the appropriate bit in OE */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_OE, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + if (reg::Read(pad_address, 1u << pad_index) != 0) { + *out = Direction_Output; + } else { + *out = Direction_Input; + } + + return ResultSuccess(); + + } + + Result DriverImpl::SetDirection(Pad *pad, Direction direction) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Configure the pad direction by modifying the appropriate bit in OE */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_OE, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, direction); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + + return ResultSuccess(); + } + + Result DriverImpl::GetValue(GpioValue *out, Pad *pad) const { + /* Validate arguments. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Get the pad value by reading the appropriate bit in IN */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_IN, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + if (reg::Read(pad_address, 1u << pad_index) != 0) { + *out = GpioValue_High; + } else { + *out = GpioValue_Low; + } + + return ResultSuccess(); + } + + Result DriverImpl::SetValue(Pad *pad, GpioValue value) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Configure the pad value by modifying the appropriate bit in IN */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_IN, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, value); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + + return ResultSuccess(); + } + + Result DriverImpl::GetInterruptMode(InterruptMode *out, Pad *pad) const { + /* Validate arguments. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Get the pad mode by reading the appropriate bits in INT_LVL */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_INT_LVL, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + switch ((reg::Read(pad_address) >> pad_index) & InternalInterruptMode_Mask) { + case InternalInterruptMode_LowLevel: *out = InterruptMode_LowLevel; break; + case InternalInterruptMode_HighLevel: *out = InterruptMode_HighLevel; break; + case InternalInterruptMode_RisingEdge: *out = InterruptMode_RisingEdge; break; + case InternalInterruptMode_FallingEdge: *out = InterruptMode_FallingEdge; break; + case InternalInterruptMode_AnyEdge: *out = InterruptMode_AnyEdge; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + Result DriverImpl::SetInterruptMode(Pad *pad, InterruptMode mode) { + /* Validate arguments. */ + AMS_ASSERT(pad != nullptr); + + /* Convert the pad to an internal number. */ + const InternalGpioPadNumber pad_number = pad->GetPadNumber(); + + /* Configure the pad mode by modifying the appropriate bits in INT_LVL */ + const uintptr_t pad_address = GetGpioRegisterAddress(this->gpio_virtual_address, GpioRegisterType_GPIO_INT_LVL, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + + switch (mode) { + case InterruptMode_LowLevel: reg::ReadWrite(pad_address, InternalInterruptMode_LowLevel << pad_index, InternalInterruptMode_Mask << pad_index); break; + case InterruptMode_HighLevel: reg::ReadWrite(pad_address, InternalInterruptMode_HighLevel << pad_index, InternalInterruptMode_Mask << pad_index); break; + case InterruptMode_RisingEdge: reg::ReadWrite(pad_address, InternalInterruptMode_RisingEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; + case InterruptMode_FallingEdge: reg::ReadWrite(pad_address, InternalInterruptMode_FallingEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; + case InterruptMode_AnyEdge: reg::ReadWrite(pad_address, InternalInterruptMode_AnyEdge << pad_index, InternalInterruptMode_Mask << pad_index); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + + return ResultSuccess(); + } + + Result DriverImpl::SetInterruptEnabled(Pad *pad, bool en) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::GetInterruptStatus(InterruptStatus *out, Pad *pad) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::ClearInterruptStatus(Pad *pad) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::GetDebounceEnabled(bool *out, Pad *pad) const { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetDebounceEnabled(Pad *pad, bool en) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::GetDebounceTime(s32 *out_ms, Pad *pad) const { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetDebounceTime(Pad *pad, s32 ms) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::GetUnknown22(u32 *out) { + /* TODO */ + AMS_ABORT(); + } + + void DriverImpl::Unknown23() { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetValueForSleepState(Pad *pad, GpioValue value) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::IsWakeEventActive(bool *out, Pad *pad) const { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetWakeEventActiveFlagSetForDebug(Pad *pad, bool en) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SetWakePinDebugMode(WakePinDebugMode mode) { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::Suspend() { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::SuspendLow() { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::Resume() { + /* TODO */ + AMS_ABORT(); + } + + Result DriverImpl::ResumeLow() { + /* TODO */ + AMS_ABORT(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.hpp new file mode 100644 index 000000000..b78f341da --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_driver_impl.hpp @@ -0,0 +1,129 @@ +/* + * 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 . + */ +#pragma once +#include + +#include "gpio_tegra_pad.hpp" +#include "gpio_register_accessor.hpp" +#include "gpio_suspend_handler.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + class DriverImpl; + + class InterruptEventHandler : public ddsf::IEventHandler { + private: + DriverImpl *driver; + os::InterruptName interrupt_name; + os::InterruptEventType interrupt_event; + int controller_number; + private: + bool CheckAndHandleInterrupt(TegraPad &pad); + public: + InterruptEventHandler() : IEventHandler(), driver(nullptr), interrupt_name(), interrupt_event(), controller_number() { /* ... */ } + + void Initialize(DriverImpl *drv, os::InterruptName intr, int ctlr); + + virtual void HandleEvent() override; + }; + + class DriverImpl : public ::ams::gpio::driver::IGpioDriver { + NON_COPYABLE(DriverImpl); + NON_MOVEABLE(DriverImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::board::nintendo_nx::impl::DriverImpl, ::ams::gpio::driver::IGpioDriver); + friend class InterruptEventHandler; + private: + dd::PhysicalAddress gpio_physical_address; + uintptr_t gpio_virtual_address; + SuspendHandler suspend_handler; + TegraPad::InterruptList interrupt_pad_list; + mutable os::SdkMutex interrupt_control_mutex; + public: + DriverImpl(dd::PhysicalAddress reg_paddr, size_t size); + + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result InitializePad(Pad *pad) override; + virtual void FinalizePad(Pad *pad) override; + + virtual Result GetDirection(Direction *out, Pad *pad) const override; + virtual Result SetDirection(Pad *pad, Direction direction) override; + + virtual Result GetValue(GpioValue *out, Pad *pad) const override; + virtual Result SetValue(Pad *pad, GpioValue value) override; + + virtual Result GetInterruptMode(InterruptMode *out, Pad *pad) const override; + virtual Result SetInterruptMode(Pad *pad, InterruptMode mode) override; + + virtual Result SetInterruptEnabled(Pad *pad, bool en) override; + + virtual Result GetInterruptStatus(InterruptStatus *out, Pad *pad) override; + virtual Result ClearInterruptStatus(Pad *pad) override; + + virtual os::SdkMutex &GetInterruptControlMutex(const Pad &pad) const override { + AMS_UNUSED(pad); + return this->interrupt_control_mutex; + } + + virtual Result GetDebounceEnabled(bool *out, Pad *pad) const override; + virtual Result SetDebounceEnabled(Pad *pad, bool en) override; + + virtual Result GetDebounceTime(s32 *out_ms, Pad *pad) const override; + virtual Result SetDebounceTime(Pad *pad, s32 ms) override; + + virtual Result GetUnknown22(u32 *out) override; + virtual void Unknown23() override; + + virtual Result SetValueForSleepState(Pad *pad, GpioValue value) override; + virtual Result IsWakeEventActive(bool *out, Pad *pad) const override; + virtual Result SetWakeEventActiveFlagSetForDebug(Pad *pad, bool en) override; + virtual Result SetWakePinDebugMode(WakePinDebugMode mode) override; + + virtual Result Suspend() override; + virtual Result SuspendLow() override; + virtual Result Resume() override; + virtual Result ResumeLow() override; + private: + static constexpr ALWAYS_INLINE TegraPad &GetTegraPad(Pad *pad) { + AMS_ASSERT(pad != nullptr); + return static_cast(*pad); + } + + static ALWAYS_INLINE const PadInfo &GetInfo(Pad *pad) { + return GetTegraPad(pad).GetInfo(); + } + + static ALWAYS_INLINE PadStatus &GetStatus(Pad *pad) { + return GetTegraPad(pad).GetStatus(); + } + + void AddInterruptPad(TegraPad *pad) { + AMS_ASSERT(pad != nullptr); + if (!pad->IsLinkedToInterruptBoundPadList()) { + this->interrupt_pad_list.push_back(*pad); + } + } + + void RemoveInterruptPad(TegraPad *pad) { + AMS_ASSERT(pad != nullptr); + if (pad->IsLinkedToInterruptBoundPadList()) { + this->interrupt_pad_list.erase(this->interrupt_pad_list.iterator_to(*pad)); + } + } + }; + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.cpp new file mode 100644 index 000000000..d229ea01e --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.cpp @@ -0,0 +1,172 @@ +/* + * 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 . + */ +#include +#include "gpio_driver_impl.hpp" +#include "gpio_initial_config.hpp" +#include "gpio_wake_pin_config.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + namespace { + + spl::HardwareType GetHardwareType() { + /* Acquire access to spl: */ + sm::ScopedServiceHolder spl_holder; + AMS_ABORT_UNLESS(static_cast(spl_holder)); + + /* Get config. */ + return ::ams::spl::GetHardwareType(); + } + + #include "gpio_initial_wake_pin_config_icosa.inc" + /* #include "gpio_initial_wake_pin_config_copper.inc" */ + #include "gpio_initial_wake_pin_config_hoag.inc" + #include "gpio_initial_wake_pin_config_iowa.inc" + #include "gpio_initial_wake_pin_config_calcio.inc" + #include "gpio_initial_wake_pin_config_five.inc" + + #include "gpio_initial_config_icosa.inc" + /* #include "gpio_initial_config_copper.inc" */ + #include "gpio_initial_config_hoag.inc" + #include "gpio_initial_config_iowa.inc" + #include "gpio_initial_config_calcio.inc" + #include "gpio_initial_config_five.inc" + + } + + void SetInitialGpioConfig() { + /* Set wake event levels, wake event enables. */ + const GpioInitialConfig *configs = nullptr; + size_t num_configs = 0; + + /* Select the correct config. */ + switch (GetHardwareType()) { + case spl::HardwareType::Icosa: + configs = InitialGpioConfigsIcosa; + num_configs = NumInitialGpioConfigsIcosa; + break; + case spl::HardwareType::Hoag: + configs = InitialGpioConfigsHoag; + num_configs = NumInitialGpioConfigsHoag; + break; + case spl::HardwareType::Iowa: + configs = InitialGpioConfigsIowa; + num_configs = NumInitialGpioConfigsIowa; + break; + case spl::HardwareType::Calcio: + configs = InitialGpioConfigsCalcio; + num_configs = NumInitialGpioConfigsCalcio; + break; + case spl::HardwareType::_Five_: + configs = InitialGpioConfigsFive; + num_configs = NumInitialGpioConfigsFive; + break; + case spl::HardwareType::Copper: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Check we can use our config. */ + AMS_ABORT_UNLESS(configs != nullptr); + + /* Apply the configs. */ + { + /* Create a driver to use for the duration of our application. */ + DriverImpl driver(GpioRegistersPhysicalAddress, GpioRegistersSize); + driver.InitializeDriver(); + + for (size_t i = 0; i < num_configs; ++i) { + /* Find the internal pad number for our device. */ + bool found = false; + for (const auto &entry : PadMapCombinationList) { + if (entry.device_code == configs[i].device_code) { + /* We found an entry. */ + found = true; + + /* Create a pad for our device. */ + TegraPad pad; + pad.SetParameters(entry.internal_number, PadInfo{entry.wake_event}); + + /* Initialize the pad. */ + R_ABORT_UNLESS(driver.InitializePad(std::addressof(pad))); + + /* Set the direction. */ + R_ABORT_UNLESS(driver.SetDirection(std::addressof(pad), configs[i].direction)); + + /* If the direction is output, set the value. */ + if (configs[i].direction == Direction_Output) { + R_ABORT_UNLESS(driver.SetValue(std::addressof(pad), configs[i].value)); + } + + /* Finalize the pad we made. */ + driver.FinalizePad(std::addressof(pad)); + break; + } + } + + /* Ensure that we applied the config for the pad we wanted. */ + AMS_ABORT_UNLESS(found); + } + + /* Finalize the driver. */ + driver.FinalizeDriver(); + } + } + + void SetInitialWakePinConfig() { + /* Ensure the wec driver is initialized. */ + ams::wec::Initialize(); + + /* Set wake event levels, wake event enables. */ + const WakePinConfig *configs = nullptr; + size_t num_configs = 0; + + /* Select the correct config. */ + switch (GetHardwareType()) { + case spl::HardwareType::Icosa: + configs = InitialWakePinConfigsIcosa; + num_configs = NumInitialWakePinConfigsIcosa; + break; + case spl::HardwareType::Hoag: + configs = InitialWakePinConfigsHoag; + num_configs = NumInitialWakePinConfigsHoag; + break; + case spl::HardwareType::Iowa: + configs = InitialWakePinConfigsIowa; + num_configs = NumInitialWakePinConfigsIowa; + break; + case spl::HardwareType::Calcio: + configs = InitialWakePinConfigsCalcio; + num_configs = NumInitialWakePinConfigsCalcio; + break; + case spl::HardwareType::_Five_: + configs = InitialWakePinConfigsFive; + num_configs = NumInitialWakePinConfigsFive; + break; + case spl::HardwareType::Copper: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Check we can use our config. */ + AMS_ABORT_UNLESS(configs != nullptr); + + /* Apply the config. */ + for (size_t i = 0; i < num_configs; ++i) { + wec::SetWakeEventLevel(configs[i].wake_event, configs[i].level); + wec::SetWakeEventEnabled(configs[i].wake_event, configs[i].enable); + } + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.hpp new file mode 100644 index 000000000..d577ccc34 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config.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 . + */ +#pragma once +#include + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + struct GpioInitialConfig { + DeviceCode device_code; + gpio::Direction direction; + gpio::GpioValue value; + }; + + void SetInitialGpioConfig(); + void SetInitialWakePinConfig(); + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_calcio.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_calcio.inc new file mode 100644 index 000000000..e20acc0ae --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_calcio.inc @@ -0,0 +1,54 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsCalcio[] = { + { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_GpioPortF1, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_High }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_Hdmi5VEn, Direction_Output, GpioValue_Low }, + { DeviceCode_UsbSwitchB1Oc, Direction_Input, GpioValue_High }, + { DeviceCode_HdmiPdTrEn, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_UsbSwitchB1En, Direction_Output, GpioValue_Low }, + { DeviceCode_HdmiHpd, Direction_Input, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsCalcio = util::size(InitialGpioConfigsCalcio); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_five.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_five.inc new file mode 100644 index 000000000..bc86cb937 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_five.inc @@ -0,0 +1,70 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsFive[] = { + { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, + { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, + { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, + { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, + { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, + { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, + { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, + { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, + { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortH0, Direction_Input, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsFive = util::size(InitialGpioConfigsFive); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_hoag.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_hoag.inc new file mode 100644 index 000000000..97a45b211 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_hoag.inc @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsHoag[] = { + { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, + { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, + { DeviceCode_Debug0, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug1, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug2, Direction_Output, GpioValue_Low }, + { DeviceCode_Debug3, Direction_Output, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, + { DeviceCode_McuIrq, Direction_Input, GpioValue_High }, + { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, + { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, + { DeviceCode_McuBoot, Direction_Output, GpioValue_Low }, + { DeviceCode_McuRst, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, + { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_High }, + { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, + { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, + { DeviceCode_LcdGpio1, Direction_Input, GpioValue_High }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, + { DeviceCode_NfcEn, Direction_Output, GpioValue_Low }, + { DeviceCode_NfcIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_NfcRst, Direction_Output, GpioValue_Low }, + { DeviceCode_GpioPortC7, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortD0, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortC5, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortC6, Direction_Input, GpioValue_Low }, + { DeviceCode_GpioPortY7, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, + { DeviceCode_McuPor, Direction_Output, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsHoag = util::size(InitialGpioConfigsHoag); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_icosa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_icosa.inc new file mode 100644 index 000000000..85800f547 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_icosa.inc @@ -0,0 +1,82 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsIcosa[] = { + { DeviceCode_RamCode3, Direction_Input, GpioValue_High }, + { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, + { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowGc, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, + { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, + { DeviceCode_DebugControllerDet, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_High }, + { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, + { DeviceCode_SdevCoaxSel1, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType0, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType1, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType2, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType3, Direction_Input, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, + { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_High }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, + { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, + { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_High }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdevCoaxSel0, Direction_Input, GpioValue_Low }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, + { DeviceCode_OtgFet1ForSdev, Direction_Output, GpioValue_Low }, + { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_Max77621GpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_OtgFet2ForSdev, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, + { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, + { DeviceCode_RamCode2, Direction_Input, GpioValue_High }, + { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd50BEn, Direction_Output, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsIcosa = util::size(InitialGpioConfigsIcosa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_iowa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_iowa.inc new file mode 100644 index 000000000..1b71bd7d8 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_config_iowa.inc @@ -0,0 +1,81 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const GpioInitialConfig InitialGpioConfigsIowa[] = { + { DeviceCode_RamCode3, Direction_Input, GpioValue_High }, + { DeviceCode_GameCardReset, Direction_Output, GpioValue_Low }, + { DeviceCode_CodecAlert, Direction_Input, GpioValue_Low }, + { DeviceCode_PowSdEn, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtConWakeS, Direction_Input, GpioValue_Low }, + { DeviceCode_GameCardCd, Direction_Input, GpioValue_High }, + { DeviceCode_DebugControllerDet, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgStatus, Direction_Input, GpioValue_Low }, + { DeviceCode_BattChgEnableN, Direction_Output, GpioValue_Low }, + { DeviceCode_FanTach, Direction_Input, GpioValue_Low }, + { DeviceCode_Vdd50AEn, Direction_Output, GpioValue_Low }, + { DeviceCode_SdevCoaxSel1, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType0, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType1, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType2, Direction_Input, GpioValue_Low }, + { DeviceCode_ProdType3, Direction_Input, GpioValue_Low }, + { DeviceCode_Vdd5V3En, Direction_Output, GpioValue_Low }, + { DeviceCode_TempAlert, Direction_Input, GpioValue_High }, + { DeviceCode_MotionInt, Direction_Input, GpioValue_Low }, + { DeviceCode_CodecHpDetIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_TpIrq, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonSleep2, Direction_Input, GpioValue_Low }, + { DeviceCode_ButtonVolUp, Direction_Input, GpioValue_High }, + { DeviceCode_ButtonVolDn, Direction_Input, GpioValue_High }, + { DeviceCode_BattMgicIrq, Direction_Input, GpioValue_Low }, + { DeviceCode_RecoveryKey, Direction_Input, GpioValue_Low }, + { DeviceCode_PowLcdBlEn, Direction_Output, GpioValue_Low }, + { DeviceCode_LcdReset, Direction_Output, GpioValue_Low }, + { DeviceCode_PdRstN, Direction_Output, GpioValue_Low }, + { DeviceCode_Bq24190Irq, Direction_Input, GpioValue_Low }, + { DeviceCode_SdCd, Direction_Input, GpioValue_High }, + { DeviceCode_SdevCoaxSel0, Direction_Input, GpioValue_Low }, + { DeviceCode_SdWp, Direction_Input, GpioValue_High }, + { DeviceCode_CodecLdoEnTemp, Direction_Output, GpioValue_Low }, + { DeviceCode_OtgFet1ForSdev, Direction_Output, GpioValue_Low }, + { DeviceCode_TpReset, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio2, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio3, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio4, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtconChgU, Direction_Output, GpioValue_Low }, + { DeviceCode_CradleIrq, Direction_Input, GpioValue_High }, + { DeviceCode_PdVconnEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowVcpuInt, Direction_Input, GpioValue_High }, + { DeviceCode_OtgFet2ForSdev, Direction_Output, GpioValue_Low }, + { DeviceCode_ExtconDetS, Direction_Input, GpioValue_Low }, + { DeviceCode_WifiRfDisable, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiReset, Direction_Output, GpioValue_Low }, + { DeviceCode_WifiWakeHost, Direction_Input, GpioValue_Low }, + { DeviceCode_ApWakeBt, Direction_Output, GpioValue_Low }, + { DeviceCode_BtRst, Direction_Output, GpioValue_Low }, + { DeviceCode_BtWakeAp, Direction_Input, GpioValue_Low }, + { DeviceCode_ExtConWakeU, Direction_Input, GpioValue_Low }, + { DeviceCode_BtGpio5, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddPEn, Direction_Output, GpioValue_Low }, + { DeviceCode_PowLcdVddNEn, Direction_Output, GpioValue_Low }, + { DeviceCode_RamCode2, Direction_Input, GpioValue_High }, + { DeviceCode_ExtconChgS, Direction_Output, GpioValue_Low }, + { DeviceCode_Vdd50BEn, Direction_Output, GpioValue_Low }, +}; + +constexpr inline size_t NumInitialGpioConfigsIowa = util::size(InitialGpioConfigsIowa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_calcio.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_calcio.inc new file mode 100644 index 000000000..6cdd662fa --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_calcio.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsCalcio[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, false, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_AlsProxInt, }, */ + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, false, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsCalcio = util::size(InitialWakePinConfigsCalcio); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_five.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_five.inc new file mode 100644 index 000000000..809cbf591 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_five.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsFive[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_AlsProxInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsFive = util::size(InitialWakePinConfigsFive); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_hoag.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_hoag.inc new file mode 100644 index 000000000..177fcc5e9 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_hoag.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsHoag[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_AlsProxInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsHoag = util::size(InitialWakePinConfigsHoag); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_icosa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_icosa.inc new file mode 100644 index 000000000..19c40e646 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_icosa.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsIcosa[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_AlsProxInt, }, */ + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsIcosa = util::size(InitialWakePinConfigsIcosa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_iowa.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_iowa.inc new file mode 100644 index 000000000..ebd8016ad --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_initial_wake_pin_config_iowa.inc @@ -0,0 +1,80 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by wake_pin_gen.py, do not edit manually. */ + +constexpr inline const WakePinConfig InitialWakePinConfigsIowa[] = { + { ams::wec::WakeEvent_PexWakeN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortA6, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_QspiCsN, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Spi2Mosi, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ExtconDetS, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_McuIrq, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart2Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart3Cts, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_WifiWakeAp, true, ams::wec::WakeEventLevel_High }, + /* { ams::wec::WakeEvent_AoTag2Pmc, }, */ + { ams::wec::WakeEvent_ExtconDetU, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_NfcInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen1I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen2I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CradleIrq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_GpioPortK6, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_RtcIrq, }, */ + { ams::wec::WakeEvent_Sdmmc1Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc2Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_HdmiCec, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Gen3I2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortL1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Clk_32kOut, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrI2cSda, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonPowerOn, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolUp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonVolDown, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ButtonSlideSw, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_ButtonHome, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_AlsProxInt, }, */ + { ams::wec::WakeEvent_TempAlert, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Bq24190Irq, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_SdCd, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ2, false, ams::wec::WakeEventLevel_Auto }, + /* { ams::wec::WakeEvent_Utmip0, }, */ + /* { ams::wec::WakeEvent_Utmip1, }, */ + /* { ams::wec::WakeEvent_Utmip2, }, */ + /* { ams::wec::WakeEvent_Utmip3, }, */ + /* { ams::wec::WakeEvent_Uhsic, }, */ + /* { ams::wec::WakeEvent_Wake2PmcXusbSystem, }, */ + { ams::wec::WakeEvent_Sdmmc3Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Sdmmc4Dat1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cScl, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_CamI2cSda, true, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_GpioPortZ5, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_DpHpd0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_PwrIntN, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_BtWakeAp, true, ams::wec::WakeEventLevel_Low }, + { ams::wec::WakeEvent_HdmiIntDpHpd, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn0, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_UsbVbusEn1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdRst, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio1, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_LcdGpio2, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_Uart4Cts, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_ModemWakeAp, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_TouchInt, false, ams::wec::WakeEventLevel_Auto }, + { ams::wec::WakeEvent_MotionInt, false, ams::wec::WakeEventLevel_Auto }, +}; + +constexpr inline size_t NumInitialWakePinConfigsIowa = util::size(InitialWakePinConfigsIowa); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_internal_pad_map_combination.inc b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_internal_pad_map_combination.inc new file mode 100644 index 000000000..a9ff37256 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_internal_pad_map_combination.inc @@ -0,0 +1,112 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by gpio_pad_gen.py, do not edit manually. */ + +constexpr inline const PadMapCombination PadMapCombinationList[] = { + { DeviceCode_CodecLdoEnTemp, InternalGpioPadNumber_Port_Z_4, ams::wec::WakeEvent_None }, + { DeviceCode_PowSdEn, InternalGpioPadNumber_Port_E_4, ams::wec::WakeEvent_None }, + { DeviceCode_BtRst, InternalGpioPadNumber_Port_H_4, ams::wec::WakeEvent_None }, + { DeviceCode_RamCode3, InternalGpioPadNumber_Port_BB_2, ams::wec::WakeEvent_None }, + { DeviceCode_GameCardReset, InternalGpioPadNumber_Port_BB_3, ams::wec::WakeEvent_None }, + { DeviceCode_CodecAlert, InternalGpioPadNumber_Port_BB_4, ams::wec::WakeEvent_None }, + { DeviceCode_PowGc, InternalGpioPadNumber_Port_E_5, ams::wec::WakeEvent_None }, + { DeviceCode_DebugControllerDet, InternalGpioPadNumber_Port_S_0, ams::wec::WakeEvent_None }, + { DeviceCode_BattChgStatus, InternalGpioPadNumber_Port_S_1, ams::wec::WakeEvent_None }, + { DeviceCode_BattChgEnableN, InternalGpioPadNumber_Port_S_6, ams::wec::WakeEvent_None }, + { DeviceCode_FanTach, InternalGpioPadNumber_Port_S_7, ams::wec::WakeEvent_None }, + { DeviceCode_Vdd50AEn, InternalGpioPadNumber_Port_A_5, ams::wec::WakeEvent_None }, + { DeviceCode_SdevCoaxSel1, InternalGpioPadNumber_Port_P_0, ams::wec::WakeEvent_None }, + { DeviceCode_ProdType0, InternalGpioPadNumber_Port_P_5, ams::wec::WakeEvent_None }, + { DeviceCode_ProdType1, InternalGpioPadNumber_Port_P_4, ams::wec::WakeEvent_None }, + { DeviceCode_ProdType2, InternalGpioPadNumber_Port_P_3, ams::wec::WakeEvent_None }, + { DeviceCode_ProdType3, InternalGpioPadNumber_Port_P_2, ams::wec::WakeEvent_None }, + { DeviceCode_TempAlert, InternalGpioPadNumber_Port_X_4, ams::wec::WakeEvent_None }, + { DeviceCode_CodecHpDetIrq, InternalGpioPadNumber_Port_V_6, ams::wec::WakeEvent_None }, + { DeviceCode_MotionInt, InternalGpioPadNumber_Port_X_2, ams::wec::WakeEvent_None }, + { DeviceCode_TpIrq, InternalGpioPadNumber_Port_X_1, ams::wec::WakeEvent_None }, + { DeviceCode_ButtonSleep2, InternalGpioPadNumber_Port_X_5, ams::wec::WakeEvent_None }, + { DeviceCode_ButtonVolUp, InternalGpioPadNumber_Port_X_6, ams::wec::WakeEvent_None }, + { DeviceCode_ButtonVolDn, InternalGpioPadNumber_Port_X_7, ams::wec::WakeEvent_None }, + { DeviceCode_RecoveryKey, InternalGpioPadNumber_Port_Y_1, ams::wec::WakeEvent_None }, + { DeviceCode_PowLcdBlEn, InternalGpioPadNumber_Port_V_1, ams::wec::WakeEvent_None }, + { DeviceCode_LcdReset, InternalGpioPadNumber_Port_V_2, ams::wec::WakeEvent_None }, + { DeviceCode_PdVconnEn, InternalGpioPadNumber_Port_K_5, ams::wec::WakeEvent_None }, + { DeviceCode_PdRstN, InternalGpioPadNumber_Port_V_5, ams::wec::WakeEvent_None }, + { DeviceCode_SdevCoaxSel0, InternalGpioPadNumber_Port_Z_2, ams::wec::WakeEvent_None }, + { DeviceCode_SdWp, InternalGpioPadNumber_Port_Z_3, ams::wec::WakeEvent_None }, + { DeviceCode_TpReset, InternalGpioPadNumber_Port_J_7, ams::wec::WakeEvent_None }, + { DeviceCode_BtGpio2, InternalGpioPadNumber_Port_K_0, ams::wec::WakeEvent_None }, + { DeviceCode_BtGpio3, InternalGpioPadNumber_Port_K_1, ams::wec::WakeEvent_None }, + { DeviceCode_BtGpio4, InternalGpioPadNumber_Port_K_2, ams::wec::WakeEvent_None }, + { DeviceCode_PowVcpuInt, InternalGpioPadNumber_Port_K_6, ams::wec::WakeEvent_None }, + { DeviceCode_Max77621GpuInt, InternalGpioPadNumber_Port_K_7, ams::wec::WakeEvent_None }, + { DeviceCode_ExtconChgU, InternalGpioPadNumber_Port_K_3, ams::wec::WakeEvent_None }, + { DeviceCode_ExtconChgS, InternalGpioPadNumber_Port_CC_3, ams::wec::WakeEvent_None }, + { DeviceCode_WifiRfDisable, InternalGpioPadNumber_Port_H_0, ams::wec::WakeEvent_None }, + { DeviceCode_WifiReset, InternalGpioPadNumber_Port_H_1, ams::wec::WakeEvent_None }, + { DeviceCode_ApWakeBt, InternalGpioPadNumber_Port_H_3, ams::wec::WakeEvent_None }, + { DeviceCode_BtGpio5, InternalGpioPadNumber_Port_H_7, ams::wec::WakeEvent_None }, + { DeviceCode_PowLcdVddPEn, InternalGpioPadNumber_Port_I_0, ams::wec::WakeEvent_None }, + { DeviceCode_PowLcdVddNEn, InternalGpioPadNumber_Port_I_1, ams::wec::WakeEvent_None }, + { DeviceCode_RamCode2, InternalGpioPadNumber_Port_CC_2, ams::wec::WakeEvent_None }, + { DeviceCode_Vdd50BEn, InternalGpioPadNumber_Port_CC_4, ams::wec::WakeEvent_None }, + { DeviceCode_OtgFet1ForSdev, InternalGpioPadNumber_Port_J_5, ams::wec::WakeEvent_None }, + { DeviceCode_OtgFet2ForSdev, InternalGpioPadNumber_Port_L_0, ams::wec::WakeEvent_None }, + { DeviceCode_ExtConWakeU, InternalGpioPadNumber_Port_H_6, ams::wec::WakeEvent_None }, + { DeviceCode_ExtConWakeS, InternalGpioPadNumber_Port_E_6, ams::wec::WakeEvent_None }, + { DeviceCode_ExtUart2Rts, InternalGpioPadNumber_Port_G_2, ams::wec::WakeEvent_None }, + { DeviceCode_ExtUart3Rts, InternalGpioPadNumber_Port_D_3, ams::wec::WakeEvent_None }, + { DeviceCode_Debug0, InternalGpioPadNumber_Port_E_0, ams::wec::WakeEvent_None }, + { DeviceCode_Debug1, InternalGpioPadNumber_Port_E_1, ams::wec::WakeEvent_None }, + { DeviceCode_Debug2, InternalGpioPadNumber_Port_E_2, ams::wec::WakeEvent_None }, + { DeviceCode_Debug3, InternalGpioPadNumber_Port_E_3, ams::wec::WakeEvent_None }, + { DeviceCode_NfcIrq, InternalGpioPadNumber_Port_J_4, ams::wec::WakeEvent_None }, + { DeviceCode_NfcRst, InternalGpioPadNumber_Port_K_7, ams::wec::WakeEvent_None }, + { DeviceCode_McuIrq, InternalGpioPadNumber_Port_E_7, ams::wec::WakeEvent_McuIrq }, + { DeviceCode_McuBoot, InternalGpioPadNumber_Port_T_0, ams::wec::WakeEvent_None }, + { DeviceCode_McuRst, InternalGpioPadNumber_Port_T_1, ams::wec::WakeEvent_None }, + { DeviceCode_Vdd5V3En, InternalGpioPadNumber_Port_X_3, ams::wec::WakeEvent_None }, + { DeviceCode_McuPor, InternalGpioPadNumber_Port_CC_5, ams::wec::WakeEvent_None }, + { DeviceCode_NfcEn, InternalGpioPadNumber_Port_J_6, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortC7, InternalGpioPadNumber_Port_C_7, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortD0, InternalGpioPadNumber_Port_D_0, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortC5, InternalGpioPadNumber_Port_C_5, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortC6, InternalGpioPadNumber_Port_C_6, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortY7, InternalGpioPadNumber_Port_Y_5, ams::wec::WakeEvent_None }, + { DeviceCode_Hdmi5VEn, InternalGpioPadNumber_Port_C_0, ams::wec::WakeEvent_None }, + { DeviceCode_UsbSwitchB1En, InternalGpioPadNumber_Port_C_1, ams::wec::WakeEvent_None }, + { DeviceCode_HdmiPdTrEn, InternalGpioPadNumber_Port_C_2, ams::wec::WakeEvent_None }, + { DeviceCode_UsbSwitchB1Oc, InternalGpioPadNumber_Port_CC_6, ams::wec::WakeEvent_None }, + { DeviceCode_HdmiHpd, InternalGpioPadNumber_Port_CC_1, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortF1, InternalGpioPadNumber_Port_F_1, ams::wec::WakeEvent_None }, + { DeviceCode_GpioPortH0, InternalGpioPadNumber_Port_H_0, ams::wec::WakeEvent_None }, + { DeviceCode_ExtconDetS, InternalGpioPadNumber_Port_E_6, ams::wec::WakeEvent_ExtconDetS }, + { DeviceCode_GameCardCd, InternalGpioPadNumber_Port_S_3, ams::wec::WakeEvent_CamI2cSda }, + { DeviceCode_BattMgicIrq, InternalGpioPadNumber_Port_Y_0, ams::wec::WakeEvent_ButtonSlideSw }, + { DeviceCode_Bq24190Irq, InternalGpioPadNumber_Port_Z_0, ams::wec::WakeEvent_Bq24190Irq }, + { DeviceCode_CradleIrq, InternalGpioPadNumber_Port_K_4, ams::wec::WakeEvent_CradleIrq }, + { DeviceCode_BtWakeAp, InternalGpioPadNumber_Port_H_5, ams::wec::WakeEvent_BtWakeAp }, + { DeviceCode_ExtconDetU, InternalGpioPadNumber_Port_H_6, ams::wec::WakeEvent_ExtconDetU }, + { DeviceCode_WifiWakeHost, InternalGpioPadNumber_Port_H_2, ams::wec::WakeEvent_WifiWakeAp }, + { DeviceCode_SdCd, InternalGpioPadNumber_Port_Z_1, ams::wec::WakeEvent_SdCd }, + { DeviceCode_ExtUart2Cts, InternalGpioPadNumber_Port_G_3, ams::wec::WakeEvent_Uart2Cts }, + { DeviceCode_ExtUart3Cts, InternalGpioPadNumber_Port_D_4, ams::wec::WakeEvent_Uart3Cts }, + { DeviceCode_LcdGpio1, InternalGpioPadNumber_Port_V_3, ams::wec::WakeEvent_LcdGpio1 }, + { DeviceCode_PmuIrq, InternalGpioPadNumber_None, ams::wec::WakeEvent_PwrIntN }, +}; + +constexpr inline size_t PadMapCombinationListSize = util::size(PadMapCombinationList); diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_register_accessor.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_register_accessor.hpp new file mode 100644 index 000000000..c9b2f6e6f --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_register_accessor.hpp @@ -0,0 +1,135 @@ +/* + * 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 . + */ +#pragma once +#include + +#include "gpio_tegra_pad.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + constexpr inline dd::PhysicalAddress GpioRegistersPhysicalAddress = 0x6000D000; + constexpr inline size_t GpioRegistersSize = 4_KB; + + constexpr inline int GpioPerControllerBitWidth = 5; + constexpr inline int GpioControllerBitWidth = 3; + + constexpr inline int PortPerControllerBitWidth = 2; + constexpr inline int PortPerController = (1 << PortPerControllerBitWidth); + + constexpr inline int GpioPerPortBitWidth = 3; + constexpr inline int GpioPerPort = (1 << GpioPerPortBitWidth); + + static_assert(PortPerControllerBitWidth + GpioPerPortBitWidth == GpioPerControllerBitWidth); + static_assert(PortPerController * GpioPerPort == (1 << GpioPerControllerBitWidth)); + + constexpr int ConvertInternalGpioPadNumberToController(InternalGpioPadNumber number) { + return (number >> GpioPerControllerBitWidth); + } + + constexpr int ConvertInternalGpioPadNumberToPort(InternalGpioPadNumber number) { + return (number >> GpioControllerBitWidth); + } + + constexpr InternalGpioPadNumber ConvertPortToInternalGpioPadNumber(int port) { + return static_cast(port << GpioControllerBitWidth); + } + + constexpr int ConvertInternalGpioPadNumberToBitIndex(InternalGpioPadNumber number) { + return (number & (GpioPerPort - 1)); + } + + constexpr int ConvertPortNumberToOffset(int port_number) { + return (port_number & (PortPerController - 1)); + } + + enum GpioController { + GpioController_1 = 0, + GpioController_2 = 1, + GpioController_3 = 2, + GpioController_4 = 3, + GpioController_5 = 4, + GpioController_6 = 5, + GpioController_7 = 6, + GpioController_8 = 7, + GpioController_Count = 8, + }; + static_assert(GpioController_Count == static_cast(1 << GpioControllerBitWidth)); + + constexpr inline os::InterruptName InterruptNameTable[GpioController_Count] = { + 64, /* GpioController_1 */ + 65, /* GpioController_2 */ + 66, /* GpioController_3 */ + 67, /* GpioController_4 */ + 87, /* GpioController_5 */ + 119, /* GpioController_6 */ + 121, /* GpioController_7 */ + 157, /* GpioController_8 */ + }; + + enum InternalInterruptMode { + InternalInterruptMode_LowLevel = 0x000000, + InternalInterruptMode_HighLevel = 0x000001, + InternalInterruptMode_RisingEdge = 0x000101, + InternalInterruptMode_FallingEdge = 0x000100, + InternalInterruptMode_AnyEdge = 0x010100, + + InternalInterruptMode_Mask = 0x010101, + }; + + enum GpioRegisterType { + GpioRegisterType_GPIO_CNF = 0, + GpioRegisterType_GPIO_OE = 1, + GpioRegisterType_GPIO_OUT = 2, + GpioRegisterType_GPIO_IN = 3, + GpioRegisterType_GPIO_INT_STA = 4, + GpioRegisterType_GPIO_INT_ENB = 5, + GpioRegisterType_GPIO_INT_LVL = 6, + GpioRegisterType_GPIO_INT_CLR = 7, + GpioRegisterType_GPIO_DB_CTRL = 8, + GpioRegisterType_GPIO_DB_CNT = 9, + }; + + constexpr inline uintptr_t MaskedWriteAddressOffset = 0x80; + constexpr inline int MaskedWriteBitOffset = 8; + + constexpr inline uintptr_t GetGpioRegisterAddress(uintptr_t gpio_address, GpioRegisterType reg_type, InternalGpioPadNumber pad_number) { + const auto controller = ConvertInternalGpioPadNumberToController(pad_number); + const auto port = ConvertInternalGpioPadNumberToPort(pad_number); + const auto offset = ConvertPortNumberToOffset(port); + + switch (reg_type) { + default: + return gpio_address + (0x100 * controller) + (0x10 * reg_type) + (0x4 * offset); + case GpioRegisterType_GPIO_DB_CTRL: + return gpio_address + (0x100 * controller) + (0x10 * GpioRegisterType_GPIO_IN) + (0x4 * offset); + case GpioRegisterType_GPIO_DB_CNT: + return gpio_address + (0x100 * controller) + MaskedWriteAddressOffset + (0x10 * GpioRegisterType_GPIO_INT_CLR) + (0x4 * offset); + } + } + + inline void SetMaskedBit(uintptr_t pad_address, int index, int value) { + const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; + + reg::Write(mask_address, (1u << (MaskedWriteBitOffset + index)) | (static_cast(value) << index)); + } + + inline void SetMaskedBits(uintptr_t pad_address, unsigned int mask, unsigned int value) { + const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; + + reg::Write(mask_address, (mask << MaskedWriteBitOffset) | (value)); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.cpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.cpp new file mode 100644 index 000000000..a922b2128 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.cpp @@ -0,0 +1,69 @@ +/* + * 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 . + */ +#include +#include "gpio_suspend_handler.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + void SuspendHandler::Initialize(uintptr_t gpio_vaddr) { + /* Set our gpio virtual address. */ + this->gpio_virtual_address = gpio_vaddr; + + /* Ensure that we can use the wec library. */ + ams::wec::Initialize(); + } + + void SuspendHandler::SetValueForSleepState(TegraPad *pad, GpioValue value) { + /* TODO */ + AMS_ABORT(); + } + + Result SuspendHandler::IsWakeEventActive(bool *out, TegraPad *pad) const { + /* TODO */ + AMS_ABORT(); + } + + Result SuspendHandler::SetWakeEventActiveFlagSetForDebug(TegraPad *pad, bool en) { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::SetWakePinDebugMode(WakePinDebugMode mode) { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::Suspend() { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::SuspendLow() { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::Resume() { + /* TODO */ + AMS_ABORT(); + } + + void SuspendHandler::ResumeLow() { + /* TODO */ + AMS_ABORT(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.hpp new file mode 100644 index 000000000..faaf59b07 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_suspend_handler.hpp @@ -0,0 +1,89 @@ +/* + * 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 . + */ +#pragma once +#include + +#include "gpio_tegra_pad.hpp" + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + class SuspendHandler { + NON_COPYABLE(SuspendHandler); + NON_MOVEABLE(SuspendHandler); + private: + struct RegisterValues { + u16 conf; + u8 oe; + u8 out; + u8 int_enb; + u32 int_lvl; + u8 db_ctrl; + u8 db_cnt; + + void Reset() { + this->conf = 0; + this->oe = 0; + this->out = 0; + this->int_enb = 0; + this->int_lvl = 0; + this->db_ctrl = 0; + this->db_cnt = 0; + } + }; + + struct ValuesForSleepState { + u8 force_set; + u8 out; + + void Reset() { + this->force_set = 0; + this->out = 0; + } + }; + private: + ddsf::IDriver &driver; + uintptr_t gpio_virtual_address; + RegisterValues register_values[GpioPadPort_Count]; + ValuesForSleepState values_for_sleep_state[GpioPadPort_Count]; + private: + uintptr_t GetGpioVirtualAddress() const { + AMS_ASSERT(this->gpio_virtual_address != 0); + return this->gpio_virtual_address; + } + public: + explicit SuspendHandler(ddsf::IDriver *drv) : driver(*drv), gpio_virtual_address(0) { + for (auto &rv : this->register_values) { + rv.Reset(); + } + for (auto &v : this->values_for_sleep_state) { + v.Reset(); + } + } + + void Initialize(uintptr_t gpio_vaddr); + + void SetValueForSleepState(TegraPad *pad, GpioValue value); + Result IsWakeEventActive(bool *out, TegraPad *pad) const; + Result SetWakeEventActiveFlagSetForDebug(TegraPad *pad, bool en); + void SetWakePinDebugMode(WakePinDebugMode mode); + + void Suspend(); + void SuspendLow(); + void Resume(); + void ResumeLow(); + }; + +} diff --git a/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_tegra_pad.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_tegra_pad.hpp new file mode 100644 index 000000000..67c093906 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_tegra_pad.hpp @@ -0,0 +1,377 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::gpio::driver::board::nintendo_nx::impl { + + enum GpioPadPort { + GpioPadPort_A = 0, + GpioPadPort_B = 1, + GpioPadPort_C = 2, + GpioPadPort_D = 3, + GpioPadPort_E = 4, + GpioPadPort_F = 5, + GpioPadPort_G = 6, + GpioPadPort_H = 7, + GpioPadPort_I = 8, + GpioPadPort_J = 9, + GpioPadPort_K = 10, + GpioPadPort_L = 11, + GpioPadPort_M = 12, + GpioPadPort_N = 13, + GpioPadPort_O = 14, + GpioPadPort_P = 15, + GpioPadPort_Q = 16, + GpioPadPort_R = 17, + GpioPadPort_S = 18, + GpioPadPort_T = 19, + GpioPadPort_U = 20, + GpioPadPort_V = 21, + GpioPadPort_W = 22, + GpioPadPort_X = 23, + GpioPadPort_Y = 24, + GpioPadPort_Z = 25, + GpioPadPort_AA = 26, + GpioPadPort_BB = 27, + GpioPadPort_CC = 28, + GpioPadPort_DD = 29, + GpioPadPort_EE = 30, + GpioPadPort_FF = 31, + GpioPadPort_Count = 32, + }; + + using InternalGpioPadNumber = int; + + constexpr unsigned int GetInternalGpioPadNumber(GpioPadPort port, unsigned int which) { + AMS_ASSERT(which < 8); + + return (static_cast(port) * 8) + which; + } + + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_None = -1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_0 = 0x00; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_1 = 0x01; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_2 = 0x02; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_3 = 0x03; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_4 = 0x04; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_5 = 0x05; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_6 = 0x06; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_A_7 = 0x07; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_0 = 0x08; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_1 = 0x09; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_2 = 0x0A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_3 = 0x0B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_4 = 0x0C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_5 = 0x0D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_6 = 0x0E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_B_7 = 0x0F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_0 = 0x10; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_1 = 0x11; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_2 = 0x12; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_3 = 0x13; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_4 = 0x14; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_5 = 0x15; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_6 = 0x16; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_C_7 = 0x17; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_0 = 0x18; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_1 = 0x19; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_2 = 0x1A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_3 = 0x1B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_4 = 0x1C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_5 = 0x1D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_6 = 0x1E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_D_7 = 0x1F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_0 = 0x20; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_1 = 0x21; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_2 = 0x22; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_3 = 0x23; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_4 = 0x24; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_5 = 0x25; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_6 = 0x26; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_E_7 = 0x27; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_0 = 0x28; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_1 = 0x29; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_2 = 0x2A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_3 = 0x2B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_4 = 0x2C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_5 = 0x2D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_6 = 0x2E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_F_7 = 0x2F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_0 = 0x30; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_1 = 0x31; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_2 = 0x32; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_3 = 0x33; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_4 = 0x34; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_5 = 0x35; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_6 = 0x36; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_G_7 = 0x37; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_0 = 0x38; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_1 = 0x39; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_2 = 0x3A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_3 = 0x3B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_4 = 0x3C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_5 = 0x3D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_6 = 0x3E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_H_7 = 0x3F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_0 = 0x40; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_1 = 0x41; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_2 = 0x42; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_3 = 0x43; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_4 = 0x44; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_5 = 0x45; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_6 = 0x46; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_I_7 = 0x47; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_0 = 0x48; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_1 = 0x49; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_2 = 0x4A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_3 = 0x4B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_4 = 0x4C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_5 = 0x4D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_6 = 0x4E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_J_7 = 0x4F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_0 = 0x50; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_1 = 0x51; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_2 = 0x52; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_3 = 0x53; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_4 = 0x54; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_5 = 0x55; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_6 = 0x56; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_K_7 = 0x57; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_0 = 0x58; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_1 = 0x59; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_2 = 0x5A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_3 = 0x5B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_4 = 0x5C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_5 = 0x5D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_6 = 0x5E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_L_7 = 0x5F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_0 = 0x60; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_1 = 0x61; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_2 = 0x62; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_3 = 0x63; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_4 = 0x64; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_5 = 0x65; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_6 = 0x66; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_M_7 = 0x67; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_0 = 0x68; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_1 = 0x69; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_2 = 0x6A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_3 = 0x6B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_4 = 0x6C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_5 = 0x6D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_6 = 0x6E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_N_7 = 0x6F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_0 = 0x70; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_1 = 0x71; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_2 = 0x72; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_3 = 0x73; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_4 = 0x74; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_5 = 0x75; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_6 = 0x76; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_O_7 = 0x77; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_0 = 0x78; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_1 = 0x79; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_2 = 0x7A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_3 = 0x7B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_4 = 0x7C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_5 = 0x7D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_6 = 0x7E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_P_7 = 0x7F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_0 = 0x80; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_1 = 0x81; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_2 = 0x82; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_3 = 0x83; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_4 = 0x84; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_5 = 0x85; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_6 = 0x86; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Q_7 = 0x87; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_0 = 0x88; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_1 = 0x89; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_2 = 0x8A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_3 = 0x8B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_4 = 0x8C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_5 = 0x8D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_6 = 0x8E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_R_7 = 0x8F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_0 = 0x90; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_1 = 0x91; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_2 = 0x92; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_3 = 0x93; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_4 = 0x94; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_5 = 0x95; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_6 = 0x96; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_S_7 = 0x97; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_0 = 0x98; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_1 = 0x99; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_2 = 0x9A; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_3 = 0x9B; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_4 = 0x9C; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_5 = 0x9D; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_6 = 0x9E; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_T_7 = 0x9F; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_0 = 0xA0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_1 = 0xA1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_2 = 0xA2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_3 = 0xA3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_4 = 0xA4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_5 = 0xA5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_6 = 0xA6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_U_7 = 0xA7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_0 = 0xA8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_1 = 0xA9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_2 = 0xAA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_3 = 0xAB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_4 = 0xAC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_5 = 0xAD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_6 = 0xAE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_V_7 = 0xAF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_0 = 0xB0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_1 = 0xB1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_2 = 0xB2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_3 = 0xB3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_4 = 0xB4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_5 = 0xB5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_6 = 0xB6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_W_7 = 0xB7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_0 = 0xB8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_1 = 0xB9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_2 = 0xBA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_3 = 0xBB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_4 = 0xBC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_5 = 0xBD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_6 = 0xBE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_X_7 = 0xBF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_0 = 0xC0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_1 = 0xC1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_2 = 0xC2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_3 = 0xC3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_4 = 0xC4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_5 = 0xC5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_6 = 0xC6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Y_7 = 0xC7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_0 = 0xC8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_1 = 0xC9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_2 = 0xCA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_3 = 0xCB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_4 = 0xCC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_5 = 0xCD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_6 = 0xCE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_Z_7 = 0xCF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_0 = 0xD0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_1 = 0xD1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_2 = 0xD2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_3 = 0xD3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_4 = 0xD4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_5 = 0xD5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_6 = 0xD6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_AA_7 = 0xD7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_0 = 0xD8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_1 = 0xD9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_2 = 0xDA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_3 = 0xDB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_4 = 0xDC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_5 = 0xDD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_6 = 0xDE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_BB_7 = 0xDF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_0 = 0xE0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_1 = 0xE1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_2 = 0xE2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_3 = 0xE3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_4 = 0xE4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_5 = 0xE5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_6 = 0xE6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_CC_7 = 0xE7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_0 = 0xE8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_1 = 0xE9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_2 = 0xEA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_3 = 0xEB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_4 = 0xEC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_5 = 0xED; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_6 = 0xEE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_DD_7 = 0xEF; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_0 = 0xF0; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_1 = 0xF1; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_2 = 0xF2; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_3 = 0xF3; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_4 = 0xF4; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_5 = 0xF5; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_6 = 0xF6; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_EE_7 = 0xF7; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_0 = 0xF8; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_1 = 0xF9; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_2 = 0xFA; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_3 = 0xFB; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_4 = 0xFC; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_5 = 0xFD; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_6 = 0xFE; + constexpr inline InternalGpioPadNumber InternalGpioPadNumber_Port_FF_7 = 0xFF; + + struct PadMapCombination { + DeviceCode device_code; + InternalGpioPadNumber internal_number; + wec::WakeEvent wake_event; + }; + + #include "gpio_internal_pad_map_combination.inc" + + struct PadInfo { + wec::WakeEvent wake_event; + + constexpr PadInfo() : wake_event(wec::WakeEvent_None) { /* ... */ } + + constexpr explicit PadInfo(wec::WakeEvent we) : wake_event(we) { /* ... */ } + + constexpr bool operator ==(const PadInfo &rhs) const { return this->wake_event == rhs.wake_event; } + constexpr bool operator !=(const PadInfo &rhs) const { return !(*this == rhs); } + }; + + struct PadStatus { + bool is_wake_active; + bool is_wake_active_debug; + + constexpr PadStatus() : is_wake_active(false), is_wake_active_debug(false) { /* ... */ } + }; + + class TegraPad : public ::ams::gpio::driver::Pad { + AMS_DDSF_CASTABLE_TRAITS(ams::gpio::driver::board::nintendo_nx::impl::TegraPad, ::ams::gpio::driver::Pad); + private: + using Base = ::ams::gpio::driver::Pad; + private: + util::IntrusiveListNode interrupt_list_node; + PadInfo info; + PadStatus status; + public: + using InterruptListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&TegraPad::interrupt_list_node>; + using InterruptList = typename InterruptListTraits::ListType; + friend class util::IntrusiveList>; + public: + TegraPad() : Pad(), interrupt_list_node(), info(), status() { /* ... */ } + + const PadInfo &GetInfo() const { return this->info; } + PadStatus &GetStatus() { return this->status; } + + void SetParameters(int pad, const PadInfo &i) { + Base::SetPadNumber(pad); + this->info = info; + } + + bool IsLinkedToInterruptBoundPadList() const { + return this->interrupt_list_node.IsLinked(); + } + }; + +} diff --git a/stratosphere/boot/source/boot_pmc_wrapper.hpp b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_wake_pin_config.hpp similarity index 79% rename from stratosphere/boot/source/boot_pmc_wrapper.hpp rename to libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_wake_pin_config.hpp index 1f96dd0de..9b251437a 100644 --- a/stratosphere/boot/source/boot_pmc_wrapper.hpp +++ b/libraries/libstratosphere/source/gpio/driver/board/nintendo_nx/impl/gpio_wake_pin_config.hpp @@ -16,10 +16,12 @@ #pragma once #include -namespace ams::boot { +namespace ams::gpio::driver::board::nintendo_nx::impl { - /* PMC Access Utilities. */ - u32 ReadPmcRegister(u32 phys_addr); - void WritePmcRegister(u32 phys_addr, u32 value, u32 mask = UINT32_MAX); + struct WakePinConfig { + wec::WakeEvent wake_event; + bool enable; + wec::WakeEventLevel level; + }; } diff --git a/libraries/libstratosphere/source/gpio/driver/gpio_driver_client_api.cpp b/libraries/libstratosphere/source/gpio/driver/gpio_driver_client_api.cpp new file mode 100644 index 000000000..8bbe1dec9 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/gpio_driver_client_api.cpp @@ -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 . + */ +#include + +#include "impl/gpio_driver_core.hpp" + +namespace ams::gpio::driver { + + void Initialize() { + return impl::InitializeDrivers(); + } + + void Finalize() { + return impl::FinalizeDrivers(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/gpio_driver_service_api.cpp b/libraries/libstratosphere/source/gpio/driver/gpio_driver_service_api.cpp new file mode 100644 index 000000000..3542bde8e --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/gpio_driver_service_api.cpp @@ -0,0 +1,54 @@ +/* + * 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 . + */ +#include + +#include "impl/gpio_driver_core.hpp" + +namespace ams::gpio::driver { + + void RegisterDriver(IGpioDriver *driver) { + return impl::RegisterDriver(driver); + } + + void UnregisterDriver(IGpioDriver *driver) { + return impl::UnregisterDriver(driver); + } + + Result RegisterDeviceCode(DeviceCode device_code, Pad *pad) { + return impl::RegisterDeviceCode(device_code, pad); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return impl::UnregisterDeviceCode(device_code); + } + + void RegisterInterruptHandler(ddsf::IEventHandler *handler) { + return impl::RegisterInterruptHandler(handler); + } + + void UnregisterInterruptHandler(ddsf::IEventHandler *handler) { + return impl::UnregisterInterruptHandler(handler); + } + + void SetInitialGpioConfig() { + return board::SetInitialGpioConfig(); + } + + void SetInitialWakePinConfig() { + return board::SetInitialWakePinConfig(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/gpio_pad.cpp b/libraries/libstratosphere/source/gpio/driver/gpio_pad.cpp new file mode 100644 index 000000000..3899e01e9 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/gpio_pad.cpp @@ -0,0 +1,48 @@ +/* + * 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 . + */ +#include + +#include "impl/gpio_driver_core.hpp" + +namespace ams::gpio::driver { + + bool Pad::IsAnySessionBoundToInterrupt() const { + /* Check to see if any session has an interrupt bound. */ + bool bound = false; + this->ForEachSession([&](const ddsf::ISession &session) -> bool { + const auto &impl = session.SafeCastTo(); + if (impl.IsInterruptBound()) { + bound = true; + return false; + } + return true; + }); + + return bound; + } + + void Pad::SignalInterruptBoundEvent() { + /* Signal relevant sessions. */ + this->ForEachSession([&](ddsf::ISession &session) -> bool { + auto &impl = session.SafeCastTo(); + if (impl.IsInterruptBound()) { + impl.SignalInterruptBoundEvent(); + } + return true; + }); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/gpio_pad_api.cpp b/libraries/libstratosphere/source/gpio/driver/gpio_pad_api.cpp new file mode 100644 index 000000000..b2aaaed84 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/gpio_pad_api.cpp @@ -0,0 +1,137 @@ +/* + * 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 . + */ +#include + +#include "impl/gpio_driver_core.hpp" + +namespace ams::gpio::driver { + + namespace { + + Result OpenSessionImpl(GpioPadSession *out, Pad *pad, ddsf::AccessMode access_mode) { + /* Construct the session. */ + auto *session = new (std::addressof(impl::GetPadSessionImpl(*out))) impl::PadSessionImpl; + auto session_guard = SCOPE_GUARD { session->~PadSessionImpl(); }; + + /* Open the session. */ + R_TRY(session->Open(pad, access_mode)); + + session_guard.Cancel(); + return ResultSuccess(); + } + + } + + Result OpenSession(GpioPadSession *out, DeviceCode device_code, ddsf::AccessMode access_mode) { + /* Find the pad. */ + Pad *pad = nullptr; + R_TRY(impl::FindPad(std::addressof(pad), device_code)); + AMS_ASSERT(pad != nullptr); + + /* Sanitize the access mode. */ + if ((access_mode & ~(ddsf::AccessMode_ReadWrite)) != 0) { + access_mode = ddsf::AccessMode_None; + } + + /* Try to open the session with the desired mode. */ + R_TRY_CATCH(OpenSessionImpl(out, pad, access_mode)) { + R_CATCH(ddsf::ResultAccessModeDenied) { + /* Our current access mode was denied. Try adding the shared flag. */ + R_TRY(OpenSessionImpl(out, pad, static_cast(access_mode | ddsf::AccessMode_Shared))); + } + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + + void CloseSession(GpioPadSession *session) { + AMS_ASSERT(session != nullptr); + impl::GetOpenPadSessionImpl(*session).~PadSessionImpl(); + } + + Result SetDirection(GpioPadSession *session, gpio::Direction direction) { + /* Check that we have a valid session. */ + AMS_ASSERT(session != nullptr); + auto &impl = impl::GetOpenPadSessionImpl(*session); + R_UNLESS(impl.IsOpen(), gpio::ResultNotOpen()); + + /* Check that we can perform the access. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the pad. */ + auto &pad = impl.GetDevice().SafeCastTo(); + + /* Perform the call. */ + R_TRY(pad.GetDriver().SafeCastTo().SetDirection(std::addressof(pad), direction)); + + return ResultSuccess(); + } + + Result GetDirection(gpio::Direction *out, GpioPadSession *session) { + /* Check that we have a valid session. */ + AMS_ASSERT(session != nullptr); + auto &impl = impl::GetOpenPadSessionImpl(*session); + R_UNLESS(impl.IsOpen(), gpio::ResultNotOpen()); + + /* Check that we can perform the access. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the pad. */ + auto &pad = impl.GetDevice().SafeCastTo(); + + /* Perform the call. */ + R_TRY(pad.GetDriver().SafeCastTo().GetDirection(out, std::addressof(pad))); + + return ResultSuccess(); + } + + Result SetValue(GpioPadSession *session, gpio::GpioValue value) { + /* Check that we have a valid session. */ + AMS_ASSERT(session != nullptr); + auto &impl = impl::GetOpenPadSessionImpl(*session); + R_UNLESS(impl.IsOpen(), gpio::ResultNotOpen()); + + /* Check that we can perform the access. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the pad. */ + auto &pad = impl.GetDevice().SafeCastTo(); + + /* Perform the call. */ + R_TRY(pad.GetDriver().SafeCastTo().SetValue(std::addressof(pad), value)); + + return ResultSuccess(); + } + + Result GetValue(gpio::GpioValue *out, GpioPadSession *session) { + /* Check that we have a valid session. */ + AMS_ASSERT(session != nullptr); + auto &impl = impl::GetOpenPadSessionImpl(*session); + R_UNLESS(impl.IsOpen(), gpio::ResultNotOpen()); + + /* Check that we can perform the access. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the pad. */ + auto &pad = impl.GetDevice().SafeCastTo(); + + /* Perform the call. */ + R_TRY(pad.GetDriver().SafeCastTo().GetValue(out, std::addressof(pad))); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/impl/gpio_driver_core.cpp b/libraries/libstratosphere/source/gpio/driver/impl/gpio_driver_core.cpp new file mode 100644 index 000000000..8da4abde0 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/impl/gpio_driver_core.cpp @@ -0,0 +1,149 @@ +/* + * 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 . + */ +#include +#include "gpio_driver_core.hpp" + +namespace ams::gpio::driver::impl { + + namespace { + + os::ThreadType g_interrupt_thread; + + constexpr inline size_t InterruptThreadStackSize = os::MemoryPageSize; + alignas(os::MemoryPageSize) u8 g_interrupt_thread_stack[InterruptThreadStackSize]; + + gpio::driver::IGpioDriver::List &GetGpioDriverList() { + static gpio::driver::IGpioDriver::List s_gpio_driver_list; + return s_gpio_driver_list; + } + + ddsf::EventHandlerManager &GetInterruptHandlerManager() { + static ddsf::EventHandlerManager s_interrupt_handler_manager; + return s_interrupt_handler_manager; + } + + ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() { + static ddsf::DeviceCodeEntryManager s_device_code_entry_manager(ddsf::GetDeviceCodeEntryHolderMemoryResource()); + return s_device_code_entry_manager; + } + + void InterruptThreadFunction(void *arg) { + AMS_UNUSED(arg); + GetInterruptHandlerManager().LoopAuto(); + } + + } + + void InitializeDrivers() { + /* Ensure the event handler manager is initialized. */ + GetInterruptHandlerManager().Initialize(); + + /* Initialize all registered drivers. */ + for (auto &driver : GetGpioDriverList()) { + driver.SafeCastTo().InitializeDriver(); + } + + /* Create the interrupt thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_interrupt_thread), InterruptThreadFunction, nullptr, g_interrupt_thread_stack, InterruptThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(gpio, InterruptHandler))); + os::SetThreadNamePointer(std::addressof(g_interrupt_thread), AMS_GET_SYSTEM_THREAD_NAME(gpio, InterruptHandler)); + os::StartThread(std::addressof(g_interrupt_thread)); + + /* Wait for the interrupt thread to enter the loop. */ + GetInterruptHandlerManager().WaitLoopEnter(); + } + + void FinalizeDrivers() { + /* Request the interrupt thread stop. */ + GetInterruptHandlerManager().RequestStop(); + os::WaitThread(std::addressof(g_interrupt_thread)); + os::DestroyThread(std::addressof(g_interrupt_thread)); + + /* TODO: What else? */ + AMS_ABORT(); + } + + void RegisterDriver(IGpioDriver *driver) { + AMS_ASSERT(driver != nullptr); + GetGpioDriverList().push_back(*driver); + } + + void UnregisterDriver(IGpioDriver *driver) { + AMS_ASSERT(driver != nullptr); + if (driver->IsLinkedToList()) { + auto &list = GetGpioDriverList(); + list.erase(list.iterator_to(*driver)); + } + } + + Result RegisterDeviceCode(DeviceCode device_code, Pad *pad) { + AMS_ASSERT(pad != nullptr); + R_TRY(GetDeviceCodeEntryManager().Add(device_code, pad)); + return ResultSuccess(); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return GetDeviceCodeEntryManager().Remove(device_code); + } + + void RegisterInterruptHandler(ddsf::IEventHandler *handler) { + AMS_ASSERT(handler != nullptr); + GetInterruptHandlerManager().RegisterHandler(handler); + } + + void UnregisterInterruptHandler(ddsf::IEventHandler *handler) { + AMS_ASSERT(handler != nullptr); + GetInterruptHandlerManager().UnregisterHandler(handler); + } + + Result FindPad(Pad **out, DeviceCode device_code) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Find the device. */ + ddsf::IDevice *device; + R_TRY(GetDeviceCodeEntryManager().FindDevice(std::addressof(device), device_code)); + + /* Set output. */ + *out = device->SafeCastToPointer(); + return ResultSuccess(); + } + + Result FindPadByNumber(Pad **out, int pad_number) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Find the pad. */ + bool found = false; + GetDeviceCodeEntryManager().ForEachEntry([&](ddsf::DeviceCodeEntry &entry) -> bool { + /* Convert the entry to a pad. */ + auto &pad = entry.GetDevice().SafeCastTo(); + + /* Check if the pad is the one we're looking for. */ + if (pad.GetPadNumber() == pad_number) { + found = true; + *out = std::addressof(pad); + return false; + } + return true; + }); + + /* Check that we found the pad. */ + R_UNLESS(found, ddsf::ResultDeviceCodeNotFound()); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/driver/impl/gpio_driver_core.hpp b/libraries/libstratosphere/source/gpio/driver/impl/gpio_driver_core.hpp new file mode 100644 index 000000000..dbf169e5e --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/impl/gpio_driver_core.hpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::gpio::driver::impl { + + void InitializeDrivers(); + void FinalizeDrivers(); + + void RegisterDriver(IGpioDriver *driver); + void UnregisterDriver(IGpioDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, Pad *pad); + bool UnregisterDeviceCode(DeviceCode device_code); + + void RegisterInterruptHandler(ddsf::IEventHandler *handler); + void UnregisterInterruptHandler(ddsf::IEventHandler *handler); + + Result FindPad(Pad **out, DeviceCode device_code); + Result FindPadByNumber(Pad **out, int pad_number); + +} diff --git a/libraries/libstratosphere/source/gpio/driver/impl/gpio_pad_session_impl.cpp b/libraries/libstratosphere/source/gpio/driver/impl/gpio_pad_session_impl.cpp new file mode 100644 index 000000000..9e8f7c805 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/driver/impl/gpio_pad_session_impl.cpp @@ -0,0 +1,150 @@ +/* + * 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 . + */ +#include + +namespace ams::gpio::driver::impl { + + Result PadSessionImpl::Open(Pad *pad, ddsf::AccessMode access_mode) { + /* Check if the pad has any open sessions. */ + const bool first_session = !pad->HasAnyOpenSession(); + + /* Open the session. */ + R_TRY(ddsf::OpenSession(pad, this, access_mode)); + auto pad_guard = SCOPE_GUARD { ddsf::CloseSession(this); }; + + /* If we're the first, we want to initialize the pad. */ + if (first_session) { + R_TRY(pad->GetDriver().SafeCastTo().InitializePad(pad)); + } + + /* We opened successfully. */ + pad_guard.Cancel(); + return ResultSuccess(); + } + + void PadSessionImpl::Close() { + /* If the session isn't open, nothing to do. */ + if (!this->IsOpen()) { + return; + } + + /* Unbind the interrupt, if it's bound. */ + if (this->IsInterruptBound()) { + this->UnbindInterrupt(); + } + + /* Get the pad we're a session for. */ + auto &pad = this->GetDevice().SafeCastTo(); + + /* Close the session. */ + ddsf::CloseSession(this); + + /* If we were the last session on the pad, finalize the pad. */ + if (!pad.HasAnyOpenSession()) { + pad.GetDriver().SafeCastTo().FinalizePad(std::addressof(pad)); + } + } + + Result PadSessionImpl::BindInterrupt(os::SystemEventType *event) { + /* Acquire exclusive access to the relevant interrupt control mutex. */ + auto &pad = this->GetDevice().SafeCastTo(); + auto &mutex = pad.GetDriver().SafeCastTo().GetInterruptControlMutex(pad); + std::scoped_lock lk(mutex); + + /* Check that we're not already bound. */ + R_UNLESS(!this->IsInterruptBound(), gpio::ResultAlreadyBound()); + R_UNLESS(!this->GetDevice().SafeCastTo().IsAnySessionBoundToInterrupt(), gpio::ResultAlreadyBound()); + + /* Create the system event. */ + R_TRY(os::CreateSystemEvent(event, os::EventClearMode_ManualClear, true)); + auto ev_guard = SCOPE_GUARD { os::DestroySystemEvent(event); }; + + /* Attach the event to our holder. */ + this->event_holder.AttachEvent(event); + auto hl_guard = SCOPE_GUARD { this->event_holder.DetachEvent(); }; + + /* Update interrupt needed. */ + R_TRY(this->UpdateDriverInterruptEnabled()); + + /* We succeeded. */ + hl_guard.Cancel(); + ev_guard.Cancel(); + return ResultSuccess(); + } + + void PadSessionImpl::UnbindInterrupt() { + /* Acquire exclusive access to the relevant interrupt control mutex. */ + auto &pad = this->GetDevice().SafeCastTo(); + auto &mutex = pad.GetDriver().SafeCastTo().GetInterruptControlMutex(pad); + std::scoped_lock lk(mutex); + + /* If we're not bound, nothing to do. */ + if (!this->IsInterruptBound()) { + return; + } + + /* Detach and destroy the event */ + os::DestroySystemEvent(this->event_holder.DetachEvent()); + + /* Update interrupt needed. */ + R_ABORT_UNLESS(this->UpdateDriverInterruptEnabled()); + } + + Result PadSessionImpl::UpdateDriverInterruptEnabled() { + /* Check we have exclusive access to the relevant interrupt control mutex. */ + auto &pad = this->GetDevice().SafeCastTo(); + auto &driver = pad.GetDriver().SafeCastTo(); + AMS_ASSERT(driver.GetInterruptControlMutex(pad).IsLockedByCurrentThread()); + + /* Set interrupt enabled. */ + return driver.SetInterruptEnabled(std::addressof(pad), pad.IsInterruptRequiredForDriver()); + } + + Result PadSessionImpl::GetInterruptEnabled(bool *out) const { + *out = this->GetDevice().SafeCastTo().IsInterruptEnabled(); + return ResultSuccess(); + } + + Result PadSessionImpl::SetInterruptEnabled(bool en) { + /* Acquire exclusive access to the relevant interrupt control mutex. */ + auto &pad = this->GetDevice().SafeCastTo(); + auto &mutex = pad.GetDriver().SafeCastTo().GetInterruptControlMutex(pad); + std::scoped_lock lk(mutex); + + /* Set the interrupt enable. */ + const bool prev = pad.IsInterruptEnabled(); + pad.SetInterruptEnabled(en); + auto pad_guard = SCOPE_GUARD { pad.SetInterruptEnabled(prev); }; + + /* Update interrupt needed. */ + R_TRY(this->UpdateDriverInterruptEnabled()); + + pad_guard.Cancel(); + return ResultSuccess(); + } + + void PadSessionImpl::SignalInterruptBoundEvent() { + /* Check we have exclusive access to the relevant interrupt control mutex. */ + auto &pad = this->GetDevice().SafeCastTo(); + auto &driver = pad.GetDriver().SafeCastTo(); + AMS_ASSERT(driver.GetInterruptControlMutex(pad).IsLockedByCurrentThread()); + + if (auto *event = this->event_holder.GetSystemEvent(); event != nullptr) { + os::SignalSystemEvent(event); + } + } + +} diff --git a/libraries/libstratosphere/source/gpio/gpio_client_api.cpp b/libraries/libstratosphere/source/gpio/gpio_client_api.cpp new file mode 100644 index 000000000..8047f558c --- /dev/null +++ b/libraries/libstratosphere/source/gpio/gpio_client_api.cpp @@ -0,0 +1,209 @@ +/* + * 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 . + */ +#include +#include "gpio_remote_manager_impl.hpp" + +namespace ams::gpio { + + namespace { + + constinit os::SdkMutex g_init_mutex; + constinit int g_initialize_count = 0; + constinit bool g_remote = false; + std::shared_ptr g_manager; + + using InternalSession = std::shared_ptr; + + InternalSession &GetInterface(GpioPadSession *session) { + AMS_ASSERT(session->_session != nullptr); + return *static_cast(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(); + g_remote = true; + } + } + + void InitializeWith(std::shared_ptr &&sp) { + std::scoped_lock lk(g_init_mutex); + + AMS_ABORT_UNLESS(g_initialize_count == 0); + + g_manager = std::move(sp); + g_initialize_count = 1; + } + + void Finalize() { + std::scoped_lock lk(g_init_mutex); + + AMS_ASSERT(g_initialize_count > 0); + + if ((--g_initialize_count) == 0) { + g_manager.reset(); + if (g_remote) { + ::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(); + } + + /* 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; + } + +} diff --git a/libraries/libstratosphere/source/gpio/gpio_remote_manager_impl.hpp b/libraries/libstratosphere/source/gpio/gpio_remote_manager_impl.hpp new file mode 100644 index 000000000..bb439b79f --- /dev/null +++ b/libraries/libstratosphere/source/gpio/gpio_remote_manager_impl.hpp @@ -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 . + */ +#pragma once +#include +#include "gpio_remote_pad_session_impl.hpp" + +namespace ams::gpio { + + class RemoteManagerImpl { + public: + RemoteManagerImpl() { /* ... */ } + + ~RemoteManagerImpl() { /* ... */ } + public: + /* Actual commands. */ + Result OpenSessionForDev(ams::sf::Out> out, s32 pad_descriptor) { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + Result OpenSession(ams::sf::Out> out, gpio::GpioPadName pad_name) { + ::GpioPadSession p; + R_TRY(::gpioOpenSession(std::addressof(p), static_cast<::GpioPadName>(static_cast(pad_name)))); + + out.SetValue(ams::sf::MakeShared(p)); + return ResultSuccess(); + } + + Result OpenSessionForTest(ams::sf::Out> out, gpio::GpioPadName pad_name) { + /* TODO: libnx bindings */ + AMS_ABORT(); + } + + Result IsWakeEventActive(ams::sf::Out out, gpio::GpioPadName pad_name) { + return ::gpioIsWakeEventActive2(out.GetPointer(), static_cast<::GpioPadName>(static_cast(pad_name))); + } + + Result GetWakeEventActiveFlagSet(ams::sf::Out 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> 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(p)); + return ResultSuccess(); + } + + Result IsWakeEventActive2(ams::sf::Out 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); + +} diff --git a/libraries/libstratosphere/source/gpio/gpio_remote_pad_session_impl.hpp b/libraries/libstratosphere/source/gpio/gpio_remote_pad_session_impl.hpp new file mode 100644 index 000000000..297132f08 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/gpio_remote_pad_session_impl.hpp @@ -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 . + */ +#pragma once +#include + +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(direction))); + } + + Result GetDirection(ams::sf::Out 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(mode))); + } + + Result GetInterruptMode(ams::sf::Out 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 out) { + return ::gpioPadGetInterruptEnable(std::addressof(this->srv), out.GetPointer()); + } + + Result GetInterruptStatus(ams::sf::Out 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(value))); + } + + Result GetValue(ams::sf::Out 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 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 out) { + return ::gpioPadGetDebounceTime(std::addressof(this->srv), out.GetPointer()); + } + + Result SetValueForSleepState(gpio::GpioValue value) { + /* TODO: libnx bindings. */ + AMS_ABORT(); + } + + Result GetValueForSleepState(ams::sf::Out out) { + /* TODO: libnx bindings. */ + AMS_ABORT(); + } + }; + static_assert(gpio::sf::IsIPadSession); + +} diff --git a/libraries/libstratosphere/source/gpio/server/gpio_server_api.cpp b/libraries/libstratosphere/source/gpio/server/gpio_server_api.cpp new file mode 100644 index 000000000..a6310b376 --- /dev/null +++ b/libraries/libstratosphere/source/gpio/server/gpio_server_api.cpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#include +#include "gpio_server_manager_impl.hpp" + +namespace ams::gpio::server { + + namespace { + + ManagerImpl g_manager_impl; + + std::shared_ptr GetManagerServiceObject() { + static std::shared_ptr s_sp = ams::sf::GetSharedPointerTo(g_manager_impl); + return s_sp; + } + + } + + std::shared_ptr GetServiceObject() { + return GetManagerServiceObject(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/server/gpio_server_manager_impl.cpp b/libraries/libstratosphere/source/gpio/server/gpio_server_manager_impl.cpp new file mode 100644 index 000000000..0089ea77b --- /dev/null +++ b/libraries/libstratosphere/source/gpio/server/gpio_server_manager_impl.cpp @@ -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 . + */ +#include +#include "gpio_server_manager_impl.hpp" + +namespace ams::gpio::server { + + ManagerImpl::ManagerImpl() : pad_session_memory_resource(), pad_allocator(std::addressof(pad_session_memory_resource)) { + this->heap_handle = lmem::CreateExpHeap(this->heap_buffer, sizeof(this->heap_buffer), lmem::CreateOption_None); + this->pad_session_memory_resource.Attach(this->heap_handle); + } + + ManagerImpl::~ManagerImpl() { + lmem::DestroyExpHeap(this->heap_handle); + } + + Result ManagerImpl::OpenSessionForDev(ams::sf::Out> out, s32 pad_descriptor) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::OpenSession(ams::sf::Out> out, gpio::GpioPadName pad_name) { + return this->OpenSession2(out, ConvertToDeviceCode(pad_name), ddsf::AccessMode_ReadWrite); + } + + Result ManagerImpl::OpenSessionForTest(ams::sf::Out> out, gpio::GpioPadName pad_name) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::IsWakeEventActive(ams::sf::Out out, gpio::GpioPadName pad_name) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::GetWakeEventActiveFlagSet(ams::sf::Out out) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::SetWakeEventActiveFlagSetForDebug(gpio::GpioPadName pad_name, bool is_enabled) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::SetWakePinDebugMode(s32 mode) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::OpenSession2(ams::sf::Out> out, DeviceCode device_code, ddsf::AccessMode access_mode) { + /* Allocate a session. */ + auto session = ams::sf::AllocateShared(this->pad_allocator, this); + + /* Open the session. */ + R_TRY(session->GetImpl().OpenSession(device_code, access_mode)); + + /* We succeeded. */ + out.SetValue(std::move(session)); + return ResultSuccess(); + } + + Result ManagerImpl::IsWakeEventActive2(ams::sf::Out out, DeviceCode device_code) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::SetWakeEventActiveFlagSetForDebug2(DeviceCode device_code, bool is_enabled) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::SetRetryValues(u32 arg0, u32 arg1) { + /* TODO */ + AMS_ABORT(); + } + +} diff --git a/libraries/libstratosphere/source/gpio/server/gpio_server_manager_impl.hpp b/libraries/libstratosphere/source/gpio/server/gpio_server_manager_impl.hpp new file mode 100644 index 000000000..18863acfc --- /dev/null +++ b/libraries/libstratosphere/source/gpio/server/gpio_server_manager_impl.hpp @@ -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 . + */ +#pragma once +#include +#include "gpio_server_pad_session_impl.hpp" + +namespace ams::gpio::server { + + class ManagerImpl { + private: + lmem::HeapHandle heap_handle; + ams::sf::ExpHeapMemoryResource pad_session_memory_resource; + typename ams::sf::ServiceObjectAllocator pad_allocator; + u8 heap_buffer[12_KB]; + public: + ManagerImpl(); + + ~ManagerImpl(); + public: + /* Actual commands. */ + Result OpenSessionForDev(ams::sf::Out> out, s32 pad_descriptor); + Result OpenSession(ams::sf::Out> out, gpio::GpioPadName pad_name); + Result OpenSessionForTest(ams::sf::Out> out, gpio::GpioPadName pad_name); + Result IsWakeEventActive(ams::sf::Out out, gpio::GpioPadName pad_name); + Result GetWakeEventActiveFlagSet(ams::sf::Out out); + Result SetWakeEventActiveFlagSetForDebug(gpio::GpioPadName pad_name, bool is_enabled); + Result SetWakePinDebugMode(s32 mode); + Result OpenSession2(ams::sf::Out> out, DeviceCode device_code, ddsf::AccessMode access_mode); + Result IsWakeEventActive2(ams::sf::Out out, DeviceCode device_code); + Result SetWakeEventActiveFlagSetForDebug2(DeviceCode device_code, bool is_enabled); + Result SetRetryValues(u32 arg0, u32 arg1); + + }; + static_assert(gpio::sf::IsIManager); + +} diff --git a/libraries/libstratosphere/source/gpio/server/gpio_server_pad_session_impl.hpp b/libraries/libstratosphere/source/gpio/server/gpio_server_pad_session_impl.hpp new file mode 100644 index 000000000..afe6681bb --- /dev/null +++ b/libraries/libstratosphere/source/gpio/server/gpio_server_pad_session_impl.hpp @@ -0,0 +1,207 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::gpio::server { + + class ManagerImpl; + + class PadSessionImpl { + private: + ManagerImpl *parent; /* NOTE: this is an sf::SharedPointer<> in Nintendo's code. */ + gpio::driver::GpioPadSession internal_pad_session; + bool has_session; + os::SystemEvent system_event; + public: + explicit PadSessionImpl(ManagerImpl *p) : parent(p), has_session(false) { /* ... */ } + + ~PadSessionImpl() { + if (this->has_session) { + gpio::driver::CloseSession(std::addressof(this->internal_pad_session)); + } + } + + Result OpenSession(DeviceCode device_code, ddsf::AccessMode access_mode) { + AMS_ABORT_UNLESS(!this->has_session); + + R_TRY(gpio::driver::OpenSession(std::addressof(this->internal_pad_session), device_code, access_mode)); + this->has_session = true; + return ResultSuccess(); + } + public: + /* Actual commands. */ + Result SetDirection(gpio::Direction direction) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* Validate the direction. */ + R_UNLESS((direction == Direction_Input || direction == Direction_Output), gpio::ResultInvalidArgument()); + + /* Invoke the driver library. */ + R_TRY(gpio::driver::SetDirection(std::addressof(this->internal_pad_session), direction)); + + return ResultSuccess(); + } + + Result GetDirection(ams::sf::Out out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* Invoke the driver library. */ + R_TRY(gpio::driver::GetDirection(out.GetPointer(), std::addressof(this->internal_pad_session))); + + return ResultSuccess(); + } + + Result SetInterruptMode(gpio::InterruptMode mode) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result GetInterruptMode(ams::sf::Out out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result SetInterruptEnable(bool enable) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result GetInterruptEnable(ams::sf::Out out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result GetInterruptStatus(ams::sf::Out out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result ClearInterruptStatus() { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result SetValue(gpio::GpioValue value) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* Validate the value. */ + R_UNLESS((value == GpioValue_Low || value == GpioValue_High), gpio::ResultInvalidArgument()); + + /* Invoke the driver library. */ + R_TRY(gpio::driver::SetValue(std::addressof(this->internal_pad_session), value)); + + return ResultSuccess(); + } + + Result GetValue(ams::sf::Out out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* Invoke the driver library. */ + R_TRY(gpio::driver::GetValue(out.GetPointer(), std::addressof(this->internal_pad_session))); + + return ResultSuccess(); + } + + Result BindInterrupt(ams::sf::OutCopyHandle out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result UnbindInterrupt() { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result SetDebounceEnabled(bool enable) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result GetDebounceEnabled(ams::sf::Out out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result SetDebounceTime(s32 ms) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result GetDebounceTime(ams::sf::Out out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result SetValueForSleepState(gpio::GpioValue value) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + + Result GetValueForSleepState(ams::sf::Out out) { + /* Validate our state. */ + AMS_ASSERT(this->has_session); + + /* TODO */ + AMS_ABORT(); + } + }; + static_assert(gpio::sf::IsIPadSession); + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_bus_device_map.inc b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_bus_device_map.inc new file mode 100644 index 000000000..30170202b --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_bus_device_map.inc @@ -0,0 +1,71 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by i2cgen.py, do not edit manually. */ + +constexpr inline const I2cDeviceDefinition I2c1DeviceList[] = { + { DeviceCode_ClassicController, 0x52 }, + { DeviceCode_Nct72, 0x4C }, + { DeviceCode_Alc5639, 0x1C }, + { DeviceCode_Bq24193, 0x6B }, + { DeviceCode_Max17050, 0x36 }, + { DeviceCode_Bm92t30mwv, 0x18 }, +}; + +constexpr inline const I2cDeviceDefinition I2c2DeviceList[] = { + { DeviceCode_Ina226Vdd15v0Hb, 0x40 }, + { DeviceCode_Ina226VsysCpuDs, 0x41 }, + { DeviceCode_Ina226VsysGpuDs, 0x44 }, + { DeviceCode_Ina226VsysDdrDs, 0x45 }, + { DeviceCode_Ina226VsysAp, 0x46 }, + { DeviceCode_Ina226VsysBlDs, 0x47 }, + { DeviceCode_Bh1730, 0x29 }, + { DeviceCode_Ina226VsysCore, 0x48 }, + { DeviceCode_Ina226Soc1V8, 0x49 }, + { DeviceCode_Ina226Lpddr1V8, 0x4A }, + { DeviceCode_Ina226Reg1V32, 0x4B }, + { DeviceCode_Ina226Vdd3V3Sys, 0x4D }, + { DeviceCode_Ina226VddDdr0V6, 0x4E }, + { DeviceCode_HoagNfcIc, 0x08 }, +}; + +constexpr inline const I2cDeviceDefinition I2c3DeviceList[] = { + { DeviceCode_Ftm3bd56, 0x49 }, +}; + +constexpr inline const I2cDeviceDefinition I2c4DeviceList[] = { + { DeviceCode_HdmiDdc, 0x50 }, + { DeviceCode_HdmiScdc, 0x54 }, + { DeviceCode_HdmiHdcp, 0x3A }, +}; + +constexpr inline const I2cDeviceDefinition I2c5DeviceList[] = { + { DeviceCode_Max77620Rtc, 0x68 }, + { DeviceCode_Max77620Pmic, 0x3C }, + { DeviceCode_Max77621Cpu, 0x1B }, + { DeviceCode_Max77621Gpu, 0x1C }, + { DeviceCode_Fan53528, 0x52 }, + { DeviceCode_Max77812_3, 0x31 }, + { DeviceCode_Max77812_2, 0x33 }, +}; + +constexpr inline const I2cBusDefinition I2cBusList[] = { + { DeviceCode_I2c1, 0x7000C000, 0x100, SpeedMode_Standard, 70, I2c1DeviceList, util::size(I2c1DeviceList) }, + { DeviceCode_I2c2, 0x7000C400, 0x100, SpeedMode_Fast, 116, I2c2DeviceList, util::size(I2c2DeviceList) }, + { DeviceCode_I2c3, 0x7000C500, 0x100, SpeedMode_Fast, 124, I2c3DeviceList, util::size(I2c3DeviceList) }, + { DeviceCode_I2c4, 0x7000C700, 0x100, SpeedMode_Standard, 152, I2c4DeviceList, util::size(I2c4DeviceList) }, + { DeviceCode_I2c5, 0x7000D000, 0x100, SpeedMode_Fast, 85, I2c5DeviceList, util::size(I2c5DeviceList) }, +}; diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp new file mode 100644 index 000000000..bab1817fc --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/i2c_driver_api.cpp @@ -0,0 +1,108 @@ +/* + * 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 . + */ +#include +#include "impl/i2c_bus_manager.hpp" +#include "impl/i2c_device_property_manager.hpp" + +namespace ams::i2c::driver::board::nintendo_nx { + + namespace { + + struct I2cDeviceDefinition { + DeviceCode device_code; + u8 slave_address; + }; + + struct I2cBusDefinition { + DeviceCode device_code; + dd::PhysicalAddress registers_phys_addr; + size_t registers_size; + SpeedMode speed_mode; + os::InterruptName interrupt_name; + const I2cDeviceDefinition *devices; + size_t num_devices; + + constexpr bool IsPowerBus() const { + return this->device_code == DeviceCode_I2c5; + } + }; + + #include "i2c_bus_device_map.inc" + + void CheckSpeedMode(SpeedMode speed_mode) { + switch (speed_mode) { + case SpeedMode_Standard: break; + case SpeedMode_Fast: break; + case SpeedMode_FastPlus: break; + case SpeedMode_HighSpeed: break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void Initialize(impl::I2cBusAccessorManager &bus_manager, impl::I2cDevicePropertyManager &device_manager) { + /* Create an accessor for each bus. */ + for (const auto &bus_def : I2cBusList) { + /* Check that the speed mode is valid. */ + CheckSpeedMode(bus_def.speed_mode); + + /* Find the bus. */ + auto *bus = bus_manager.Find([&bus_def](const auto &it) { + return it.GetRegistersPhysicalAddress() == bus_def.registers_phys_addr; + }); + + /* If the bus doesn't exist, create it. */ + if (bus == nullptr) { + /* Allocate the bus. */ + bus = bus_manager.Allocate(); + + /* Initialize the bus. */ + bus->Initialize(bus_def.registers_phys_addr, bus_def.registers_size, bus_def.interrupt_name, bus_def.IsPowerBus(), bus_def.speed_mode); + + /* Register the bus. */ + i2c::driver::RegisterDriver(bus); + } + + /* Set the bus's device code. */ + bus->RegisterDeviceCode(bus_def.device_code); + + /* Allocate and register the devices for the bus. */ + for (size_t i = 0; i < bus_def.num_devices; ++i) { + /* Get the device definition. */ + const auto &entry = bus_def.devices[i]; + + /* Allocate the device. */ + I2cDeviceProperty *device = device_manager.Allocate(entry.slave_address, AddressingMode_SevenBit); + + /* Register the device with our bus. */ + bus->RegisterDevice(device); + + /* Register the device code with our driver. */ + R_ABORT_UNLESS(i2c::driver::RegisterDeviceCode(entry.device_code, device)); + } + } + } + + } + + void Initialize() { + /* TODO: Should these be moved into getters? They're only used here, and they never destruct. */ + static impl::I2cBusAccessorManager s_bus_accessor_manager(ddsf::GetMemoryResource()); + static impl::I2cDevicePropertyManager s_device_manager(ddsf::GetMemoryResource()); + + return Initialize(s_bus_accessor_manager, s_device_manager); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp new file mode 100644 index 000000000..5e8dff88a --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.cpp @@ -0,0 +1,771 @@ +/* + * 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 . + */ +#include +#include "i2c_bus_accessor.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + namespace { + + constexpr inline TimeSpan Timeout = TimeSpan::FromMilliSeconds(100); + + #define IO_PACKET_BITS_MASK(NAME) REG_NAMED_BITS_MASK (_IMPL_IO_PACKET_, NAME) + #define IO_PACKET_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (_IMPL_IO_PACKET_, NAME, VALUE) + #define IO_PACKET_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (_IMPL_IO_PACKET_, NAME, ENUM) + #define IO_PACKET_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(_IMPL_IO_PACKET_, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_IO_PACKET_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (_IMPL_IO_PACKET_, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_IO_PACKET_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_IO_PACKET_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_IO_PACKET_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_IO_PACKET_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (_IMPL_IO_PACKET_, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_IO_PACKET_REG_THREE_BIT_ENUM(HEADER_WORD0_PKT_TYPE, 0, REQUEST, RESPONSE, INTERRUPT, STOP, RSVD4, RSVD5, RSVD6, RSVD7); + DEFINE_IO_PACKET_REG_FOUR_BIT_ENUM(HEADER_WORD0_PROTOCOL, 4, RSVD0, I2C, RSVD2, RSVD3, RSVD4, RSVD5, RSVD6, RSVD7, RSVD8, RSVD9, RSVD10, RSVD11, RSVD12, RSVD13, RSVD14, RSVD15) + DEFINE_IO_PACKET_REG(HEADER_WORD0_CONTROLLER_ID, 12, 4); + DEFINE_IO_PACKET_REG(HEADER_WORD0_PKT_ID, 16, 8); + DEFINE_IO_PACKET_REG_TWO_BIT_ENUM(HEADER_WORD0_PROT_HDR_SZ, 28, 1_WORD, 2_WORD, 3_WORD, 4_WORD); + + DEFINE_IO_PACKET_REG(HEADER_WORD1_PAYLOAD_SIZE, 0, 12); + + DEFINE_IO_PACKET_REG(PROTOCOL_HEADER_SLAVE_ADDR, 0, 10); + DEFINE_IO_PACKET_REG(PROTOCOL_HEADER_HS_MASTER_ADDR, 12, 3); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_CONTINUE_XFER, 15, USE_REPEAT_START_TOP, CONTINUE); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_REPEAT_START_STOP, 16, STOP_CONDITION, REPEAT_START_CONDITION); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_IE, 17, DISABLE, ENABLE); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_ADDRESS_MODE, 18, SEVEN_BIT, TEN_BIT); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_READ_WRITE, 19, WRITE, READ); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_SEND_START_BYTE, 20, DISABLE, ENABLE); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_CONTINUE_ON_NACK, 21, DISABLE, ENABLE); + DEFINE_IO_PACKET_REG_BIT_ENUM(PROTOCOL_HEADER_HS_MODE, 22, DISABLE, ENABLE); + + } + + void I2cBusAccessor::Initialize(dd::PhysicalAddress reg_paddr, size_t reg_size, os::InterruptName intr, bool pb, SpeedMode sm) { + AMS_ASSERT(this->state == State::NotInitialized); + + this->is_power_bus = pb; + this->speed_mode = sm; + this->interrupt_name = intr; + this->registers_phys_addr = reg_paddr; + this->registers_size = reg_size; + this->state = State::Initializing; + } + + void I2cBusAccessor::RegisterDeviceCode(DeviceCode dc) { + AMS_ASSERT(this->state == State::Initializing); + + this->device_code = dc; + } + + void I2cBusAccessor::InitializeDriver() { + AMS_ASSERT(this->state == State::Initializing); + + this->registers = reinterpret_cast(dd::QueryIoMapping(this->registers_phys_addr, this->registers_size)); + AMS_ABORT_UNLESS(this->registers != nullptr); + + this->state = State::Initialized; + } + + void I2cBusAccessor::FinalizeDriver() { + AMS_ASSERT(this->state == State::Initialized); + this->state = State::Initializing; + } + + Result I2cBusAccessor::InitializeDevice(I2cDeviceProperty *device) { + /* Check that the device is valid. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(this->state == State::Initialized); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* Increment our user count -- if we're already open, we're done. */ + AMS_ASSERT(this->user_count >= 0); + ++this->user_count; + R_SUCCEED_IF(this->user_count > 1); + + /* Initialize our interrupt event. */ + os::InitializeInterruptEvent(std::addressof(this->interrupt_event), this->interrupt_name, os::EventClearMode_ManualClear); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + + /* If we're not power bus, perform power management init. */ + if (!this->is_power_bus) { + /* Initialize regulator library. */ + regulator::Initialize(); + + /* Try to open regulator session. */ + R_TRY(this->TryOpenRegulatorSession()); + + /* If we have a regulator session, set voltage to 2.9V. */ + if (this->has_regulator_session) { + /* NOTE: Nintendo does not check the result, here. */ + regulator::SetVoltageValue(std::addressof(this->regulator_session), 2'900'000u); + } + + /* Initialize clock/reset library. */ + clkrst::Initialize(); + } + + /* Execute initial config. */ + this->ExecuteInitialConfig(); + + /* If we have a regulator session, enable voltage. */ + if (!this->is_power_bus && this->has_regulator_session) { + /* Check whether voltage was already enabled. */ + const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session)); + + /* NOTE: Nintendo does not check the result of this call. */ + regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true); + + /* If we enabled voltage, delay to give our enable time to take. */ + if (!was_enabled) { + os::SleepThread(TimeSpan::FromMicroSeconds(560)); + } + } + + return ResultSuccess(); + } + + void I2cBusAccessor::FinalizeDevice(I2cDeviceProperty *device) { + /* Check that the device is valid. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(this->state == State::Initialized); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* Increment our user count -- if we're not the last user, we're done. */ + AMS_ASSERT(this->user_count > 0); + --this->user_count; + if (this->user_count > 0) { + return; + } + + /* Finalize our interrupt event. */ + os::FinalizeInterruptEvent(std::addressof(this->interrupt_event)); + + /* If we have a regulator session, disable voltage. */ + if (this->has_regulator_session) { + /* NOTE: Nintendo does not check the result of this call. */ + regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false); + } + + /* Finalize the clock/reset library. */ + clkrst::Finalize(); + + /* If we have a regulator session, close it. */ + if (this->has_regulator_session) { + regulator::CloseSession(std::addressof(this->regulator_session)); + this->has_regulator_session = false; + } + + /* Finalize the regulator library. */ + regulator::Finalize(); + } + + Result I2cBusAccessor::Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) { + /* Check pre-conditions. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(src != nullptr); + AMS_ASSERT(src_size > 0); + + if (this->is_power_bus) { + AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended); + } else { + AMS_ASSERT(this->state == State::Initialized); + } + + /* Send the data. */ + return this->Send(static_cast(src), src_size, option, device->GetAddress(), device->GetAddressingMode()); + } + + Result I2cBusAccessor::Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) { + /* Check pre-conditions. */ + AMS_ASSERT(device != nullptr); + AMS_ASSERT(dst != nullptr); + AMS_ASSERT(dst_size > 0); + + if (this->is_power_bus) { + AMS_ASSERT(this->state == State::Initialized || this->state == State::Suspended); + } else { + AMS_ASSERT(this->state == State::Initialized); + } + + /* Send the data. */ + return this->Receive(static_cast(dst), dst_size, option, device->GetAddress(), device->GetAddressingMode()); + } + + void I2cBusAccessor::SuspendBus() { + /* Check that state is valid. */ + AMS_ASSERT(this->state == State::Initialized); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* If we need to, disable clock/voltage appropriately. */ + if (!this->is_power_bus && this->user_count > 0) { + /* Disable clock. */ + { + /* Open a clkrst session. */ + clkrst::ClkRstSession clkrst_session; + R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); + ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; + + /* Set clock disabled for the session. */ + clkrst::SetClockDisabled(std::addressof(clkrst_session)); + } + + /* Disable voltage. */ + if (this->has_regulator_session) { + regulator::SetVoltageEnabled(std::addressof(this->regulator_session), false); + } + } + + /* Update state. */ + this->state = State::Suspended; + } + + void I2cBusAccessor::SuspendPowerBus() { + /* Check that state is valid. */ + AMS_ASSERT(this->state == State::Suspended); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* If we need to, disable clock/voltage appropriately. */ + if (this->is_power_bus && this->user_count > 0) { + /* Nothing should actually be done here. */ + } + + /* Update state. */ + this->state = State::PowerBusSuspended; + } + + void I2cBusAccessor::ResumeBus() { + /* Check that state is valid. */ + AMS_ASSERT(this->state == State::Suspended); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* If we need to, enable clock/voltage appropriately. */ + if (!this->is_power_bus && this->user_count > 0) { + /* Enable voltage. */ + if (this->has_regulator_session) { + /* Check whether voltage was already enabled. */ + const bool was_enabled = regulator::GetVoltageEnabled(std::addressof(this->regulator_session)); + + /* NOTE: Nintendo does not check the result of this call. */ + regulator::SetVoltageEnabled(std::addressof(this->regulator_session), true); + + /* If we enabled voltage, delay to give our enable time to take. */ + if (!was_enabled) { + os::SleepThread(TimeSpan::FromMicroSeconds(560)); + } + } + + /* Execute initial config, which will enable clock as relevant. */ + this->ExecuteInitialConfig(); + } + + /* Update state. */ + this->state = State::Initialized; + } + + void I2cBusAccessor::ResumePowerBus() { + /* Check that state is valid. */ + AMS_ASSERT(this->state == State::PowerBusSuspended); + + /* Acquire exclusive access. */ + std::scoped_lock lk(this->user_count_mutex); + + /* If we need to, enable clock/voltage appropriately. */ + if (this->is_power_bus && this->user_count > 0) { + /* Execute initial config, which will enable clock as relevant. */ + this->ExecuteInitialConfig(); + } + + /* Update state. */ + this->state = State::Suspended; + } + + Result I2cBusAccessor::TryOpenRegulatorSession() { + /* Ensure we track the session. */ + this->has_regulator_session = true; + auto s_guard = SCOPE_GUARD { this->has_regulator_session = false; }; + + /* Try to open the session. */ + R_TRY_CATCH(regulator::OpenSession(std::addressof(this->regulator_session), this->device_code)) { + R_CATCH(ddsf::ResultDeviceCodeNotFound) { + /* It's okay if the device isn't found, but we don't have a session if so. */ + this->has_regulator_session = false; + } + } R_END_TRY_CATCH; + + /* We opened (or not). */ + s_guard.Cancel(); + return ResultSuccess(); + } + + void I2cBusAccessor::ExecuteInitialConfig() { + /* Lock exclusive access to registers. */ + std::scoped_lock lk(this->register_mutex); + + /* Reset the controller. */ + this->ResetController(); + + /* Set clock registers. */ + this->SetClockRegisters(this->speed_mode); + + /* Set packet mode registers. */ + this->SetPacketModeRegisters(); + + /* Flush fifos. */ + this->FlushFifos(); + } + + Result I2cBusAccessor::Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { + /* Acquire exclusive access to the registers. */ + std::scoped_lock lk(this->register_mutex); + + /* Configure interrupt mask, clear interrupt status. */ + reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_DATA_REQ_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); + + reg::Write(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET)); + + /* Write the header. */ + this->WriteHeader(Xfer_Write, src_size, option, slave_address, addressing_mode); + + /* Setup tracking variables for the data. */ + const u8 *cur = src; + size_t remaining = src_size; + + while (true) { + /* Get the number of empty bytes in the fifo status. */ + const u32 empty = reg::GetValue(this->registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_TX_FIFO_EMPTY_CNT)); + + /* Write up to (empty) bytes to the fifo. */ + for (u32 i = 0; remaining > 0 && i < empty; ++i) { + /* Build the data word to send. */ + const size_t cur_bytes = std::min(remaining, sizeof(u32)); + + u32 word = 0; + for (size_t j = 0; j < cur_bytes; ++j) { + word |= cur[j] << (BITSIZEOF(u8) * j); + } + + /* Write the data word. */ + reg::Write(this->registers->tx_packet_fifo, word); + + /* Advance. */ + cur += cur_bytes; + remaining -= cur_bytes; + } + + /* If we're done, break. */ + if (remaining == 0) { + break; + } + + /* Wait for our current data to send. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultTimeout(); + } + + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + } + + /* Configure interrupt mask to not care about tfifo data req. */ + reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); + + /* Wait for the packet transfer to complete. */ + while (true) { + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + + /* Check if packet transfer is done. */ + if (reg::HasValue(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET))) { + break; + } + + /* Wait for our the packet to transfer. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultTimeout(); + } + } + + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + + /* We're done. */ + this->DisableInterruptMask(); + return ResultSuccess(); + } + + Result I2cBusAccessor::Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { + /* Acquire exclusive access to the registers. */ + std::scoped_lock lk(this->register_mutex); + + /* Configure interrupt mask, clear interrupt status. */ + reg::Write(this->registers->interrupt_mask_register, I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_DATA_REQ_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, ENABLE), + I2C_REG_BITS_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, ENABLE)); + + reg::Write(this->registers->interrupt_status_register, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, SET), + I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, SET)); + + /* Write the header. */ + this->WriteHeader(Xfer_Read, dst_size, option, slave_address, addressing_mode); + + /* Setup tracking variables for the data. */ + u8 *cur = dst; + size_t remaining = dst_size; + + while (remaining > 0) { + /* Wait for data to come in. */ + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), Timeout)) { + /* We timed out. */ + this->HandleTransactionError(i2c::ResultBusBusy()); + + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return i2c::ResultTimeout(); + } + + /* Check and handle any errors. */ + R_TRY(this->CheckAndHandleError()); + + /* Get the number of full bytes in the fifo status. */ + const u32 full = reg::GetValue(this->registers->fifo_status, I2C_REG_BITS_MASK(FIFO_STATUS_RX_FIFO_FULL_CNT)); + + /* Determine how many words we can read. */ + const size_t cur_words = std::min(util::DivideUp(remaining, sizeof(u32)), static_cast(full)); + + /* Read the correct number of words from the fifo. */ + for (size_t i = 0; i < cur_words; ++i) { + /* Read the word from the fifo. */ + const u32 word = reg::Read(this->registers->rx_fifo); + + /* Copy bytes from the word. */ + const size_t cur_bytes = std::min(remaining, sizeof(u32)); + for (size_t j = 0; j < cur_bytes; ++j) { + cur[j] = (word >> (BITSIZEOF(u8) * j)) & 0xFF; + } + + /* Advance. */ + cur += cur_bytes; + remaining -= cur_bytes; + } + } + + /* We're done. */ + return ResultSuccess(); + } + + void I2cBusAccessor::WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode) { + /* Parse interesting values from our arguments. */ + const bool is_read = xfer == Xfer_Read; + const bool is_7_bit = addressing_mode == AddressingMode_SevenBit; + const bool is_stop = (option & TransactionOption_StopCondition) != 0; + const bool is_hs = this->speed_mode == SpeedMode_HighSpeed; + const u32 slave_addr = ((static_cast(slave_address) & 0x7F) << 1) | (is_read ? 1 : 0); + + /* Flush fifos. */ + this->FlushFifos(); + + /* Enqueue the first header word. */ + reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_ENUM (HEADER_WORD0_PROT_HDR_SZ, 1_WORD), + IO_PACKET_BITS_VALUE(HEADER_WORD0_PKT_ID, 0), + IO_PACKET_BITS_VALUE(HEADER_WORD0_CONTROLLER_ID, 0), + IO_PACKET_BITS_ENUM (HEADER_WORD0_PROTOCOL, I2C), + IO_PACKET_BITS_ENUM (HEADER_WORD0_PKT_TYPE, REQUEST)); + + /* Enqueue the second header word. */ + reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_VALUE(HEADER_WORD1_PAYLOAD_SIZE, static_cast(size - 1))); + + /* Enqueue the protocol header word. */ + reg::Write(this->registers->tx_packet_fifo, IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_HS_MODE, is_hs, ENABLE, DISABLE), + IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_ON_NACK, DISABLE), + IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_SEND_START_BYTE, DISABLE), + IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_READ_WRITE, is_read, READ, WRITE), + IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_ADDRESS_MODE, is_7_bit, SEVEN_BIT, TEN_BIT), + IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_IE, ENABLE), + IO_PACKET_BITS_ENUM_SEL(PROTOCOL_HEADER_REPEAT_START_STOP, is_stop, STOP_CONDITION, REPEAT_START_CONDITION), + IO_PACKET_BITS_ENUM (PROTOCOL_HEADER_CONTINUE_XFER, USE_REPEAT_START_TOP), + IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_HS_MASTER_ADDR, 0), + IO_PACKET_BITS_VALUE (PROTOCOL_HEADER_SLAVE_ADDR, slave_addr)); + } + + void I2cBusAccessor::ResetController() const { + /* Reset the controller. */ + if (!this->is_power_bus) { + /* Open a clkrst session. */ + clkrst::ClkRstSession clkrst_session; + R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); + ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; + + /* Reset the controller, setting clock rate to 408 MHz / 5 (to account for clock divisor). */ + /* NOTE: Nintendo does not check result for any of these calls. */ + clkrst::SetResetAsserted(std::addressof(clkrst_session)); + clkrst::SetClockRate(std::addressof(clkrst_session), 408'000'000 / (4 + 1)); + clkrst::SetResetDeasserted(std::addressof(clkrst_session)); + } + } + + void I2cBusAccessor::ClearBus() const { + /* Try to clear the bus up to three times. */ + constexpr int MaxRetryCount = 3; + constexpr int BusyLoopMicroSeconds = 1000; + + int try_count = 0; + bool need_retry; + do { + /* Update trackers. */ + ++try_count; + need_retry = false; + + /* Reset the controller. */ + this->ResetController(); + + /* Configure the sclk threshold for bus clear config. */ + reg::Write(this->registers->bus_clear_config, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9)); + + /* Set stop cond and terminate in bus clear config. */ + reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, STOP)); + reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE)); + + /* Set master config load, busy loop up to 1ms for it to take. */ + reg::ReadWrite(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); + + const os::Tick start_tick_a = os::GetSystemTick(); + while (reg::HasValue(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE))) { + if ((os::GetSystemTick() - start_tick_a).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { + need_retry = true; + break; + } + } + + if (need_retry) { + continue; + } + + /* Set bus clear enable, wait up to 1ms for it to take. */ + reg::ReadWrite(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE)); + + const os::Tick start_tick_b = os::GetSystemTick(); + while (reg::HasValue(this->registers->bus_clear_config, I2C_REG_BITS_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE))) { + if ((os::GetSystemTick() - start_tick_b).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { + need_retry = true; + break; + } + } + + if (need_retry) { + continue; + } + + /* Wait up to 1ms for the bus clear to complete. */ + const os::Tick start_tick_c = os::GetSystemTick(); + while (reg::HasValue(this->registers->bus_clear_status, I2C_REG_BITS_ENUM(BUS_CLEAR_STATUS_BC_STATUS, NOT_CLEARED))) { + if ((os::GetSystemTick() - start_tick_c).ToTimeSpan().GetMicroSeconds() > BusyLoopMicroSeconds) { + need_retry = true; + break; + } + } + + if (need_retry) { + continue; + } + } while (try_count < MaxRetryCount && need_retry); + } + + void I2cBusAccessor::SetClockRegisters(SpeedMode speed_mode) { + /* Determine parameters for the speed mode. */ + u32 t_high, t_low, clk_div, debounce, src_div; + bool high_speed = false; + + if (this->is_power_bus) { + t_high = 0x02; + t_low = 0x04; + clk_div = 0x05; + debounce = 0x02; + src_div = 0; /* unused */ + } else { + switch (speed_mode) { + case SpeedMode_Standard: + t_high = 0x02; + t_low = 0x04; + clk_div = 0x19; + debounce = 0x02; + src_div = 0x13; + break; + case SpeedMode_Fast: + t_high = 0x02; + t_low = 0x04; + clk_div = 0x19; + debounce = 0x02; + src_div = 0x04; + break; + case SpeedMode_FastPlus: + t_high = 0x02; + t_low = 0x04; + clk_div = 0x10; + debounce = 0x00; + src_div = 0x02; + break; + case SpeedMode_HighSpeed: + t_high = 0x03; + t_low = 0x08; + clk_div = 0x02; + debounce = 0x00; + src_div = 0x02; + high_speed = true; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + /* Write the clock divisors. */ + if (high_speed) { + reg::Write(this->registers->hs_interface_timing_0, I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_THIGH, t_high), + I2C_REG_BITS_VALUE(HS_INTERFACE_TIMING_0_HS_TLOW, t_low)); + + reg::Write(this->registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, clk_div)); + } else { + reg::Write(this->registers->interface_timing_0, I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_THIGH, t_high), + I2C_REG_BITS_VALUE(INTERFACE_TIMING_0_TLOW, t_low)); + + reg::Write(this->registers->clk_divisor_register, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, clk_div)); + } + + /* Configure debounce. */ + reg::Write(this->registers->cnfg, I2C_REG_BITS_VALUE(I2C_CNFG_DEBOUNCE_CNT, debounce)); + reg::Read(this->registers->cnfg); + + /* Set the clock rate, if we should. */ + if (!this->is_power_bus) { + /* Open a clkrst session. */ + clkrst::ClkRstSession clkrst_session; + R_ABORT_UNLESS(clkrst::OpenSession(std::addressof(clkrst_session), this->device_code)); + ON_SCOPE_EXIT { clkrst::CloseSession(std::addressof(clkrst_session)); }; + + /* Reset the controller, setting clock rate to 408 MHz / (src_div + 1). */ + /* NOTE: Nintendo does not check result for any of these calls. */ + clkrst::SetResetAsserted(std::addressof(clkrst_session)); + clkrst::SetClockRate(std::addressof(clkrst_session), 408'000'000 / (src_div + 1)); + clkrst::SetResetDeasserted(std::addressof(clkrst_session)); + } + } + + void I2cBusAccessor::SetPacketModeRegisters() { + /* Set packet mode enable. */ + reg::ReadWrite(this->registers->cnfg, I2C_REG_BITS_ENUM(I2C_CNFG_PACKET_MODE_EN, GO)); + + /* Set master config load. */ + reg::ReadWrite(this->registers->config_load, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); + + /* Set tx/fifo triggers to default (maximum values). */ + reg::Write(this->registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7), + I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7)); + } + + Result I2cBusAccessor::FlushFifos() { + /* Flush the fifo. */ + reg::Write(this->registers->fifo_control, I2C_REG_BITS_VALUE(FIFO_CONTROL_RX_FIFO_TRIG, 7), + I2C_REG_BITS_VALUE(FIFO_CONTROL_TX_FIFO_TRIG, 7), + I2C_REG_BITS_ENUM (FIFO_CONTROL_RX_FIFO_FLUSH, SET), + I2C_REG_BITS_ENUM (FIFO_CONTROL_TX_FIFO_FLUSH, SET)); + + /* Wait up to 5 ms for the flush to complete. */ + int count = 0; + while (!reg::HasValue(this->registers->fifo_control, I2C_REG_BITS_ENUM(FIFO_CONTROL_FIFO_FLUSH, RX_UNSET_TX_UNSET))) { + R_UNLESS((++count < 5), i2c::ResultBusBusy()); + os::SleepThread(TimeSpan::FromMilliSeconds(1)); + } + + return ResultSuccess(); + } + + Result I2cBusAccessor::GetTransactionResult() const { + /* Get packet status/interrupt status. */ + volatile u32 packet_status = reg::Read(this->registers->packet_transfer_status); + volatile u32 interrupt_status = reg::Read(this->registers->interrupt_status_register); + + /* Check for ack. */ + R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_DATA, UNSET)), i2c::ResultNoAck()); + R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_ADDR, UNSET)), i2c::ResultNoAck()); + R_UNLESS(reg::HasValue(interrupt_status, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, UNSET)), i2c::ResultNoAck()); + + /* If we lost arbitration, we'll need to clear the bus. */ + auto clear_guard = SCOPE_GUARD { this->ClearBus(); }; + + /* Check for arb lost. */ + R_UNLESS(reg::HasValue(packet_status, I2C_REG_BITS_ENUM(PACKET_TRANSFER_STATUS_ARB_LOST, UNSET)), i2c::ResultBusBusy()); + R_UNLESS(reg::HasValue(interrupt_status, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, UNSET)), i2c::ResultBusBusy()); + + clear_guard.Cancel(); + return ResultSuccess(); + } + + void I2cBusAccessor::HandleTransactionError(Result result) { + R_TRY_CATCH(result) { + R_CATCH(i2c::ResultNoAck, i2c::ResultBusBusy) { + /* Reset the controller. */ + this->ResetController(); + + /* Set clock registers. */ + this->SetClockRegisters(this->speed_mode); + + /* Set packet mode registers. */ + this->SetPacketModeRegisters(); + + /* Flush fifos. */ + this->FlushFifos(); + } + } R_END_TRY_CATCH_WITH_ABORT_UNLESS; + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp new file mode 100644 index 000000000..8691dbaf5 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_accessor.hpp @@ -0,0 +1,137 @@ +/* + * 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 . + */ +#pragma once +#include +#include "i2c_i2c_registers.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + class I2cBusAccessor : public ::ams::i2c::driver::II2cDriver { + NON_COPYABLE(I2cBusAccessor); + NON_MOVEABLE(I2cBusAccessor); + AMS_DDSF_CASTABLE_TRAITS(ams::i2c::driver::board::nintendo_nx::impl::I2cBusAccessor, ::ams::i2c::driver::II2cDriver); + private: + enum class State { + NotInitialized = 0, + Initializing = 1, + Initialized = 2, + Suspended = 3, + PowerBusSuspended = 4, + }; + + enum Xfer { + Xfer_Write = 0, + Xfer_Read = 1, + }; + private: + volatile I2cRegisters *registers; + SpeedMode speed_mode; + os::InterruptEventType interrupt_event; + int user_count; + os::SdkMutex user_count_mutex; + os::SdkMutex register_mutex; + regulator::RegulatorSession regulator_session; + bool has_regulator_session; + State state; + os::SdkMutex transaction_order_mutex; + bool is_power_bus; + dd::PhysicalAddress registers_phys_addr; + size_t registers_size; + os::InterruptName interrupt_name; + DeviceCode device_code; + util::IntrusiveListNode bus_accessor_list_node; + public: + using BusAccessorListTraits = util::IntrusiveListMemberTraitsDeferredAssert<&I2cBusAccessor::bus_accessor_list_node>; + using BusAccessorList = typename BusAccessorListTraits::ListType; + friend class util::IntrusiveList>; + public: + I2cBusAccessor() + : registers(nullptr), speed_mode(SpeedMode_Fast), user_count(0), user_count_mutex(), + register_mutex(), has_regulator_session(false), state(State::NotInitialized), transaction_order_mutex(), + is_power_bus(false), registers_phys_addr(0), registers_size(0), interrupt_name(), device_code(-1), bus_accessor_list_node() + { + /* ... */ + } + + void Initialize(dd::PhysicalAddress reg_paddr, size_t reg_size, os::InterruptName intr, bool pb, SpeedMode sm); + void RegisterDeviceCode(DeviceCode device_code); + + SpeedMode GetSpeedMode() const { return this->speed_mode; } + dd::PhysicalAddress GetRegistersPhysicalAddress() const { return this->registers_phys_addr; } + size_t GetRegistersSize() const { return this->registers_size; } + os::InterruptName GetInterruptName() const { return this->interrupt_name; } + private: + Result TryOpenRegulatorSession(); + + void ExecuteInitialConfig(); + + Result Send(const u8 *src, size_t src_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); + Result Receive(u8 *dst, size_t dst_size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); + + void WriteHeader(Xfer xfer, size_t size, TransactionOption option, u16 slave_address, AddressingMode addressing_mode); + + void ResetController() const; + void ClearBus() const; + void SetClockRegisters(SpeedMode speed_mode); + void SetPacketModeRegisters(); + + Result FlushFifos(); + + Result GetTransactionResult() const; + void HandleTransactionError(Result result); + + void DisableInterruptMask() { + reg::Write(this->registers->interrupt_mask_register, 0); + reg::Read(this->registers->interrupt_mask_register); + } + + Result CheckAndHandleError() { + const Result result = this->GetTransactionResult(); + this->HandleTransactionError(result); + if (R_FAILED(result)) { + this->DisableInterruptMask(); + os::ClearInterruptEvent(std::addressof(this->interrupt_event)); + return result; + } + + return ResultSuccess(); + } + public: + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result InitializeDevice(I2cDeviceProperty *device) override; + virtual void FinalizeDevice(I2cDeviceProperty *device) override; + + virtual Result Send(I2cDeviceProperty *device, const void *src, size_t src_size, TransactionOption option) override; + virtual Result Receive(void *dst, size_t dst_size, I2cDeviceProperty *device, TransactionOption option) override; + + virtual os::SdkMutex &GetTransactionOrderMutex() override { + return this->transaction_order_mutex; + } + + virtual void SuspendBus() override; + virtual void SuspendPowerBus() override; + + virtual void ResumeBus() override; + virtual void ResumePowerBus() override; + + virtual const DeviceCode &GetDeviceCode() const override { + return this->device_code; + } + }; + +} diff --git a/stratosphere/boot/source/i2c/i2c_types.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp similarity index 75% rename from stratosphere/boot/source/i2c/i2c_types.hpp rename to libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp index 72a30237f..64ae4668b 100644 --- a/stratosphere/boot/source/i2c/i2c_types.hpp +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_bus_manager.hpp @@ -15,18 +15,13 @@ */ #pragma once #include +#include "i2c_bus_accessor.hpp" +#include "i2c_i_allocator.hpp" -namespace ams::i2c { +namespace ams::i2c::driver::board::nintendo_nx::impl { - enum class AddressingMode { - SevenBit = 0, - }; - - enum class SpeedMode { - Normal = 100000, - Fast = 400000, - FastPlus = 1000000, - HighSpeed = 3400000, + class I2cBusAccessorManager : public IAllocator { + /* ... */ }; } diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp new file mode 100644 index 000000000..726d87632 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_device_property_manager.hpp @@ -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 . + */ +#pragma once +#include +#include "i2c_i_allocator.hpp" + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + class I2cDevicePropertyManager : public IAllocator { + /* ... */ + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i2c_registers.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i2c_registers.hpp new file mode 100644 index 000000000..c8925ce6e --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i2c_registers.hpp @@ -0,0 +1,66 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + struct I2cRegisters { + volatile u32 cnfg; + volatile u32 cmd_addr0; + volatile u32 cmd_addr1; + volatile u32 cmd_data1; + volatile u32 cmd_data2; + volatile u32 _14; + volatile u32 _18; + volatile u32 status; + volatile u32 sl_cnfg; + volatile u32 sl_rcvd; + volatile u32 sl_status; + volatile u32 sl_addr1; + volatile u32 sl_addr2; + volatile u32 tlow_sext; + volatile u32 _38; + volatile u32 sl_delay_count; + volatile u32 sl_int_mask; + volatile u32 sl_int_source; + volatile u32 sl_int_set; + volatile u32 _4c; + volatile u32 tx_packet_fifo; + volatile u32 rx_fifo; + volatile u32 packet_transfer_status; + volatile u32 fifo_control; + volatile u32 fifo_status; + volatile u32 interrupt_mask_register; + volatile u32 interrupt_status_register; + volatile u32 clk_divisor_register; + volatile u32 interrupt_source_register; + volatile u32 interrupt_set_register; + volatile u32 slv_tx_packet_fifo; + volatile u32 slv_rx_fifo; + volatile u32 slv_packet_status; + volatile u32 bus_clear_config; + volatile u32 bus_clear_status; + volatile u32 config_load; + volatile u32 _90; + volatile u32 interface_timing_0; + volatile u32 interface_timing_1; + volatile u32 hs_interface_timing_0; + volatile u32 hs_interface_timing_1; + }; + static_assert(sizeof(I2cRegisters) == 0xA4); + +} diff --git a/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp new file mode 100644 index 000000000..270c882e1 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/board/nintendo_nx/impl/i2c_i_allocator.hpp @@ -0,0 +1,79 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::i2c::driver::board::nintendo_nx::impl { + + template + class IAllocator { + private: + using T = typename ListType::value_type; + private: + ams::MemoryResource *memory_resource; + ListType list; + mutable os::SdkMutex list_lock; + public: + IAllocator(ams::MemoryResource *mr) : memory_resource(mr), list(), list_lock() { /* ... */ } + + ~IAllocator() { + std::scoped_lock lk(this->list_lock); + + /* Remove all entries. */ + auto it = this->list.begin(); + while (it != this->list.end()) { + T *obj = std::addressof(*it); + it = this->list.erase(it); + + obj->~T(); + this->memory_resource->Deallocate(obj, sizeof(T)); + } + } + + template + T *Allocate(Args &&...args) { + std::scoped_lock lk(this->list_lock); + + /* Allocate space for the object. */ + void *storage = this->memory_resource->Allocate(sizeof(T), alignof(T)); + AMS_ABORT_UNLESS(storage != nullptr); + + /* Construct the object. */ + T *t = new (static_cast(storage)) T(std::forward(args)...); + + /* Link the object into our list. */ + this->list.push_back(*t); + + return t; + } + + template + T *Find(F f) { + std::scoped_lock lk(this->list_lock); + + for (T &it : this->list) { + if (f(static_cast(it))) { + return std::addressof(it); + } + } + + return nullptr; + } + + /* TODO: Support free */ + }; + +} diff --git a/libraries/libstratosphere/source/i2c/driver/i2c_driver_bus_api.cpp b/libraries/libstratosphere/source/i2c/driver/i2c_driver_bus_api.cpp new file mode 100644 index 000000000..3b11792ae --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/i2c_driver_bus_api.cpp @@ -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 . + */ +#include + +#include "impl/i2c_driver_core.hpp" + +namespace ams::i2c::driver { + + namespace { + + constexpr inline int DefaultRetryCount = 3; + constexpr inline TimeSpan DefaultRetryInterval = TimeSpan::FromMilliSeconds(5); + + Result OpenSessionImpl(I2cSession *out, I2cDeviceProperty *device) { + /* Construct the session. */ + auto *session = new (std::addressof(impl::GetI2cSessionImpl(*out))) impl::I2cSessionImpl(DefaultRetryCount, DefaultRetryInterval); + auto session_guard = SCOPE_GUARD { session->~I2cSessionImpl(); }; + + /* Open the session. */ + R_TRY(session->Open(device, ddsf::AccessMode_ReadWrite)); + + /* We succeeded. */ + session_guard.Cancel(); + return ResultSuccess(); + } + + } + + Result OpenSession(I2cSession *out, DeviceCode device_code) { + AMS_ASSERT(out != nullptr); + + /* Find the device. */ + I2cDeviceProperty *device = nullptr; + R_TRY(impl::FindDevice(std::addressof(device), device_code)); + AMS_ASSERT(device != nullptr); + + /* Open the session. */ + R_TRY(OpenSessionImpl(out, device)); + + return ResultSuccess(); + } + + void CloseSession(I2cSession &session) { + impl::GetOpenI2cSessionImpl(session).~I2cSessionImpl(); + } + + Result Send(I2cSession &session, const void *src, size_t src_size, TransactionOption option) { + AMS_ASSERT(src != nullptr); + AMS_ABORT_UNLESS(src_size > 0); + + return impl::GetOpenI2cSessionImpl(session).Send(src, src_size, option); + } + + Result Receive(void *dst, size_t dst_size, I2cSession &session, TransactionOption option) { + AMS_ASSERT(dst != nullptr); + AMS_ABORT_UNLESS(dst_size > 0); + + return impl::GetOpenI2cSessionImpl(session).Receive(dst, dst_size, option); + } + + Result ExecuteCommandList(void *dst, size_t dst_size, I2cSession &session, const void *src, size_t src_size) { + AMS_ASSERT(src != nullptr); + AMS_ASSERT(dst != nullptr); + + AMS_ABORT_UNLESS(src_size > 0); + AMS_ABORT_UNLESS(dst_size > 0); + + return impl::GetOpenI2cSessionImpl(session).ExecuteCommandList(dst, dst_size, src, src_size); + } + + Result SetRetryPolicy(I2cSession &session, int max_retry_count, int retry_interval_us) { + AMS_ASSERT(max_retry_count > 0); + AMS_ASSERT(retry_interval_us > 0); + + return impl::GetOpenI2cSessionImpl(session).SetRetryPolicy(max_retry_count, retry_interval_us); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/i2c_driver_client_api.cpp b/libraries/libstratosphere/source/i2c/driver/i2c_driver_client_api.cpp new file mode 100644 index 000000000..c5717e503 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/i2c_driver_client_api.cpp @@ -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 . + */ +#include + +#include "impl/i2c_driver_core.hpp" + +namespace ams::i2c::driver { + + void Initialize() { + return impl::InitializeDrivers(); + } + + void Finalize() { + return impl::FinalizeDrivers(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/i2c_driver_service_api.cpp b/libraries/libstratosphere/source/i2c/driver/i2c_driver_service_api.cpp new file mode 100644 index 000000000..340615743 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/i2c_driver_service_api.cpp @@ -0,0 +1,38 @@ +/* + * 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 . + */ +#include + +#include "impl/i2c_driver_core.hpp" + +namespace ams::i2c::driver { + + void RegisterDriver(II2cDriver *driver) { + return impl::RegisterDriver(driver); + } + + void UnregisterDriver(II2cDriver *driver) { + return impl::UnregisterDriver(driver); + } + + Result RegisterDeviceCode(DeviceCode device_code, I2cDeviceProperty *device) { + return impl::RegisterDeviceCode(device_code, device); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return impl::UnregisterDeviceCode(device_code); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.cpp b/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.cpp new file mode 100644 index 000000000..08d2609bc --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.cpp @@ -0,0 +1,130 @@ +/* + * 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 . + */ +#include +#include "i2c_driver_core.hpp" + +namespace ams::i2c::driver::impl { + + namespace { + + constinit os::SdkMutex g_init_mutex; + constinit int g_init_count = 0; + + i2c::driver::II2cDriver::List &GetI2cDriverList() { + static i2c::driver::II2cDriver::List s_driver_list; + return s_driver_list; + } + + ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() { + static ddsf::DeviceCodeEntryManager s_device_code_entry_manager(ddsf::GetDeviceCodeEntryHolderMemoryResource()); + return s_device_code_entry_manager; + } + + } + + + void InitializeDrivers() { + std::scoped_lock lk(g_init_mutex); + + /* Initialize all registered drivers, if this is our first initialization. */ + if ((g_init_count++) == 0) { + for (auto &driver : GetI2cDriverList()) { + driver.SafeCastTo().InitializeDriver(); + } + } + } + + void FinalizeDrivers() { + std::scoped_lock lk(g_init_mutex); + + /* If we have no remaining sessions, close. */ + if ((--g_init_count) == 0) { + /* Reset all device code entries. */ + GetDeviceCodeEntryManager().Reset(); + + /* Finalize all drivers. */ + for (auto &driver : GetI2cDriverList()) { + driver.SafeCastTo().FinalizeDriver(); + } + } + } + + void RegisterDriver(II2cDriver *driver) { + AMS_ASSERT(driver != nullptr); + GetI2cDriverList().push_back(*driver); + } + + void UnregisterDriver(II2cDriver *driver) { + AMS_ASSERT(driver != nullptr); + if (driver->IsLinkedToList()) { + auto &list = GetI2cDriverList(); + list.erase(list.iterator_to(*driver)); + } + } + + Result RegisterDeviceCode(DeviceCode device_code, I2cDeviceProperty *device) { + AMS_ASSERT(device != nullptr); + R_TRY(GetDeviceCodeEntryManager().Add(device_code, device)); + return ResultSuccess(); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return GetDeviceCodeEntryManager().Remove(device_code); + } + + Result FindDevice(I2cDeviceProperty **out, DeviceCode device_code) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Find the device. */ + ddsf::IDevice *device; + R_TRY(GetDeviceCodeEntryManager().FindDevice(std::addressof(device), device_code)); + + /* Set output. */ + *out = device->SafeCastToPointer(); + return ResultSuccess(); + } + + Result FindDeviceByBusIndexAndAddress(I2cDeviceProperty **out, i2c::I2cBus bus_index, u16 slave_address) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Convert the bus index to a device code. */ + const DeviceCode device_code = ConvertToDeviceCode(bus_index); + + /* Find the device. */ + bool found = false; + GetDeviceCodeEntryManager().ForEachEntry([&](ddsf::DeviceCodeEntry &entry) -> bool { + /* Convert the entry to an I2cDeviceProperty. */ + auto &device = entry.GetDevice().SafeCastTo(); + auto &driver = device.GetDriver().SafeCastTo(); + + /* Check if the device is the one we're looking for. */ + if (driver.GetDeviceCode() == device_code && device.GetAddress() == slave_address) { + found = true; + *out = std::addressof(device); + return false; + } + return true; + }); + + /* Check that we found the pad. */ + R_UNLESS(found, ddsf::ResultDeviceCodeNotFound()); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.hpp b/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.hpp new file mode 100644 index 000000000..9df2e60f5 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/impl/i2c_driver_core.hpp @@ -0,0 +1,33 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::i2c::driver::impl { + + void InitializeDrivers(); + void FinalizeDrivers(); + + void RegisterDriver(II2cDriver *driver); + void UnregisterDriver(II2cDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, I2cDeviceProperty *device); + bool UnregisterDeviceCode(DeviceCode device_code); + + Result FindDevice(I2cDeviceProperty **out, DeviceCode device_code); + Result FindDeviceByBusIndexAndAddress(I2cDeviceProperty **out, i2c::I2cBus bus_index, u16 slave_address); + +} diff --git a/libraries/libstratosphere/source/i2c/driver/impl/i2c_i2c_session_impl.cpp b/libraries/libstratosphere/source/i2c/driver/impl/i2c_i2c_session_impl.cpp new file mode 100644 index 000000000..413cc4ad5 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/driver/impl/i2c_i2c_session_impl.cpp @@ -0,0 +1,199 @@ +/* + * 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 . + */ +#include +#include "i2c_driver_core.hpp" +#include "../../impl/i2c_command_list_format.hpp" + +namespace ams::i2c::driver::impl { + + namespace { + + constexpr TransactionOption EncodeTransactionOption(bool start, bool stop) { + return static_cast((start ? TransactionOption_StartCondition : 0) | (stop ? TransactionOption_StopCondition : 0)); + } + + } + + Result I2cSessionImpl::Open(I2cDeviceProperty *device, ddsf::AccessMode access_mode) { + AMS_ASSERT(device != nullptr); + + /* Check if we're the device's first session. */ + const bool first = !device->HasAnyOpenSession(); + + /* Open the session. */ + R_TRY(ddsf::OpenSession(device, this, access_mode)); + auto guard = SCOPE_GUARD { ddsf::CloseSession(this); }; + + /* If we're the first session, initialize the device. */ + if (first) { + R_TRY(device->GetDriver().SafeCastTo().InitializeDevice(device)); + } + + /* We're opened. */ + guard.Cancel(); + return ResultSuccess(); + } + + void I2cSessionImpl::Close() { + /* If we're not open, do nothing. */ + if (!this->IsOpen()) { + return; + } + + /* Get the device. */ + auto &device = this->GetDevice().SafeCastTo(); + + /* Close the session. */ + ddsf::CloseSession(this); + + /* If there are no remaining sessions, finalize the device. */ + if (!device.HasAnyOpenSession()) { + device.GetDriver().SafeCastTo().FinalizeDevice(std::addressof(device)); + } + } + + Result I2cSessionImpl::SendHandler(const u8 **cur_cmd, u8 **cur_dst) { + /* Read the header bytes. */ + const util::BitPack8 hdr0{*((*cur_cmd)++)}; + const util::BitPack8 hdr1{*((*cur_cmd)++)}; + + /* Decode the header. */ + const bool start = hdr0.Get(); + const bool stop = hdr0.Get(); + const size_t size = hdr1.Get(); + + /* Execute the transaction. */ + R_TRY(this->ExecuteTransactionWithRetry(nullptr, Command::Send, *cur_cmd, size, EncodeTransactionOption(start, stop))); + + /* Advance. */ + *cur_cmd += size; + + return ResultSuccess(); + } + + Result I2cSessionImpl::ReceiveHandler(const u8 **cur_cmd, u8 **cur_dst) { + /* Read the header bytes. */ + const util::BitPack8 hdr0{*((*cur_cmd)++)}; + const util::BitPack8 hdr1{*((*cur_cmd)++)}; + + /* Decode the header. */ + const bool start = hdr0.Get(); + const bool stop = hdr0.Get(); + const size_t size = hdr1.Get(); + + /* Execute the transaction. */ + R_TRY(this->ExecuteTransactionWithRetry(*cur_dst, Command::Receive, nullptr, size, EncodeTransactionOption(start, stop))); + + /* Advance. */ + *cur_dst += size; + + return ResultSuccess(); + } + + Result I2cSessionImpl::ExtensionHandler(const u8 **cur_cmd, u8 **cur_dst) { + /* Read the header bytes. */ + const util::BitPack8 hdr0{*((*cur_cmd)++)}; + + /* Execute the subcommand. */ + switch (hdr0.Get()) { + case i2c::impl::SubCommandId_Sleep: + { + const util::BitPack8 param{*((*cur_cmd)++)}; + + os::SleepThread(TimeSpan::FromMicroSeconds(param.Get())); + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + Result I2cSessionImpl::ExecuteTransactionWithRetry(void *dst, Command command, const void *src, size_t size, TransactionOption option) { + /* Get the device. */ + auto &device = GetDevice().SafeCastTo(); + + /* Repeatedly try to execute the transaction. */ + int retry_count; + while (true) { + /* Execute the transaction. */ + Result result; + switch (command) { + case Command::Send: result = device.GetDriver().SafeCastTo().Send(std::addressof(device), src, size, option); break; + case Command::Receive: result = device.GetDriver().SafeCastTo().Receive(dst, size, std::addressof(device), option); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* If we timed out, retry up to our max retry count. */ + R_TRY_CATCH(result) { + R_CATCH(i2c::ResultTimeout) { + if ((++retry_count) <= this->max_retry_count) { + os::SleepThread(this->retry_interval); + continue; + } + return i2c::ResultBusBusy(); + } + } R_END_TRY_CATCH; + + return ResultSuccess(); + } + } + + Result I2cSessionImpl::Send(const void *src, size_t src_size, TransactionOption option) { + /* Acquire exclusive access to the device. */ + std::scoped_lock lk(this->GetDevice().SafeCastTo().GetDriver().SafeCastTo().GetTransactionOrderMutex()); + + return this->ExecuteTransactionWithRetry(nullptr, Command::Send, src, src_size, option); + } + + Result I2cSessionImpl::Receive(void *dst, size_t dst_size, TransactionOption option) { + /* Acquire exclusive access to the device. */ + std::scoped_lock lk(this->GetDevice().SafeCastTo().GetDriver().SafeCastTo().GetTransactionOrderMutex()); + + return this->ExecuteTransactionWithRetry(dst, Command::Receive, nullptr, dst_size, option); + } + + Result I2cSessionImpl::ExecuteCommandList(void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Acquire exclusive access to the device. */ + std::scoped_lock lk(this->GetDevice().SafeCastTo().GetDriver().SafeCastTo().GetTransactionOrderMutex()); + + /* Prepare to process the command list. */ + const u8 * cur_u8 = static_cast(src); + const u8 * const end_u8 = cur_u8 + src_size; + u8 * dst_u8 = static_cast(dst); + + /* Process commands. */ + while (cur_u8 < end_u8) { + const util::BitPack8 hdr{*cur_u8}; + + switch (hdr.Get()) { + case i2c::impl::CommandId_Send: R_TRY(this->SendHandler(std::addressof(cur_u8), std::addressof(dst_u8))); break; + case i2c::impl::CommandId_Receive: R_TRY(this->ReceiveHandler(std::addressof(cur_u8), std::addressof(dst_u8))); break; + case i2c::impl::CommandId_Extension: R_TRY(this->ExtensionHandler(std::addressof(cur_u8), std::addressof(dst_u8))); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + return ResultSuccess(); + } + + Result I2cSessionImpl::SetRetryPolicy(int mr, int interval_us) { + this->max_retry_count = mr; + this->retry_interval = TimeSpan::FromMicroSeconds(interval_us); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/i2c_client_api.cpp b/libraries/libstratosphere/source/i2c/i2c_client_api.cpp new file mode 100644 index 000000000..e8d3a74a7 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/i2c_client_api.cpp @@ -0,0 +1,165 @@ +/* + * 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 . + */ +#include + +namespace ams::i2c { + + namespace { + + constinit os::SdkMutex g_init_mutex; + constinit int g_initialize_count = 0; + + constinit os::SdkMutex g_i2c_mutex; + std::shared_ptr g_i2c_manager; + constinit int g_i2c_count = 0; + + constinit os::SdkMutex g_i2c_pcv_mutex; + std::shared_ptr g_i2c_pcv_manager; + constinit int g_i2c_pcv_count = 0; + + using InternalSession = std::shared_ptr; + + InternalSession &GetInterface(const I2cSession &session) { + AMS_ASSERT(session._session != nullptr); + return *static_cast(session._session); + } + + std::shared_ptr GetManager(DeviceCode device_code) { + if (IsPowerBusDeviceCode(device_code)) { + return g_i2c_pcv_manager; + } else { + return g_i2c_manager; + } + } + + } + + void InitializeWith(std::shared_ptr &&sp, std::shared_ptr &&sp_pcv) { + std::scoped_lock lk(g_init_mutex); + + AMS_ABORT_UNLESS(g_initialize_count == 0); + + { + std::scoped_lock lk(g_i2c_mutex); + g_i2c_manager = std::move(sp); + AMS_ABORT_UNLESS(g_i2c_count == 0); + g_i2c_count = 1; + } + + { + std::scoped_lock lk(g_i2c_pcv_mutex); + g_i2c_pcv_manager = std::move(sp); + AMS_ABORT_UNLESS(g_i2c_pcv_count == 0); + g_i2c_pcv_count = 1; + } + + g_initialize_count = 1; + } + + void InitializeEmpty() { + std::scoped_lock lk(g_init_mutex); + + ++g_initialize_count; + } + + void Finalize() { + std::scoped_lock lk(g_init_mutex); + + AMS_ASSERT(g_initialize_count > 0); + + if ((--g_initialize_count) == 0) { + { + std::scoped_lock lk(g_i2c_mutex); + AMS_ASSERT(g_i2c_count > 0); + if (g_i2c_count > 0) { + if ((--g_i2c_count) == 0) { + g_i2c_manager.reset(); + } + } + } + { + std::scoped_lock lk(g_i2c_pcv_mutex); + AMS_ASSERT(g_i2c_pcv_count > 0); + if (g_i2c_pcv_count > 0) { + if ((--g_i2c_pcv_count) == 0) { + g_i2c_pcv_manager.reset(); + } + } + } + } + } + + Result OpenSession(I2cSession *out, 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 manager for the device. */ + auto manager = GetManager(device_code); + + /* Get the session. */ + { + ams::sf::cmif::ServiceObjectHolder object_holder; + if (hos::GetVersion() >= hos::Version_6_0_0) { + R_TRY(manager->OpenSession2(std::addressof(object_holder), device_code)); + } else { + R_TRY(manager->OpenSession(std::addressof(object_holder), ConvertToI2cDevice(device_code))); + } + *internal_session = object_holder.GetServiceObject(); + } + + /* Set output. */ + out->_session = internal_session; + + /* We succeeded. */ + session_guard.Cancel(); + return ResultSuccess(); + } + + void CloseSession(I2cSession &session) { + /* Close the session. */ + delete std::addressof(GetInterface(session)); + session._session = nullptr; + } + + Result Send(const I2cSession &session, const void *src, size_t src_size, TransactionOption option) { + const ams::sf::InAutoSelectBuffer buf(src, src_size); + + return GetInterface(session)->Send(buf, option); + } + + Result Receive(void *dst, size_t dst_size, const I2cSession &session, TransactionOption option) { + const ams::sf::OutAutoSelectBuffer buf(dst, dst_size); + + return GetInterface(session)->Receive(buf, option); + } + + Result ExecuteCommandList(void *dst, size_t dst_size, const I2cSession &session, const void *src, size_t src_size) { + const ams::sf::OutAutoSelectBuffer buf(dst, dst_size); + const ams::sf::InPointerArray arr(static_cast(src), src_size); + + return GetInterface(session)->ExecuteCommandList(buf, arr); + } + + void SetRetryPolicy(const I2cSession &session, int max_retry_count, int retry_interval_us) { + AMS_ASSERT(max_retry_count >= 0); + AMS_ASSERT(retry_interval_us >= 0); + + R_ABORT_UNLESS(GetInterface(session)->SetRetryPolicy(max_retry_count, retry_interval_us)); + } + +} diff --git a/libraries/libstratosphere/source/i2c/i2c_command_list_formatter.cpp b/libraries/libstratosphere/source/i2c/i2c_command_list_formatter.cpp new file mode 100644 index 000000000..37a6dc65b --- /dev/null +++ b/libraries/libstratosphere/source/i2c/i2c_command_list_formatter.cpp @@ -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 . + */ +#include +#include "impl/i2c_command_list_format.hpp" + +namespace ams::i2c { + + Result CommandListFormatter::IsEnqueueAble(size_t sz) const { + R_UNLESS(this->command_list_length - this->current_index >= sz, ResultCommandListFull()); + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueReceiveCommand(i2c::TransactionOption option, size_t size) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Receive); + header0.Set((option & TransactionOption_StopCondition) != 0); + header0.Set((option & TransactionOption_StartCondition) != 0); + + header1.Set(size); + + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueSendCommand(i2c::TransactionOption option, const void *src, size_t size) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength + size)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Send); + header0.Set((option & TransactionOption_StopCondition) != 0); + header0.Set((option & TransactionOption_StartCondition) != 0); + + header1.Set(size); + + /* Copy the data we're sending. */ + std::memcpy(cmd_list + this->current_index, src, size); + this->current_index += size; + + return ResultSuccess(); + } + + Result CommandListFormatter::EnqueueSleepCommand(int us) { + /* Check that we can enqueue the command. */ + constexpr size_t CommandLength = 2; + R_TRY(this->IsEnqueueAble(CommandLength)); + + /* Get the command list. */ + util::BitPack8 *cmd_list = static_cast(this->command_list); + + /* Get references to the header. */ + auto &header0 = cmd_list[this->current_index++]; + auto &header1 = cmd_list[this->current_index++]; + + /* Set the header. */ + header0.Set(impl::CommandId_Extension); + header0.Set(impl::SubCommandId_Sleep); + + header1.Set(us); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp b/libraries/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp new file mode 100644 index 000000000..c4f4ad923 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/impl/i2c_command_list_format.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::i2c::impl { + + enum CommandId { + CommandId_Send = 0, + CommandId_Receive = 1, + CommandId_Extension = 2, + CommandId_Count = 3, + }; + + enum SubCommandId { + SubCommandId_Sleep = 0, + }; + + struct CommonCommandFormat { + using CommandId = util::BitPack8::Field<0, 2>; + using SubCommandId = util::BitPack8::Field<2, 6>; + }; + + struct ReceiveCommandFormat { + using StartCondition = util::BitPack8::Field<6, 1, bool>; + using StopCondition = util::BitPack8::Field<7, 1, bool>; + using Size = util::BitPack8::Field<0, 8>; + }; + + struct SendCommandFormat { + using StartCondition = util::BitPack8::Field<6, 1, bool>; + using StopCondition = util::BitPack8::Field<7, 1, bool>; + using Size = util::BitPack8::Field<0, 8>; + }; + + struct SleepCommandFormat { + using MicroSeconds = util::BitPack8::Field<0, 8>; + }; + +} diff --git a/libraries/libstratosphere/source/i2c/server/i2c_server_api.cpp b/libraries/libstratosphere/source/i2c/server/i2c_server_api.cpp new file mode 100644 index 000000000..dcd6a987c --- /dev/null +++ b/libraries/libstratosphere/source/i2c/server/i2c_server_api.cpp @@ -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 . + */ +#include +#include "i2c_server_manager_impl.hpp" + +namespace ams::i2c::server { + + namespace { + + ManagerImpl g_manager_impl; + ManagerImpl g_pcv_manager_impl; + + std::shared_ptr GetManagerServiceObject() { + static std::shared_ptr s_sp = ams::sf::GetSharedPointerTo(g_manager_impl); + return s_sp; + } + + std::shared_ptr GetManagerServiceObjectPowerBus() { + static std::shared_ptr s_sp = ams::sf::GetSharedPointerTo(g_pcv_manager_impl); + return s_sp; + } + + } + + std::shared_ptr GetServiceObject() { + return GetManagerServiceObject(); + } + + std::shared_ptr GetServiceObjectPowerBus() { + return GetManagerServiceObjectPowerBus(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/server/i2c_server_manager_impl.cpp b/libraries/libstratosphere/source/i2c/server/i2c_server_manager_impl.cpp new file mode 100644 index 000000000..579fccd24 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/server/i2c_server_manager_impl.cpp @@ -0,0 +1,61 @@ +/* + * 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 . + */ +#include +#include "i2c_server_manager_impl.hpp" + +namespace ams::i2c::server { + + ManagerImpl::ManagerImpl() : session_memory_resource(), allocator(std::addressof(session_memory_resource)) { + this->heap_handle = lmem::CreateExpHeap(this->heap_buffer, sizeof(this->heap_buffer), lmem::CreateOption_None); + this->session_memory_resource.Attach(this->heap_handle); + } + + ManagerImpl::~ManagerImpl() { + lmem::DestroyExpHeap(this->heap_handle); + } + + Result ManagerImpl::OpenSessionForDev(ams::sf::Out> out, s32 bus_idx, u16 slave_address, i2c::AddressingMode addressing_mode, i2c::SpeedMode speed_mode) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::OpenSession(ams::sf::Out> out, i2c::I2cDevice device) { + return this->OpenSession2(out, ConvertToDeviceCode(device)); + } + + Result ManagerImpl::HasDevice(ams::sf::Out out, i2c::I2cDevice device) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::HasDeviceForDev(ams::sf::Out out, i2c::I2cDevice device) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::OpenSession2(ams::sf::Out> out, DeviceCode device_code) { + /* Allocate a session. */ + auto session = ams::sf::AllocateShared(this->allocator, this); + + /* Open the session. */ + R_TRY(session->GetImpl().OpenSession(device_code)); + + /* We succeeded. */ + out.SetValue(std::move(session)); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/i2c/server/i2c_server_manager_impl.hpp b/libraries/libstratosphere/source/i2c/server/i2c_server_manager_impl.hpp new file mode 100644 index 000000000..821504648 --- /dev/null +++ b/libraries/libstratosphere/source/i2c/server/i2c_server_manager_impl.hpp @@ -0,0 +1,42 @@ +/* + * 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 . + */ +#pragma once +#include +#include "i2c_server_session_impl.hpp" + +namespace ams::i2c::server { + + class ManagerImpl { + private: + lmem::HeapHandle heap_handle; + ams::sf::ExpHeapMemoryResource session_memory_resource; + typename ams::sf::ServiceObjectAllocator allocator; + u8 heap_buffer[4_KB]; + public: + ManagerImpl(); + + ~ManagerImpl(); + public: + /* Actual commands. */ + Result OpenSessionForDev(ams::sf::Out> out, s32 bus_idx, u16 slave_address, i2c::AddressingMode addressing_mode, i2c::SpeedMode speed_mode); + Result OpenSession(ams::sf::Out> out, i2c::I2cDevice device); + Result HasDevice(ams::sf::Out out, i2c::I2cDevice device); + Result HasDeviceForDev(ams::sf::Out out, i2c::I2cDevice device); + Result OpenSession2(ams::sf::Out> out, DeviceCode device_code); + }; + static_assert(i2c::sf::IsIManager); + +} diff --git a/libraries/libstratosphere/source/i2c/server/i2c_server_session_impl.hpp b/libraries/libstratosphere/source/i2c/server/i2c_server_session_impl.hpp new file mode 100644 index 000000000..11c52d38e --- /dev/null +++ b/libraries/libstratosphere/source/i2c/server/i2c_server_session_impl.hpp @@ -0,0 +1,76 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::i2c::server { + + class ManagerImpl; + + class SessionImpl { + private: + ManagerImpl *parent; /* NOTE: this is an sf::SharedPointer<> in Nintendo's code. */ + i2c::driver::I2cSession internal_session; + bool has_session; + public: + explicit SessionImpl(ManagerImpl *p) : parent(p), has_session(false) { /* ... */ } + + ~SessionImpl() { + if (this->has_session) { + i2c::driver::CloseSession(this->internal_session); + } + } + + Result OpenSession(DeviceCode device_code) { + AMS_ABORT_UNLESS(!this->has_session); + + R_TRY(i2c::driver::OpenSession(std::addressof(this->internal_session), device_code)); + this->has_session = true; + return ResultSuccess(); + } + public: + /* Actual commands. */ + Result SendOld(const ams::sf::InBuffer &in_data, i2c::TransactionOption option) { + return i2c::driver::Send(this->internal_session, in_data.GetPointer(), in_data.GetSize(), option); + } + + Result ReceiveOld(const ams::sf::OutBuffer &out_data, i2c::TransactionOption option) { + return i2c::driver::Receive(out_data.GetPointer(), out_data.GetSize(), this->internal_session, option); + } + + Result ExecuteCommandListOld(const ams::sf::OutBuffer &rcv_buf, const ams::sf::InPointerArray &command_list){ + return i2c::driver::ExecuteCommandList(rcv_buf.GetPointer(), rcv_buf.GetSize(), this->internal_session, command_list.GetPointer(), command_list.GetSize() * sizeof(i2c::I2cCommand)); + } + + Result Send(const ams::sf::InAutoSelectBuffer &in_data, i2c::TransactionOption option) { + return i2c::driver::Send(this->internal_session, in_data.GetPointer(), in_data.GetSize(), option); + } + + Result Receive(const ams::sf::OutAutoSelectBuffer &out_data, i2c::TransactionOption option) { + return i2c::driver::Receive(out_data.GetPointer(), out_data.GetSize(), this->internal_session, option); + } + + Result ExecuteCommandList(const ams::sf::OutAutoSelectBuffer &rcv_buf, const ams::sf::InPointerArray &command_list) { + return i2c::driver::ExecuteCommandList(rcv_buf.GetPointer(), rcv_buf.GetSize(), this->internal_session, command_list.GetPointer(), command_list.GetSize() * sizeof(i2c::I2cCommand)); + } + + Result SetRetryPolicy(s32 max_retry_count, s32 retry_interval_us) { + return i2c::driver::SetRetryPolicy(this->internal_session, max_retry_count, retry_interval_us); + } + }; + static_assert(i2c::sf::IsISession); + +} diff --git a/libraries/libstratosphere/source/os/os_interrupt_event.cpp b/libraries/libstratosphere/source/os/os_interrupt_event.cpp index e2bd5811f..54de8d18b 100644 --- a/libraries/libstratosphere/source/os/os_interrupt_event.cpp +++ b/libraries/libstratosphere/source/os/os_interrupt_event.cpp @@ -15,6 +15,7 @@ */ #include #include "impl/os_interrupt_event_impl.hpp" +#include "impl/os_waitable_holder_impl.hpp" #include "impl/os_waitable_object_list.hpp" namespace ams::os { @@ -61,4 +62,12 @@ namespace ams::os { return GetReference(event->impl).Clear(); } + void InitializeWaitableHolder(WaitableHolderType *waitable_holder, InterruptEventType *event) { + AMS_ASSERT(event->state == InterruptEventType::State_Initialized); + + new (GetPointer(waitable_holder->impl_storage)) impl::WaitableHolderOfInterruptEvent(event); + + waitable_holder->user_data = 0; + } + } diff --git a/libraries/libstratosphere/source/os/os_sdk_condition_variable.cpp b/libraries/libstratosphere/source/os/os_sdk_condition_variable.cpp new file mode 100644 index 000000000..78bff6612 --- /dev/null +++ b/libraries/libstratosphere/source/os/os_sdk_condition_variable.cpp @@ -0,0 +1,48 @@ +/* + * 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 . + */ +#include +#include "impl/os_thread_manager.hpp" +#include "impl/os_timeout_helper.hpp" + +namespace ams::os { + + void SdkConditionVariableType::Wait(SdkMutexType &mutex) { + /* Check that we own the mutex. */ + AMS_ABORT_UNLESS(os::IsSdkMutexLockedByCurrentThread(std::addressof(mutex))); + + /* Wait on the mutex. */ + GetReference(this->_storage).Wait(GetPointer(mutex._storage)); + } + + bool SdkConditionVariableType::TimedWait(SdkMutexType &mutex, TimeSpan timeout) { + /* Check preconditions. */ + AMS_ASSERT(timeout.GetNanoSeconds() >= 0); + AMS_ABORT_UNLESS(os::IsSdkMutexLockedByCurrentThread(std::addressof(mutex))); + + /* Handle zero timeout by unlocking and re-locking. */ + if (timeout == TimeSpan(0)) { + GetReference(mutex._storage).Leave(); + GetReference(mutex._storage).Enter(); + return false; + } + + /* Handle timed wait. */ + impl::TimeoutHelper timeout_helper(timeout); + auto status = GetReference(this->_storage).TimedWait(GetPointer(mutex._storage), timeout_helper); + return status == ConditionVariableStatus::Success; + } + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.cpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.cpp new file mode 100644 index 000000000..fabedbbd4 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.cpp @@ -0,0 +1,138 @@ +/* + * 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 . + */ +#include +#include "pinmux_pad_index.hpp" +#include "pinmux_board_driver_api.hpp" +#include "pinmux_platform_pads.hpp" + +namespace ams::pinmux::driver::board::nintendo_nx { + + namespace { + + constinit bool g_initialized = false; + + #include "pinmux_initial_pad_config_icosa.inc" + #include "pinmux_initial_pad_config_hoag.inc" + #include "pinmux_initial_pad_config_iowa.inc" + #include "pinmux_initial_pad_config_calcio.inc" + #include "pinmux_initial_pad_config_five.inc" + + #include "pinmux_initial_drive_pad_config.inc" + #include "pinmux_initial_drive_pad_config_hoag.inc" + + } + + bool IsInitialized() { + return g_initialized; + } + + void Initialize() { + InitializePlatformPads(); + g_initialized = true; + } + + void Finalize() { + /* ... */ + } + + void SetInitialConfig() { + const PinmuxPadConfig *configs = nullptr; + size_t num_configs = 0; + bool is_mariko = false; + switch (spl::GetHardwareType()) { + case spl::HardwareType::Icosa: + configs = PinmuxPadConfigsIcosa; + num_configs = NumPinmuxPadConfigsIcosa; + is_mariko = false; + break; + case spl::HardwareType::Hoag: + configs = PinmuxPadConfigsHoag; + num_configs = NumPinmuxPadConfigsHoag; + is_mariko = true; + break; + case spl::HardwareType::Iowa: + configs = PinmuxPadConfigsIowa; + num_configs = NumPinmuxPadConfigsIowa; + is_mariko = true; + break; + case spl::HardwareType::Calcio: + configs = PinmuxPadConfigsCalcio; + num_configs = NumPinmuxPadConfigsCalcio; + is_mariko = true; + break; + case spl::HardwareType::_Five_: + configs = PinmuxPadConfigsFive; + num_configs = NumPinmuxPadConfigsFive; + is_mariko = true; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + AMS_ABORT_UNLESS(configs != nullptr); + + for (size_t i = 0; i < num_configs; ++i) { + UpdateSinglePinmuxPad(configs[i]); + } + + if (is_mariko) { + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Clk, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Cmd, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat0, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat1, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat2, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat3, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat4, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat5, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat6, 0x2000, 0x2000 }); + UpdateSinglePinmuxPad({ PinmuxPadIndex_Sdmmc2Dat7, 0x2000, 0x2000 }); + } + } + + void SetInitialDrivePadConfig() { + const PinmuxDrivePadConfig *configs = nullptr; + size_t num_configs = 0; + switch (spl::GetHardwareType()) { + case spl::HardwareType::Icosa: + configs = PinmuxDrivePadConfigs; + num_configs = NumPinmuxDrivePadConfigs; + break; + case spl::HardwareType::Hoag: + configs = PinmuxDrivePadConfigsHoag; + num_configs = NumPinmuxDrivePadConfigsHoag; + break; + case spl::HardwareType::Iowa: + configs = PinmuxDrivePadConfigs; + num_configs = NumPinmuxDrivePadConfigs; + break; + case spl::HardwareType::Calcio: + configs = PinmuxDrivePadConfigs; + num_configs = NumPinmuxDrivePadConfigs; + break; + case spl::HardwareType::_Five_: + configs = PinmuxDrivePadConfigs; + num_configs = NumPinmuxDrivePadConfigs; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + AMS_ABORT_UNLESS(configs != nullptr); + + for (size_t i = 0; i < num_configs; ++i) { + UpdateSinglePinmuxDrivePad(configs[i]); + } + } + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.hpp new file mode 100644 index 000000000..48be15d8a --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_board_driver_api.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::pinmux::driver::board::nintendo_nx { + + bool IsInitialized(); + + void Initialize(); + void Finalize(); + + void SetInitialConfig(); + void SetInitialDrivePadConfig(); + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_drive_pad_characters.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_drive_pad_characters.inc new file mode 100644 index 000000000..a6ed61f30 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_drive_pad_characters.inc @@ -0,0 +1,170 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ + +constexpr inline const PinmuxDrivePadCharacter PinmuxDrivePadCharacters[] = { + { 0x08E4, 0x01F1F000, "AlsProxInt" }, + { 0x08E8, 0x01F1F000, "ApReady" }, + { 0x08EC, 0x01F1F000, "ApWakeBt" }, + { 0x08F0, 0x01F1F000, "ApWakeNfc" }, + { 0x08F4, 0x01F1F000, "AudMclk" }, + { 0x08F8, 0x01F1F000, "BattBcl" }, + { 0x08FC, 0x01F1F000, "BtRst" }, + { 0x0900, 0x01F1F000, "BtWakeAp" }, + { 0x0904, 0x01F1F000, "ButtonHome" }, + { 0x0908, 0x01F1F000, "ButtonPowerOn" }, + { 0x090C, 0x01F1F000, "ButtonSlideSw" }, + { 0x0910, 0x01F1F000, "ButtonVolDown" }, + { 0x0914, 0x01F1F000, "ButtonVolUp" }, + { 0x0918, 0x01F1F000, "Cam1Mclk" }, + { 0x091C, 0x01F1F000, "Cam1Pwdn" }, + { 0x0920, 0x01F1F000, "Cam1Strobe" }, + { 0x0924, 0x01F1F000, "Cam2Mclk" }, + { 0x0928, 0x01F1F000, "Cam2Pwdn" }, + { 0x092C, 0x01F1F000, "CamAfEn" }, + { 0x0930, 0x01F1F000, "CamFlashEn" }, + { 0x0934, 0x01F1F000, "CamI2cScl" }, + { 0x0938, 0x01F1F000, "CamI2cSda" }, + { 0x093C, 0x01F1F000, "CamRst" }, + { 0x0940, 0x01F1F000, "Clk32kIn" }, + { 0x0944, 0x01F1F000, "Clk32kOut" }, + { 0x0948, 0x01F1F000, "ClkReq" }, + { 0x094C, 0x01F1F000, "CorePwrReq" }, + { 0x0950, 0x01F1F000, "CpuPwrReq" }, + { 0x0954, 0xF0000000, "Dap1Din" }, + { 0x0958, 0xF0000000, "Dap1Dout" }, + { 0x095C, 0xF0000000, "Dap1Fs" }, + { 0x0960, 0xF0000000, "Dap1Sclk" }, + { 0x0964, 0xF0000000, "Dap2Din" }, + { 0x0968, 0xF0000000, "Dap2Dout" }, + { 0x096C, 0xF0000000, "Dap2Fs" }, + { 0x0970, 0xF0000000, "Dap2Sclk" }, + { 0x0974, 0x01F1F000, "Dap4Din" }, + { 0x0978, 0x01F1F000, "Dap4Dout" }, + { 0x097C, 0x01F1F000, "Dap4Fs" }, + { 0x0980, 0x01F1F000, "Dap4Sclk" }, + { 0x0984, 0x01F1F000, "Dmic1Clk" }, + { 0x0988, 0x01F1F000, "Dmic1Dat" }, + { 0x098C, 0x01F1F000, "Dmic2Clk" }, + { 0x0990, 0x01F1F000, "Dmic2Dat" }, + { 0x0994, 0x01F1F000, "Dmic3Clk" }, + { 0x0998, 0x01F1F000, "Dmic3Dat" }, + { 0x099C, 0x01F1F000, "DpHpd" }, + { 0x09A0, 0x01F1F000, "DvfsClk" }, + { 0x09A4, 0x01F1F000, "DvfsPwm" }, + { 0x09A8, 0x01F1F000, "Gen1I2cScl" }, + { 0x09AC, 0x01F1F000, "Gen1I2cSda" }, + { 0x09B0, 0x01F1F000, "Gen2I2cScl" }, + { 0x09B4, 0x01F1F000, "Gen2I2cSda" }, + { 0x09B8, 0x01F1F000, "Gen3I2cScl" }, + { 0x09BC, 0x01F1F000, "Gen3I2cSda" }, + { 0x09C0, 0x01F1F000, "GpioPa6" }, + { 0x09C4, 0x01F1F000, "GpioPcc7" }, + { 0x09C8, 0x01F1F000, "GpioPe6" }, + { 0x09CC, 0x01F1F000, "GpioPe7" }, + { 0x09D0, 0x01F1F000, "GpioPh6" }, + { 0x09D4, 0xF0000000, "GpioPk0" }, + { 0x09D8, 0xF0000000, "GpioPk1" }, + { 0x09DC, 0xF0000000, "GpioPk2" }, + { 0x09E0, 0xF0000000, "GpioPk3" }, + { 0x09E4, 0xF0000000, "GpioPk4" }, + { 0x09E8, 0xF0000000, "GpioPk5" }, + { 0x09EC, 0xF0000000, "GpioPk6" }, + { 0x09F0, 0xF0000000, "GpioPk7" }, + { 0x09F4, 0xF0000000, "GpioPl0" }, + { 0x09F8, 0xF0000000, "GpioPl1" }, + { 0x09FC, 0x07F7F000, "GpioPz0" }, + { 0x0A00, 0x07F7F000, "GpioPz1" }, + { 0x0A04, 0x07F7F000, "GpioPz2" }, + { 0x0A08, 0x07F7F000, "GpioPz3" }, + { 0x0A0C, 0x07F7F000, "GpioPz4" }, + { 0x0A10, 0x07F7F000, "GpioPz5" }, + { 0x0A14, 0x01F1F000, "GpioX1Aud" }, + { 0x0A18, 0x01F1F000, "GpioX3Aud" }, + { 0x0A1C, 0x01F1F000, "GpsEn" }, + { 0x0A20, 0x01F1F000, "GpsRst" }, + { 0x0A24, 0x01F1F000, "HdmiCec" }, + { 0x0A28, 0x01F1F000, "HdmiIntDpHpd" }, + { 0x0A2C, 0x01F1F000, "JtagRtck" }, + { 0x0A30, 0x01F1F000, "LcdBlEn" }, + { 0x0A34, 0x01F1F000, "LcdBlPwm" }, + { 0x0A38, 0x01F1F000, "LcdGpio1" }, + { 0x0A3C, 0x01F1F000, "LcdGpio2" }, + { 0x0A40, 0x01F1F000, "LcdRst" }, + { 0x0A44, 0x01F1F000, "LcdTe" }, + { 0x0A48, 0x01F1F000, "ModemWakeAp" }, + { 0x0A4C, 0x01F1F000, "MotionInt" }, + { 0x0A50, 0x01F1F000, "NfcEn" }, + { 0x0A54, 0x01F1F000, "NfcInt" }, + { 0x0A58, 0x01F1F000, "PexL0ClkReqN" }, + { 0x0A5C, 0x01F1F000, "PexL0RstN" }, + { 0x0A60, 0x01F1F000, "PexL1ClkreqN" }, + { 0x0A64, 0x01F1F000, "PexL1RstN" }, + { 0x0A68, 0x01F1F000, "PexWakeN" }, + { 0x0A6C, 0x01F1F000, "PwrI2cScl" }, + { 0x0A70, 0x01F1F000, "PwrI2cSda" }, + { 0x0A74, 0x01F1F000, "PwrIntN" }, + { 0x0A78, 0x07F7F000, "QspiComp" }, + { 0x0A90, 0xF0000000, "QspiSck" }, + { 0x0A94, 0x01F1F000, "SataLedActive" }, + { 0x0A98, 0xF7F7F000, "Sdmmc1Pad" }, + { 0x0AB0, 0xF7F7F000, "Sdmmc3Pad" }, + { 0x0AC8, 0x01F1F000, "Shutdown" }, + { 0x0ACC, 0x01F1F000, "SpdifIn" }, + { 0x0AD0, 0x01F1F000, "SpdifOut" }, + { 0x0AD4, 0xF0000000, "Spi1Cs0" }, + { 0x0AD8, 0xF0000000, "Spi1Cs1" }, + { 0x0ADC, 0xF0000000, "Spi1Miso" }, + { 0x0AE0, 0xF0000000, "Spi1Mosi" }, + { 0x0AE4, 0xF0000000, "Spi1Sck" }, + { 0x0AE8, 0xF0000000, "Spi2Cs0" }, + { 0x0AEC, 0xF0000000, "Spi2Cs1" }, + { 0x0AF0, 0xF0000000, "Spi2Miso" }, + { 0x0AF4, 0xF0000000, "Spi2Mosi" }, + { 0x0AF8, 0xF0000000, "Spi2Sck" }, + { 0x0AFC, 0xF0000000, "Spi4Cs0" }, + { 0x0B00, 0xF0000000, "Spi4Miso" }, + { 0x0B04, 0xF0000000, "Spi4Mosi" }, + { 0x0B08, 0xF0000000, "Spi4Sck" }, + { 0x0B0C, 0x01F1F000, "TempAlert" }, + { 0x0B10, 0x01F1F000, "TouchClk" }, + { 0x0B14, 0x01F1F000, "TouchInt" }, + { 0x0B18, 0x01F1F000, "TouchRst" }, + { 0x0B1C, 0x01F1F000, "Uart1Cts" }, + { 0x0B20, 0x01F1F000, "Uart1Rts" }, + { 0x0B24, 0x01F1F000, "Uart1Rx" }, + { 0x0B28, 0x01F1F000, "Uart1Tx" }, + { 0x0B2C, 0x01F1F000, "Uart2Cts" }, + { 0x0B30, 0x01F1F000, "Uart2Rts" }, + { 0x0B34, 0x01F1F000, "Uart2Rx" }, + { 0x0B38, 0x01F1F000, "Uart2Tx" }, + { 0x0B3C, 0x01F1F000, "Uart3Cts" }, + { 0x0B40, 0x01F1F000, "Uart3Rts" }, + { 0x0B44, 0x01F1F000, "Uart3Rx" }, + { 0x0B48, 0x01F1F000, "Uart3Tx" }, + { 0x0B4C, 0x01F1F000, "Uart4Cts" }, + { 0x0B50, 0x01F1F000, "Uart4Rts" }, + { 0x0B54, 0x01F1F000, "Uart4Rx" }, + { 0x0B58, 0x01F1F000, "Uart4Tx" }, + { 0x0B5C, 0x01F1F000, "UsbVbusEn0" }, + { 0x0B60, 0x01F1F000, "UsbVbusEn1" }, + { 0x0B64, 0x01F1F000, "WifiEn" }, + { 0x0B68, 0x01F1F000, "WifiRst" }, + { 0x0B6C, 0x01F1F000, "WifiWakeAp" }, +}; + +constexpr inline size_t NumPinmuxDrivePadCharacters = util::size(PinmuxDrivePadCharacters); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config.inc new file mode 100644 index 000000000..2caa61518 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config.inc @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by drive_pad_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxDrivePadConfig PinmuxDrivePadConfigs[] = { + { PinmuxDrivePadIndex_AudMclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Cam1Mclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Cam2Mclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamAfEn, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamFlashEn, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamI2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamI2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Din, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Dout, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Fs, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Sclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic1Clk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic1Dat, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic2Clk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic2Dat, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic3Clk, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic3Dat, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_DvfsClk, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_DvfsPwm, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen1I2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen1I2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen2I2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen2I2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen3I2cScl, 0x00007000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen3I2cSda, 0x00007000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioPz0, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioPz1, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioX1Aud, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioX3Aud, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_PwrI2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_PwrI2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_TouchClk, 0x01414000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Cts, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Rts, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Rx, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Tx, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Spi1Cs0, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Cs1, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Miso, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Mosi, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Sck, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Cs0, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Cs1, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Miso, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Mosi, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Sck, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Sdmmc3Pad, 0x51212000, 0xF1F1F000 }, +}; + +constexpr inline const size_t NumPinmuxDrivePadConfigs = util::size(PinmuxDrivePadConfigs); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config_hoag.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config_hoag.inc new file mode 100644 index 000000000..8831b8f4b --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_drive_pad_config_hoag.inc @@ -0,0 +1,69 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by drive_pad_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxDrivePadConfig PinmuxDrivePadConfigsHoag[] = { + { PinmuxDrivePadIndex_AudMclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Cam1Mclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Cam2Mclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamAfEn, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamFlashEn, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamI2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_CamI2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Din, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Dout, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Fs, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dap4Sclk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic1Clk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic1Dat, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic2Clk, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic2Dat, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic3Clk, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Dmic3Dat, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_DvfsClk, 0x01F1F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_DvfsPwm, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen1I2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen1I2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen2I2cScl, 0x00004000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen2I2cSda, 0x00004000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen3I2cScl, 0x00007000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Gen3I2cSda, 0x00007000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioPz0, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioPz1, 0x01010000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioX1Aud, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_GpioX3Aud, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_PwrI2cScl, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_PwrI2cSda, 0x0001F000, 0x01F1F000 }, + { PinmuxDrivePadIndex_TouchClk, 0x01414000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Cts, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Rts, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Rx, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Uart3Tx, 0x01404000, 0x01F1F000 }, + { PinmuxDrivePadIndex_Spi1Cs0, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Cs1, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Miso, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Mosi, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi1Sck, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Cs0, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Cs1, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Miso, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Mosi, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Spi2Sck, 0x00000000, 0xF0000000 }, + { PinmuxDrivePadIndex_Sdmmc3Pad, 0x51212000, 0xF1F1F000 }, +}; + +constexpr inline const size_t NumPinmuxDrivePadConfigsHoag = util::size(PinmuxDrivePadConfigsHoag); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_calcio.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_calcio.inc new file mode 100644 index 000000000..94bca5c42 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_calcio.inc @@ -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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsCalcio[] = { +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_HdmiCec, 0x00000240, 0x0000027F }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000204, 0x0000027F }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_Spi1Mosi, 0x0000000C, 0x0000007F }, +{ PinmuxPadIndex_Spi1Miso, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_Spi1Sck, 0x0000000C, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_DpHpd0, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000024, 0x0000027F }, +{ PinmuxPadIndex_AudMclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioX3Aud, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap1Din, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap1Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap1Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cSda, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamFlashEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SataLedActive, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_AlsProxInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_MotionInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdBlEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz4, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000018 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000018 }, +{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Rx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Rts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Cts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPl1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi4Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi4Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart3Tx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart3Rx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart3Rts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart3Cts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_NfcEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_NfcInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifIn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsCalcio = util::size(PinmuxPadConfigsCalcio); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_five.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_five.inc new file mode 100644 index 000000000..027913665 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_five.inc @@ -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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsFive[] = { +{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Cts, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdBlEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_NfcEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_NfcInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsFive = util::size(PinmuxPadConfigsFive); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_hoag.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_hoag.inc new file mode 100644 index 000000000..d0efa9956 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_hoag.inc @@ -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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsHoag[] = { +{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_TouchClk, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart3Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Cts, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPe7, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Fs, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk7, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Spi4Miso, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Spi4Sck, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000204, 0x00000267 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DvfsClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Mclk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPz5, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_Dap4Din, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Rx, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Rts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Uart2Cts, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPl0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifOut, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_SpdifIn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsHoag = util::size(PinmuxPadConfigsHoag); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_icosa.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_icosa.inc new file mode 100644 index 000000000..e1cee6f72 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_icosa.inc @@ -0,0 +1,183 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsIcosa[] = { +{ PinmuxPadIndex_AudMclk, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x00000267 }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x00000267 }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000048, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_GpioPz5, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000060 }, +{ PinmuxPadIndex_PwrIntN, 0x00000030, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart3Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_QspiIo0, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_QspiIo1, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_QspiSck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_QspiCsN, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_DvfsClk, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, +{ PinmuxPadIndex_Cam1Mclk, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz0, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz2, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Din, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk7, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPl0, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_SpdifOut, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000004, 0x00000267 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_AlsProxInt, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsIcosa = util::size(PinmuxPadConfigsIcosa); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_iowa.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_iowa.inc new file mode 100644 index 000000000..3b0022e97 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_initial_pad_config_iowa.inc @@ -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 . + */ + +/* NOTE: This file is auto-generated by pinmux_initial_config.py, do not edit manually. */ + +constexpr inline const PinmuxPadConfig PinmuxPadConfigsIowa[] = { +{ PinmuxPadIndex_AudMclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap1Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap1Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Gen3I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen3I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL0ClkreqN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_PexL0RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexL1ClkreqN, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PexL1RstN, 0x00000000, 0x0000027F }, +{ PinmuxPadIndex_PexWakeN, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_Sdmmc1Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Cmd, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc1Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Clk, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Cmd, 0x00000040, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat0, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat1, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat2, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat3, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat4, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat5, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat6, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc2Dat7, 0x00000050, 0x0000007F }, +{ PinmuxPadIndex_Shutdown, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_LcdBlPwm, 0x00000001, 0x00000067 }, +{ PinmuxPadIndex_LcdGpio2, 0x00000001, 0x0000007F }, +{ PinmuxPadIndex_PwrI2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_PwrI2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Clk32kIn, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Clk32kOut, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_GpioPz5, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_CorePwrReq, 0x00000000, 0x00000078 }, +{ PinmuxPadIndex_PwrIntN, 0x00000020, 0x00000078 }, +{ PinmuxPadIndex_Gen1I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen1I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cScl, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Gen2I2cSda, 0x00000040, 0x0000027F }, +{ PinmuxPadIndex_Uart2Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart2Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart2Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart1Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rx, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Uart1Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart1Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_JtagRtck, 0x00000000, 0x00000067 }, +{ PinmuxPadIndex_GpioPl1, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Mosi, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Spi4Miso, 0x00000030, 0x0000007F }, +{ PinmuxPadIndex_Spi4Sck, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Spi4Cs0, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rx, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart3Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart3Cts, 0x00000020, 0x0000007F }, +{ PinmuxPadIndex_Uart4Tx, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rx, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Uart4Rts, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Uart4Cts, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Din, 0x00000028, 0x0000007F }, +{ PinmuxPadIndex_Dap2Dout, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Fs, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_Dap2Sclk, 0x00000000, 0x0000007F }, +{ PinmuxPadIndex_HdmiIntDpHpd, 0x00000020, 0x0000027F }, +{ PinmuxPadIndex_DvfsClk, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioX1Aud, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioX3Aud, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Dmic3Clk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPe6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamI2cSda, 0x00000034, 0x0000027F }, +{ PinmuxPadIndex_Cam1Mclk, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Cam2Mclk, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_CamFlashEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Cam1Pwdn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_SataLedActive, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Sdmmc3Clk, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat1, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat2, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_Sdmmc3Dat3, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_AlsProxInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_TempAlert, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_MotionInt, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_TouchRst, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_TouchInt, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonPowerOn, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolUp, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonVolDown, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_ButtonSlideSw, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ButtonHome, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_LcdBlEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_LcdRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_ApReady, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPz0, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPz1, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz2, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_GpioPz3, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPz4, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Din, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Dap4Sclk, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart2Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPk0, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk1, 0x0000004C, 0x0000007F }, +{ PinmuxPadIndex_GpioPk2, 0x00000044, 0x0000007F }, +{ PinmuxPadIndex_GpioPk3, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk4, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPk5, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_GpioPk6, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_GpioPl0, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_Uart3Tx, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_WifiEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_WifiWakeAp, 0x0000002C, 0x0000007F }, +{ PinmuxPadIndex_ApWakeBt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtRst, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_BtWakeAp, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_GpioPh6, 0x00000024, 0x0000007F }, +{ PinmuxPadIndex_ApWakeNfc, 0x00000004, 0x0000007F }, +{ PinmuxPadIndex_NfcEn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_NfcInt, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_SpdifOut, 0x00000034, 0x0000007F }, +{ PinmuxPadIndex_SpdifIn, 0x00000004, 0x00000067 }, +{ PinmuxPadIndex_UsbVbusEn0, 0x00000004, 0x00000267 }, +{ PinmuxPadIndex_DvfsPwm, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi2Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic1Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic1Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic2Clk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic2Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dmic3Dat, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPe7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamI2cScl, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_CamAfEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam2Pwdn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Cam1Strobe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPa6, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Clkb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc2Dqsb, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Sdmmc3Cmd, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_TouchClk, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ModemWakeAp, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdTe, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_LcdGpio1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_ClkReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_CpuPwrReq, 0x00000000, 0x00000000 }, +{ PinmuxPadIndex_Dap4Dout, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Dap4Fs, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPk7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Mosi, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Miso, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Sck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_Spi1Cs1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsEn, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpsRst, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiSck, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiCsN, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo2, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_QspiIo3, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_GpioPcc7, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_UsbVbusEn1, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_DpHpd0, 0x00000005, 0x00000007 }, +{ PinmuxPadIndex_HdmiCec, 0x00000005, 0x00000007 }, +}; + +constexpr inline const size_t NumPinmuxPadConfigsIowa = util::size(PinmuxPadConfigsIowa); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_characters.inc b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_characters.inc new file mode 100644 index 000000000..a84081d92 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_characters.inc @@ -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 . + */ + +/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ + +constexpr inline const PinmuxPadCharacter PinmuxPadCharacters[] = { + { 0x3000, 0x072FF, 0x01, "Sdmmc1Clk" }, + { 0x3004, 0x072FF, 0x02, "Sdmmc1Cmd" }, + { 0x3008, 0x072FF, 0x02, "Sdmmc1Dat3" }, + { 0x300C, 0x072FF, 0x02, "Sdmmc1Dat2" }, + { 0x3010, 0x072FF, 0x02, "Sdmmc1Dat1" }, + { 0x3014, 0x072FF, 0x01, "Sdmmc1Dat0" }, + { 0x301C, 0x072FF, 0x01, "Sdmmc3Clk" }, + { 0x3020, 0x072FF, 0x01, "Sdmmc3Cmd" }, + { 0x3024, 0x072FF, 0x01, "Sdmmc3Dat0" }, + { 0x3028, 0x072FF, 0x01, "Sdmmc3Dat1" }, + { 0x302C, 0x072FF, 0x01, "Sdmmc3Dat2" }, + { 0x3030, 0x072FF, 0x01, "Sdmmc3Dat3" }, + { 0x3038, 0x01DFF, 0x01, "PexL0RstN" }, + { 0x303C, 0x01DFF, 0x01, "PexL0ClkreqN" }, + { 0x3040, 0x01DFF, 0x01, "PexWakeN" }, + { 0x3044, 0x01DFF, 0x01, "PexL1RstN" }, + { 0x3048, 0x01DFF, 0x01, "PexL1ClkreqN" }, + { 0x304C, 0x019FF, 0x01, "SataLedActive" }, + { 0x3050, 0x1F2FF, 0x01, "Spi1Mosi" }, + { 0x3054, 0x1F2FF, 0x01, "Spi1Miso" }, + { 0x3058, 0x1F2FF, 0x01, "Spi1Sck" }, + { 0x305C, 0x1F2FF, 0x01, "Spi1Cs0" }, + { 0x3060, 0x1F2FF, 0x01, "Spi1Cs1" }, + { 0x3064, 0x072FF, 0x02, "Spi2Mosi" }, + { 0x3068, 0x072FF, 0x02, "Spi2Miso" }, + { 0x306C, 0x072FF, 0x02, "Spi2Sck" }, + { 0x3070, 0x072FF, 0x02, "Spi2Cs0" }, + { 0x3074, 0x072FF, 0x01, "Spi2Cs1" }, + { 0x3078, 0x1F2FF, 0x01, "Spi4Mosi" }, + { 0x307C, 0x1F2FF, 0x01, "Spi4Miso" }, + { 0x3080, 0x1F2FF, 0x01, "Spi4Sck" }, + { 0x3084, 0x1F2FF, 0x01, "Spi4Cs0" }, + { 0x3088, 0x072FF, 0x01, "QspiSck" }, + { 0x308C, 0x072FF, 0x01, "QspiCsN" }, + { 0x3090, 0x072FF, 0x01, "QspiIo0" }, + { 0x3094, 0x072FF, 0x01, "QspiIo1" }, + { 0x3098, 0x072FF, 0x01, "QspiIo2" }, + { 0x309C, 0x072FF, 0x01, "QspiIo3" }, + { 0x30A4, 0x019FF, 0x02, "Dmic1Clk" }, + { 0x30A8, 0x019FF, 0x02, "Dmic1Dat" }, + { 0x30AC, 0x019FF, 0x02, "Dmic2Clk" }, + { 0x30B0, 0x019FF, 0x02, "Dmic2Dat" }, + { 0x30B4, 0x019FF, 0x02, "Dmic3Clk" }, + { 0x30B8, 0x019FF, 0x02, "Dmic3Dat" }, + { 0x30BC, 0x01DFF, 0x01, "Gen1I2cScl" }, + { 0x30C0, 0x01DFF, 0x01, "Gen1I2cSda" }, + { 0x30C4, 0x01DFF, 0x01, "Gen2I2cScl" }, + { 0x30C8, 0x01DFF, 0x01, "Gen2I2cSda" }, + { 0x30CC, 0x01DFF, 0x01, "Gen3I2cScl" }, + { 0x30D0, 0x01DFF, 0x01, "Gen3I2cSda" }, + { 0x30D4, 0x01DFF, 0x02, "CamI2cScl" }, + { 0x30D8, 0x01DFF, 0x02, "CamI2cSda" }, + { 0x30DC, 0x01DFF, 0x01, "PwrI2cScl" }, + { 0x30E0, 0x01DFF, 0x01, "PwrI2cSda" }, + { 0x30E4, 0x019FF, 0x01, "Uart1Tx" }, + { 0x30E8, 0x019FF, 0x01, "Uart1Rx" }, + { 0x30EC, 0x019FF, 0x01, "Uart1Rts" }, + { 0x30F0, 0x019FF, 0x01, "Uart1Cts" }, + { 0x30F4, 0x019FF, 0x00, "Uart2Tx" }, + { 0x30F8, 0x019FF, 0x00, "Uart2Rx" }, + { 0x30FC, 0x019FF, 0x02, "Uart2Rts" }, + { 0x3100, 0x019FF, 0x02, "Uart2Cts" }, + { 0x3104, 0x019FF, 0x02, "Uart3Tx" }, + { 0x3108, 0x019FF, 0x02, "Uart3Rx" }, + { 0x310C, 0x019FF, 0x02, "Uart3Rts" }, + { 0x3110, 0x019FF, 0x02, "Uart3Cts" }, + { 0x3114, 0x019FF, 0x02, "Uart4Tx" }, + { 0x3118, 0x019FF, 0x02, "Uart4Rx" }, + { 0x311C, 0x019FF, 0x02, "Uart4Rts" }, + { 0x3120, 0x019FF, 0x02, "Uart4Cts" }, + { 0x3124, 0x072FF, 0x01, "Dap1Fs" }, + { 0x3128, 0x072FF, 0x01, "Dap1Din" }, + { 0x312C, 0x072FF, 0x01, "Dap1Dout" }, + { 0x3130, 0x072FF, 0x01, "Dap1Sclk" }, + { 0x3134, 0x072FF, 0x01, "Dap2Fs" }, + { 0x3138, 0x072FF, 0x01, "Dap2Din" }, + { 0x313C, 0x072FF, 0x01, "Dap2Dout" }, + { 0x3140, 0x072FF, 0x01, "Dap2Sclk" }, + { 0x3144, 0x072FF, 0x01, "Dap4Fs" }, + { 0x3148, 0x072FF, 0x01, "Dap4Din" }, + { 0x314C, 0x072FF, 0x01, "Dap4Dout" }, + { 0x3150, 0x072FF, 0x01, "Dap4Sclk" }, + { 0x3154, 0x072FF, 0x01, "Cam1Mclk" }, + { 0x3158, 0x072FF, 0x01, "Cam2Mclk" }, + { 0x315C, 0x072FF, 0x01, "JtagRtck" }, + { 0x3160, 0x0118C, 0xFF, "Clk32kIn" }, + { 0x3164, 0x072FF, 0x02, "Clk32kOut" }, + { 0x3168, 0x01DFF, 0x01, "BattBcl" }, + { 0x316C, 0x011CC, 0xFF, "ClkReq" }, + { 0x3170, 0x011CC, 0xFF, "CpuPwrReq" }, + { 0x3174, 0x011CC, 0xFF, "PwrIntN" }, + { 0x3178, 0x011CC, 0xFF, "Shutdown" }, + { 0x317C, 0x011CC, 0xFF, "CorePwrReq" }, + { 0x3180, 0x019FF, 0x01, "AudMclk" }, + { 0x3184, 0x019FF, 0x00, "DvfsPwm" }, + { 0x3188, 0x019FF, 0x00, "DvfsClk" }, + { 0x318C, 0x019FF, 0x00, "GpioX1Aud" }, + { 0x3190, 0x019FF, 0x00, "GpioX3Aud" }, + { 0x3194, 0x01DFF, 0x00, "GpioPcc7" }, + { 0x3198, 0x01DFF, 0x01, "HdmiCec" }, + { 0x319C, 0x01DFF, 0x01, "HdmiIntDpHpd" }, + { 0x31A0, 0x019FF, 0x01, "SpdifOut" }, + { 0x31A4, 0x019FF, 0x01, "SpdifIn" }, + { 0x31A8, 0x01DFF, 0x01, "UsbVbusEn0" }, + { 0x31AC, 0x01DFF, 0x01, "UsbVbusEn1" }, + { 0x31B0, 0x019FF, 0x01, "DpHpd0" }, + { 0x31B4, 0x019FF, 0x00, "WifiEn" }, + { 0x31B8, 0x019FF, 0x00, "WifiRst" }, + { 0x31BC, 0x019FF, 0x00, "WifiWakeAp" }, + { 0x31C0, 0x019FF, 0x00, "ApWakeBt" }, + { 0x31C4, 0x019FF, 0x00, "BtRst" }, + { 0x31C8, 0x019FF, 0x00, "BtWakeAp" }, + { 0x31CC, 0x019FF, 0x00, "ApWakeNfc" }, + { 0x31D0, 0x019FF, 0x00, "NfcEn" }, + { 0x31D4, 0x019FF, 0x00, "NfcInt" }, + { 0x31D8, 0x019FF, 0x00, "GpsEn" }, + { 0x31DC, 0x019FF, 0x00, "GpsRst" }, + { 0x31E0, 0x019FF, 0x01, "CamRst" }, + { 0x31E4, 0x019FF, 0x02, "CamAfEn" }, + { 0x31E8, 0x019FF, 0x02, "CamFlashEn" }, + { 0x31EC, 0x019FF, 0x01, "Cam1Pwdn" }, + { 0x31F0, 0x019FF, 0x01, "Cam2Pwdn" }, + { 0x31F4, 0x019FF, 0x01, "Cam1Strobe" }, + { 0x31F8, 0x019FF, 0x01, "LcdTe" }, + { 0x31FC, 0x019FF, 0x03, "LcdBlPwm" }, + { 0x3200, 0x019FF, 0x00, "LcdBlEn" }, + { 0x3204, 0x019FF, 0x00, "LcdRst" }, + { 0x3208, 0x019FF, 0x01, "LcdGpio1" }, + { 0x320C, 0x019FF, 0x02, "LcdGpio2" }, + { 0x3210, 0x019FF, 0x00, "ApReady" }, + { 0x3214, 0x019FF, 0x00, "TouchRst" }, + { 0x3218, 0x019FF, 0x01, "TouchClk" }, + { 0x321C, 0x019FF, 0x00, "ModemWakeAp" }, + { 0x3220, 0x019FF, 0x00, "TouchInt" }, + { 0x3224, 0x019FF, 0x00, "MotionInt" }, + { 0x3228, 0x019FF, 0x00, "AlsProxInt" }, + { 0x322C, 0x019FF, 0x00, "TempAlert" }, + { 0x3230, 0x019FF, 0x00, "ButtonPowerOn" }, + { 0x3234, 0x019FF, 0x00, "ButtonVolUp" }, + { 0x3238, 0x019FF, 0x00, "ButtonVolDown" }, + { 0x323C, 0x019FF, 0x00, "ButtonSlideSw" }, + { 0x3240, 0x019FF, 0x00, "ButtonHome" }, + { 0x3244, 0x019FF, 0x01, "GpioPa6" }, + { 0x3248, 0x019FF, 0x00, "GpioPe6" }, + { 0x324C, 0x019FF, 0x00, "GpioPe7" }, + { 0x3250, 0x019FF, 0x00, "GpioPh6" }, + { 0x3254, 0x072FF, 0x02, "GpioPk0" }, + { 0x3258, 0x072FF, 0x02, "GpioPk1" }, + { 0x325C, 0x072FF, 0x02, "GpioPk2" }, + { 0x3260, 0x072FF, 0x02, "GpioPk3" }, + { 0x3264, 0x072FF, 0x01, "GpioPk4" }, + { 0x3268, 0x072FF, 0x01, "GpioPk5" }, + { 0x326C, 0x072FF, 0x01, "GpioPk6" }, + { 0x3270, 0x072FF, 0x01, "GpioPk7" }, + { 0x3274, 0x072FF, 0x00, "GpioPl0" }, + { 0x3278, 0x072FF, 0x01, "GpioPl1" }, + { 0x327C, 0x072FF, 0x01, "GpioPz0" }, + { 0x3280, 0x072FF, 0x02, "GpioPz1" }, + { 0x3284, 0x072FF, 0x02, "GpioPz2" }, + { 0x3288, 0x072FF, 0x01, "GpioPz3" }, + { 0x328C, 0x072FF, 0x01, "GpioPz4" }, + { 0x3290, 0x072FF, 0x01, "GpioPz5" }, + { 0x3294, 0x1F2FF, 0x02, "Sdmmc2Dat0" }, + { 0x3298, 0x1F2FF, 0x02, "Sdmmc2Dat1" }, + { 0x329C, 0x1F2FF, 0x02, "Sdmmc2Dat2" }, + { 0x32A0, 0x1F2FF, 0x02, "Sdmmc2Dat3" }, + { 0x32A4, 0x1F2FF, 0x02, "Sdmmc2Dat4" }, + { 0x32A8, 0x1F2FF, 0x02, "Sdmmc2Dat5" }, + { 0x32AC, 0x1F2FF, 0x02, "Sdmmc2Dat6" }, + { 0x32B0, 0x1F2FF, 0x02, "Sdmmc2Dat7" }, + { 0x32B4, 0x1F2FF, 0x02, "Sdmmc2Clk" }, + { 0x32B8, 0x1F2FF, 0x00, "Sdmmc2Clkb" }, + { 0x32BC, 0x1F2FF, 0x02, "Sdmmc2Cmd" }, + { 0x32C0, 0x1F2FF, 0x00, "Sdmmc2Dqs" }, + { 0x32C4, 0x1F2FF, 0x00, "Sdmmc2Dqsb" }, +}; + +constexpr inline size_t NumPinmuxPadCharacters = util::size(PinmuxPadCharacters); diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_index.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_index.hpp new file mode 100644 index 000000000..df2dc592b --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_pad_index.hpp @@ -0,0 +1,348 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by pinmux_character_gen.py, do not edit manually. */ + +#pragma once + +enum PinmuxPadIndex { + PinmuxPadIndex_Sdmmc1Clk = 0, + PinmuxPadIndex_Sdmmc1Cmd = 1, + PinmuxPadIndex_Sdmmc1Dat3 = 2, + PinmuxPadIndex_Sdmmc1Dat2 = 3, + PinmuxPadIndex_Sdmmc1Dat1 = 4, + PinmuxPadIndex_Sdmmc1Dat0 = 5, + PinmuxPadIndex_Sdmmc3Clk = 6, + PinmuxPadIndex_Sdmmc3Cmd = 7, + PinmuxPadIndex_Sdmmc3Dat0 = 8, + PinmuxPadIndex_Sdmmc3Dat1 = 9, + PinmuxPadIndex_Sdmmc3Dat2 = 10, + PinmuxPadIndex_Sdmmc3Dat3 = 11, + PinmuxPadIndex_PexL0RstN = 12, + PinmuxPadIndex_PexL0ClkreqN = 13, + PinmuxPadIndex_PexWakeN = 14, + PinmuxPadIndex_PexL1RstN = 15, + PinmuxPadIndex_PexL1ClkreqN = 16, + PinmuxPadIndex_SataLedActive = 17, + PinmuxPadIndex_Spi1Mosi = 18, + PinmuxPadIndex_Spi1Miso = 19, + PinmuxPadIndex_Spi1Sck = 20, + PinmuxPadIndex_Spi1Cs0 = 21, + PinmuxPadIndex_Spi1Cs1 = 22, + PinmuxPadIndex_Spi2Mosi = 23, + PinmuxPadIndex_Spi2Miso = 24, + PinmuxPadIndex_Spi2Sck = 25, + PinmuxPadIndex_Spi2Cs0 = 26, + PinmuxPadIndex_Spi2Cs1 = 27, + PinmuxPadIndex_Spi4Mosi = 28, + PinmuxPadIndex_Spi4Miso = 29, + PinmuxPadIndex_Spi4Sck = 30, + PinmuxPadIndex_Spi4Cs0 = 31, + PinmuxPadIndex_QspiSck = 32, + PinmuxPadIndex_QspiCsN = 33, + PinmuxPadIndex_QspiIo0 = 34, + PinmuxPadIndex_QspiIo1 = 35, + PinmuxPadIndex_QspiIo2 = 36, + PinmuxPadIndex_QspiIo3 = 37, + PinmuxPadIndex_Dmic1Clk = 38, + PinmuxPadIndex_Dmic1Dat = 39, + PinmuxPadIndex_Dmic2Clk = 40, + PinmuxPadIndex_Dmic2Dat = 41, + PinmuxPadIndex_Dmic3Clk = 42, + PinmuxPadIndex_Dmic3Dat = 43, + PinmuxPadIndex_Gen1I2cScl = 44, + PinmuxPadIndex_Gen1I2cSda = 45, + PinmuxPadIndex_Gen2I2cScl = 46, + PinmuxPadIndex_Gen2I2cSda = 47, + PinmuxPadIndex_Gen3I2cScl = 48, + PinmuxPadIndex_Gen3I2cSda = 49, + PinmuxPadIndex_CamI2cScl = 50, + PinmuxPadIndex_CamI2cSda = 51, + PinmuxPadIndex_PwrI2cScl = 52, + PinmuxPadIndex_PwrI2cSda = 53, + PinmuxPadIndex_Uart1Tx = 54, + PinmuxPadIndex_Uart1Rx = 55, + PinmuxPadIndex_Uart1Rts = 56, + PinmuxPadIndex_Uart1Cts = 57, + PinmuxPadIndex_Uart2Tx = 58, + PinmuxPadIndex_Uart2Rx = 59, + PinmuxPadIndex_Uart2Rts = 60, + PinmuxPadIndex_Uart2Cts = 61, + PinmuxPadIndex_Uart3Tx = 62, + PinmuxPadIndex_Uart3Rx = 63, + PinmuxPadIndex_Uart3Rts = 64, + PinmuxPadIndex_Uart3Cts = 65, + PinmuxPadIndex_Uart4Tx = 66, + PinmuxPadIndex_Uart4Rx = 67, + PinmuxPadIndex_Uart4Rts = 68, + PinmuxPadIndex_Uart4Cts = 69, + PinmuxPadIndex_Dap1Fs = 70, + PinmuxPadIndex_Dap1Din = 71, + PinmuxPadIndex_Dap1Dout = 72, + PinmuxPadIndex_Dap1Sclk = 73, + PinmuxPadIndex_Dap2Fs = 74, + PinmuxPadIndex_Dap2Din = 75, + PinmuxPadIndex_Dap2Dout = 76, + PinmuxPadIndex_Dap2Sclk = 77, + PinmuxPadIndex_Dap4Fs = 78, + PinmuxPadIndex_Dap4Din = 79, + PinmuxPadIndex_Dap4Dout = 80, + PinmuxPadIndex_Dap4Sclk = 81, + PinmuxPadIndex_Cam1Mclk = 82, + PinmuxPadIndex_Cam2Mclk = 83, + PinmuxPadIndex_JtagRtck = 84, + PinmuxPadIndex_Clk32kIn = 85, + PinmuxPadIndex_Clk32kOut = 86, + PinmuxPadIndex_BattBcl = 87, + PinmuxPadIndex_ClkReq = 88, + PinmuxPadIndex_CpuPwrReq = 89, + PinmuxPadIndex_PwrIntN = 90, + PinmuxPadIndex_Shutdown = 91, + PinmuxPadIndex_CorePwrReq = 92, + PinmuxPadIndex_AudMclk = 93, + PinmuxPadIndex_DvfsPwm = 94, + PinmuxPadIndex_DvfsClk = 95, + PinmuxPadIndex_GpioX1Aud = 96, + PinmuxPadIndex_GpioX3Aud = 97, + PinmuxPadIndex_GpioPcc7 = 98, + PinmuxPadIndex_HdmiCec = 99, + PinmuxPadIndex_HdmiIntDpHpd = 100, + PinmuxPadIndex_SpdifOut = 101, + PinmuxPadIndex_SpdifIn = 102, + PinmuxPadIndex_UsbVbusEn0 = 103, + PinmuxPadIndex_UsbVbusEn1 = 104, + PinmuxPadIndex_DpHpd0 = 105, + PinmuxPadIndex_WifiEn = 106, + PinmuxPadIndex_WifiRst = 107, + PinmuxPadIndex_WifiWakeAp = 108, + PinmuxPadIndex_ApWakeBt = 109, + PinmuxPadIndex_BtRst = 110, + PinmuxPadIndex_BtWakeAp = 111, + PinmuxPadIndex_ApWakeNfc = 112, + PinmuxPadIndex_NfcEn = 113, + PinmuxPadIndex_NfcInt = 114, + PinmuxPadIndex_GpsEn = 115, + PinmuxPadIndex_GpsRst = 116, + PinmuxPadIndex_CamRst = 117, + PinmuxPadIndex_CamAfEn = 118, + PinmuxPadIndex_CamFlashEn = 119, + PinmuxPadIndex_Cam1Pwdn = 120, + PinmuxPadIndex_Cam2Pwdn = 121, + PinmuxPadIndex_Cam1Strobe = 122, + PinmuxPadIndex_LcdTe = 123, + PinmuxPadIndex_LcdBlPwm = 124, + PinmuxPadIndex_LcdBlEn = 125, + PinmuxPadIndex_LcdRst = 126, + PinmuxPadIndex_LcdGpio1 = 127, + PinmuxPadIndex_LcdGpio2 = 128, + PinmuxPadIndex_ApReady = 129, + PinmuxPadIndex_TouchRst = 130, + PinmuxPadIndex_TouchClk = 131, + PinmuxPadIndex_ModemWakeAp = 132, + PinmuxPadIndex_TouchInt = 133, + PinmuxPadIndex_MotionInt = 134, + PinmuxPadIndex_AlsProxInt = 135, + PinmuxPadIndex_TempAlert = 136, + PinmuxPadIndex_ButtonPowerOn = 137, + PinmuxPadIndex_ButtonVolUp = 138, + PinmuxPadIndex_ButtonVolDown = 139, + PinmuxPadIndex_ButtonSlideSw = 140, + PinmuxPadIndex_ButtonHome = 141, + PinmuxPadIndex_GpioPa6 = 142, + PinmuxPadIndex_GpioPe6 = 143, + PinmuxPadIndex_GpioPe7 = 144, + PinmuxPadIndex_GpioPh6 = 145, + PinmuxPadIndex_GpioPk0 = 146, + PinmuxPadIndex_GpioPk1 = 147, + PinmuxPadIndex_GpioPk2 = 148, + PinmuxPadIndex_GpioPk3 = 149, + PinmuxPadIndex_GpioPk4 = 150, + PinmuxPadIndex_GpioPk5 = 151, + PinmuxPadIndex_GpioPk6 = 152, + PinmuxPadIndex_GpioPk7 = 153, + PinmuxPadIndex_GpioPl0 = 154, + PinmuxPadIndex_GpioPl1 = 155, + PinmuxPadIndex_GpioPz0 = 156, + PinmuxPadIndex_GpioPz1 = 157, + PinmuxPadIndex_GpioPz2 = 158, + PinmuxPadIndex_GpioPz3 = 159, + PinmuxPadIndex_GpioPz4 = 160, + PinmuxPadIndex_GpioPz5 = 161, + PinmuxPadIndex_Sdmmc2Dat0 = 162, + PinmuxPadIndex_Sdmmc2Dat1 = 163, + PinmuxPadIndex_Sdmmc2Dat2 = 164, + PinmuxPadIndex_Sdmmc2Dat3 = 165, + PinmuxPadIndex_Sdmmc2Dat4 = 166, + PinmuxPadIndex_Sdmmc2Dat5 = 167, + PinmuxPadIndex_Sdmmc2Dat6 = 168, + PinmuxPadIndex_Sdmmc2Dat7 = 169, + PinmuxPadIndex_Sdmmc2Clk = 170, + PinmuxPadIndex_Sdmmc2Clkb = 171, + PinmuxPadIndex_Sdmmc2Cmd = 172, + PinmuxPadIndex_Sdmmc2Dqs = 173, + PinmuxPadIndex_Sdmmc2Dqsb = 174, +}; + +enum PinmuxDrivePadIndex { + PinmuxDrivePadIndex_AlsProxInt = 0, + PinmuxDrivePadIndex_ApReady = 1, + PinmuxDrivePadIndex_ApWakeBt = 2, + PinmuxDrivePadIndex_ApWakeNfc = 3, + PinmuxDrivePadIndex_AudMclk = 4, + PinmuxDrivePadIndex_BattBcl = 5, + PinmuxDrivePadIndex_BtRst = 6, + PinmuxDrivePadIndex_BtWakeAp = 7, + PinmuxDrivePadIndex_ButtonHome = 8, + PinmuxDrivePadIndex_ButtonPowerOn = 9, + PinmuxDrivePadIndex_ButtonSlideSw = 10, + PinmuxDrivePadIndex_ButtonVolDown = 11, + PinmuxDrivePadIndex_ButtonVolUp = 12, + PinmuxDrivePadIndex_Cam1Mclk = 13, + PinmuxDrivePadIndex_Cam1Pwdn = 14, + PinmuxDrivePadIndex_Cam1Strobe = 15, + PinmuxDrivePadIndex_Cam2Mclk = 16, + PinmuxDrivePadIndex_Cam2Pwdn = 17, + PinmuxDrivePadIndex_CamAfEn = 18, + PinmuxDrivePadIndex_CamFlashEn = 19, + PinmuxDrivePadIndex_CamI2cScl = 20, + PinmuxDrivePadIndex_CamI2cSda = 21, + PinmuxDrivePadIndex_CamRst = 22, + PinmuxDrivePadIndex_Clk32kIn = 23, + PinmuxDrivePadIndex_Clk32kOut = 24, + PinmuxDrivePadIndex_ClkReq = 25, + PinmuxDrivePadIndex_CorePwrReq = 26, + PinmuxDrivePadIndex_CpuPwrReq = 27, + PinmuxDrivePadIndex_Dap1Din = 28, + PinmuxDrivePadIndex_Dap1Dout = 29, + PinmuxDrivePadIndex_Dap1Fs = 30, + PinmuxDrivePadIndex_Dap1Sclk = 31, + PinmuxDrivePadIndex_Dap2Din = 32, + PinmuxDrivePadIndex_Dap2Dout = 33, + PinmuxDrivePadIndex_Dap2Fs = 34, + PinmuxDrivePadIndex_Dap2Sclk = 35, + PinmuxDrivePadIndex_Dap4Din = 36, + PinmuxDrivePadIndex_Dap4Dout = 37, + PinmuxDrivePadIndex_Dap4Fs = 38, + PinmuxDrivePadIndex_Dap4Sclk = 39, + PinmuxDrivePadIndex_Dmic1Clk = 40, + PinmuxDrivePadIndex_Dmic1Dat = 41, + PinmuxDrivePadIndex_Dmic2Clk = 42, + PinmuxDrivePadIndex_Dmic2Dat = 43, + PinmuxDrivePadIndex_Dmic3Clk = 44, + PinmuxDrivePadIndex_Dmic3Dat = 45, + PinmuxDrivePadIndex_DpHpd = 46, + PinmuxDrivePadIndex_DvfsClk = 47, + PinmuxDrivePadIndex_DvfsPwm = 48, + PinmuxDrivePadIndex_Gen1I2cScl = 49, + PinmuxDrivePadIndex_Gen1I2cSda = 50, + PinmuxDrivePadIndex_Gen2I2cScl = 51, + PinmuxDrivePadIndex_Gen2I2cSda = 52, + PinmuxDrivePadIndex_Gen3I2cScl = 53, + PinmuxDrivePadIndex_Gen3I2cSda = 54, + PinmuxDrivePadIndex_GpioPa6 = 55, + PinmuxDrivePadIndex_GpioPcc7 = 56, + PinmuxDrivePadIndex_GpioPe6 = 57, + PinmuxDrivePadIndex_GpioPe7 = 58, + PinmuxDrivePadIndex_GpioPh6 = 59, + PinmuxDrivePadIndex_GpioPk0 = 60, + PinmuxDrivePadIndex_GpioPk1 = 61, + PinmuxDrivePadIndex_GpioPk2 = 62, + PinmuxDrivePadIndex_GpioPk3 = 63, + PinmuxDrivePadIndex_GpioPk4 = 64, + PinmuxDrivePadIndex_GpioPk5 = 65, + PinmuxDrivePadIndex_GpioPk6 = 66, + PinmuxDrivePadIndex_GpioPk7 = 67, + PinmuxDrivePadIndex_GpioPl0 = 68, + PinmuxDrivePadIndex_GpioPl1 = 69, + PinmuxDrivePadIndex_GpioPz0 = 70, + PinmuxDrivePadIndex_GpioPz1 = 71, + PinmuxDrivePadIndex_GpioPz2 = 72, + PinmuxDrivePadIndex_GpioPz3 = 73, + PinmuxDrivePadIndex_GpioPz4 = 74, + PinmuxDrivePadIndex_GpioPz5 = 75, + PinmuxDrivePadIndex_GpioX1Aud = 76, + PinmuxDrivePadIndex_GpioX3Aud = 77, + PinmuxDrivePadIndex_GpsEn = 78, + PinmuxDrivePadIndex_GpsRst = 79, + PinmuxDrivePadIndex_HdmiCec = 80, + PinmuxDrivePadIndex_HdmiIntDpHpd = 81, + PinmuxDrivePadIndex_JtagRtck = 82, + PinmuxDrivePadIndex_LcdBlEn = 83, + PinmuxDrivePadIndex_LcdBlPwm = 84, + PinmuxDrivePadIndex_LcdGpio1 = 85, + PinmuxDrivePadIndex_LcdGpio2 = 86, + PinmuxDrivePadIndex_LcdRst = 87, + PinmuxDrivePadIndex_LcdTe = 88, + PinmuxDrivePadIndex_ModemWakeAp = 89, + PinmuxDrivePadIndex_MotionInt = 90, + PinmuxDrivePadIndex_NfcEn = 91, + PinmuxDrivePadIndex_NfcInt = 92, + PinmuxDrivePadIndex_PexL0ClkReqN = 93, + PinmuxDrivePadIndex_PexL0RstN = 94, + PinmuxDrivePadIndex_PexL1ClkreqN = 95, + PinmuxDrivePadIndex_PexL1RstN = 96, + PinmuxDrivePadIndex_PexWakeN = 97, + PinmuxDrivePadIndex_PwrI2cScl = 98, + PinmuxDrivePadIndex_PwrI2cSda = 99, + PinmuxDrivePadIndex_PwrIntN = 100, + PinmuxDrivePadIndex_QspiComp = 101, + PinmuxDrivePadIndex_QspiSck = 102, + PinmuxDrivePadIndex_SataLedActive = 103, + PinmuxDrivePadIndex_Sdmmc1Pad = 104, + PinmuxDrivePadIndex_Sdmmc3Pad = 105, + PinmuxDrivePadIndex_Shutdown = 106, + PinmuxDrivePadIndex_SpdifIn = 107, + PinmuxDrivePadIndex_SpdifOut = 108, + PinmuxDrivePadIndex_Spi1Cs0 = 109, + PinmuxDrivePadIndex_Spi1Cs1 = 110, + PinmuxDrivePadIndex_Spi1Miso = 111, + PinmuxDrivePadIndex_Spi1Mosi = 112, + PinmuxDrivePadIndex_Spi1Sck = 113, + PinmuxDrivePadIndex_Spi2Cs0 = 114, + PinmuxDrivePadIndex_Spi2Cs1 = 115, + PinmuxDrivePadIndex_Spi2Miso = 116, + PinmuxDrivePadIndex_Spi2Mosi = 117, + PinmuxDrivePadIndex_Spi2Sck = 118, + PinmuxDrivePadIndex_Spi4Cs0 = 119, + PinmuxDrivePadIndex_Spi4Miso = 120, + PinmuxDrivePadIndex_Spi4Mosi = 121, + PinmuxDrivePadIndex_Spi4Sck = 122, + PinmuxDrivePadIndex_TempAlert = 123, + PinmuxDrivePadIndex_TouchClk = 124, + PinmuxDrivePadIndex_TouchInt = 125, + PinmuxDrivePadIndex_TouchRst = 126, + PinmuxDrivePadIndex_Uart1Cts = 127, + PinmuxDrivePadIndex_Uart1Rts = 128, + PinmuxDrivePadIndex_Uart1Rx = 129, + PinmuxDrivePadIndex_Uart1Tx = 130, + PinmuxDrivePadIndex_Uart2Cts = 131, + PinmuxDrivePadIndex_Uart2Rts = 132, + PinmuxDrivePadIndex_Uart2Rx = 133, + PinmuxDrivePadIndex_Uart2Tx = 134, + PinmuxDrivePadIndex_Uart3Cts = 135, + PinmuxDrivePadIndex_Uart3Rts = 136, + PinmuxDrivePadIndex_Uart3Rx = 137, + PinmuxDrivePadIndex_Uart3Tx = 138, + PinmuxDrivePadIndex_Uart4Cts = 139, + PinmuxDrivePadIndex_Uart4Rts = 140, + PinmuxDrivePadIndex_Uart4Rx = 141, + PinmuxDrivePadIndex_Uart4Tx = 142, + PinmuxDrivePadIndex_UsbVbusEn0 = 143, + PinmuxDrivePadIndex_UsbVbusEn1 = 144, + PinmuxDrivePadIndex_WifiEn = 145, + PinmuxDrivePadIndex_WifiRst = 146, + PinmuxDrivePadIndex_WifiWakeAp = 147, +}; diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.cpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.cpp new file mode 100644 index 000000000..7efce9dc7 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.cpp @@ -0,0 +1,641 @@ +/* + * 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 . + */ +#include +#include "pinmux_pad_index.hpp" +#include "pinmux_board_driver_api.hpp" +#include "pinmux_platform_pads.hpp" + +namespace ams::pinmux::driver::board::nintendo_nx { + + namespace { + + uintptr_t g_apb_misc_virtual_address = dd::QueryIoMapping(0x70000000, 0x4000); + + enum PinmuxPadMask : u32 { + PinmuxPadMask_Pm = 0x3, + PinmuxPadMask_Pupd = 0xC, + PinmuxPadMask_Tristate = 0x10, + PinmuxPadMask_Park = 0x20, + PinmuxPadMask_EInput = 0x40, + PinmuxPadMask_Lock = 0x80, + PinmuxPadMask_ELpdr = 0x100, + PinmuxPadMask_EHsm = 0x200, + PinmuxPadMask_EIoHv = 0x400, + PinmuxPadMask_EOd = 0x800, + PinmuxPadMask_ESchmt = 0x1000, + PinmuxPadMask_DrvType = 0x6000, + PinmuxPadMask_Preemp = 0x8000, + PinmuxPadMask_IoReset = 0x10000, + }; + + enum PinmuxPadBitOffset : u32 { + PinmuxPadBitOffset_Pm = 0x0, + PinmuxPadBitOffset_Pupd = 0x2, + PinmuxPadBitOffset_Tristate = 0x4, + PinmuxPadBitOffset_Park = 0x5, + PinmuxPadBitOffset_EInput = 0x6, + PinmuxPadBitOffset_Lock = 0x7, + PinmuxPadBitOffset_ELpdr = 0x8, + PinmuxPadBitOffset_EHsm = 0x9, + PinmuxPadBitOffset_EIoHv = 0xA, + PinmuxPadBitOffset_EOd = 0xB, + PinmuxPadBitOffset_ESchmt = 0xC, + PinmuxPadBitOffset_DrvType = 0xD, + PinmuxPadBitOffset_Preemp = 0xF, + PinmuxPadBitOffset_IoReset = 0x10, + }; + + enum PinmuxOptBitMask : u32 { + PinmuxOptBitMask_Pm = 0x7, + PinmuxOptBitMask_Pupd = 0x18, + PinmuxOptBitMask_Dir = 0x60, + PinmuxOptBitMask_Lock = 0x80, + PinmuxOptBitMask_IoReset = 0x100, + PinmuxOptBitMask_IoHv = 0x200, + PinmuxOptBitMask_Park = 0x400, + PinmuxOptBitMask_Lpdr = 0x800, + PinmuxOptBitMask_Hsm = 0x1000, + PinmuxOptBitMask_Schmt = 0x2000, + PinmuxOptBitMask_DrvType = 0xC000, + PinmuxOptBitMask_Preemp = 0x10000, + }; + + enum PinmuxOptBitOffset { + PinmuxOptBitOffset_Pm = 0x0, + PinmuxOptBitOffset_Pupd = 0x3, + PinmuxOptBitOffset_Dir = 0x5, + PinmuxOptBitOffset_Lock = 0x7, + PinmuxOptBitOffset_IoReset = 0x8, + PinmuxOptBitOffset_IoHv = 0x9, + PinmuxOptBitOffset_Park = 0xA, + PinmuxOptBitOffset_Lpdr = 0xB, + PinmuxOptBitOffset_Hsm = 0xC, + PinmuxOptBitOffset_Schmt = 0xD, + PinmuxOptBitOffset_DrvType = 0xE, + PinmuxOptBitOffset_Preemp = 0x10, + }; + + enum PinmuxDrivePadMask : u32{ + PinmuxDrivePadMask_DrvDn = 0x0001F000, + PinmuxDrivePadMask_DrvUp = 0x01F00000, + PinmuxDrivePadMask_CzDrvDn = 0x0007F000, + PinmuxDrivePadMask_CzDrvUp = 0x07F00000, + PinmuxDrivePadMask_SlwR = 0x30000000, + PinmuxDrivePadMask_SlwF = 0xC0000000, + }; + + enum PinmuxDrivePadBitOffset : u32 { + PinmuxDrivePadBitOffset_DrvDn = 12, + PinmuxDrivePadBitOffset_DrvUp = 20, + PinmuxDrivePadBitOffset_CzDrvDn = 12, + PinmuxDrivePadBitOffset_CzDrvUp = 20, + PinmuxDrivePadBitOffset_SlwR = 28, + PinmuxDrivePadBitOffset_SlwF = 30, + }; + + enum PinmuxDriveOptBitMask : u32 { + PinmuxDriveOptBitMask_DrvDn = 0x0001F000, + PinmuxDriveOptBitMask_DrvUp = 0x01F00000, + PinmuxDriveOptBitMask_CzDrvDn = 0x0007F000, + PinmuxDriveOptBitMask_CzDrvUp = 0x07F00000, + PinmuxDriveOptBitMask_SlwR = 0x30000000, + PinmuxDriveOptBitMask_SlwF = 0xC0000000, + }; + + enum PinmuxDriveOptBitOffset : u32 { + PinmuxDriveOptBitOffset_DrvDn = 12, + PinmuxDriveOptBitOffset_DrvUp = 20, + PinmuxDriveOptBitOffset_CzDrvDn = 12, + PinmuxDriveOptBitOffset_CzDrvUp = 20, + PinmuxDriveOptBitOffset_SlwR = 28, + PinmuxDriveOptBitOffset_SlwF = 30, + }; + + enum PinmuxOpt : u32 { + /* Pm */ + PinmuxOpt_Gpio = 0x4, + PinmuxOpt_Unused = 0x5, + + /* Pupd */ + PinmuxOpt_NoPupd = 0x0, + PinmuxOpt_PullDown = 0x8, + PinmuxOpt_PullUp = 0x10, + + /* Dir */ + PinmuxOpt_Output = 0x0, + PinmuxOpt_Input = 0x20, + PinmuxOpt_Bidirection = 0x40, + PinmuxOpt_OpenDrain = 0x60, + + /* Lock */ + PinmuxOpt_Unlock = 0x0, + PinmuxOpt_Lock = 0x80, + + /* IoReset */ + PinmuxOpt_DisableIoReset = 0x0, + PinmuxOpt_EnableIoReset = 0x100, + + /* IoHv */ + PinmuxOpt_NormalVoltage = 0x0, + PinmuxOpt_HighVoltage = 0x200, + + /* Park */ + PinmuxOpt_ResetOnLowPower = 0x0, + PinmuxOpt_ParkOnLowPower = 0x400, + + /* Lpdr */ + PinmuxOpt_DisableBaseDriver = 0x0, + PinmuxOpt_EnableBaseDriver = 0x800, + + /* Hsm */ + PinmuxOpt_DisableHighSpeedMode = 0x0, + PinmuxOpt_EnableHighSpeedMode = 0x1000, + + /* Schmt */ + PinmuxOpt_CmosMode = 0x0, + PinmuxOpt_SchmittTrigger = 0x2000, + + /* DrvType */ + PinmuxOpt_DrvType1X = 0x0, + PinmuxOpt_DrvType2X = 0x4000, + PinmuxOpt_DrvType3X = 0x8000, + PinmuxOpt_DrvType4X = 0xC000, + + /* Preemp */ + PinmuxOpt_DisablePreemp = 0x0, + PinmuxOpt_EnablePreemp = 0x10000, + }; + + enum PinmuxPadPm : u32 { + PinmuxPadPm_Default = 0xFFFFFFFF, + PinmuxPadPm_Pm0 = 0x0, + PinmuxPadPm_Pm1 = 0x1, + PinmuxPadPm_Pm2 = 0x2, + PinmuxPadPm_Pm3 = 0x3, + PinmuxPadPm_Safe = 0x4, + }; + + struct PinmuxPadCharacter { + u32 reg_offset; + u32 reg_mask; + u8 safe_func; + const char *pad_name; + }; + + struct PinmuxDrivePadCharacter { + u32 reg_offset; + u32 reg_mask; + const char *pad_name; + }; + + #include "pinmux_pad_characters.inc" + #include "pinmux_drive_pad_characters.inc" + + class PinmuxPad { + private: + u32 reg_address; + u32 reg_mask; + u32 reg_value; + u8 safe_func; + const char *pad_name; + private: + bool IsValidRegisterAddress() const { + return this->reg_address - 0x70003000 <= 0x2C4; + } + + uintptr_t GetRegisterAddress() const { + return g_apb_misc_virtual_address + (this->reg_address - 0x70000000); + } + + bool UpdateBits(u32 value, u32 offset, u32 mask) { + if ((this->reg_mask & mask) != 0) { + if ((value & (mask >> offset)) != ((this->reg_value & mask) >> offset)) { + this->reg_value = (this->reg_value & ~mask) | ((value << offset) & mask); + } + return true; + } else { + return false; + } + } + + u32 ReadReg() const { + if (this->IsValidRegisterAddress()) { + return reg::Read(this->GetRegisterAddress()); + } else { + return 0; + } + } + + void WriteReg() const { + if (this->IsValidRegisterAddress()) { + reg::Write(this->GetRegisterAddress(), this->reg_value); + } + } + + bool IsLocked() const { + return (this->reg_value & PinmuxPadMask_Lock) != 0; + } + + void UpdatePm(u8 v) { + if (v != 0xFF) { + if (v == PinmuxPadPm_Safe) { + v = this->safe_func; + } + this->UpdateBits(v, PinmuxPadBitOffset_Pm, PinmuxPadMask_Pm); + } + } + + void UpdatePupd(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Pupd, PinmuxPadMask_Pupd); + } + } + + void UpdateTristate(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Tristate, PinmuxPadMask_Tristate); + } + } + + void UpdateEInput(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_EInput, PinmuxPadMask_EInput); + } + } + + void UpdateEOd(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_EOd, PinmuxPadMask_EOd); + } + } + + void UpdateLock(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Lock, PinmuxPadMask_Lock); + } + } + + void UpdateIoReset(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_IoReset, PinmuxPadMask_IoReset); + } + } + + void UpdatePark(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Park, PinmuxPadMask_Park); + } + } + + void UpdateELpdr(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_ELpdr, PinmuxPadMask_ELpdr); + } + } + + void UpdateEHsm(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_EHsm, PinmuxPadMask_EHsm); + } + } + + void UpdateEIoHv(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_EIoHv, PinmuxPadMask_EIoHv); + } + } + + void UpdateESchmt(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_ESchmt, PinmuxPadMask_ESchmt); + } + } + + void UpdatePreemp(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_Preemp, PinmuxPadMask_Preemp); + } + } + + void UpdateDrvType(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxPadBitOffset_DrvType, PinmuxPadMask_DrvType); + } + } + public: + constexpr PinmuxPad() : reg_address(), reg_mask(), reg_value(), safe_func(), pad_name() { /* ... */ } + + void UpdatePinmuxPad(u32 config, u32 config_mask) { + /* Update register value. */ + this->reg_value = this->ReadReg(); + + /* Check if we're locked. */ + if (this->IsLocked()) { + return; + } + + /* Update PM. */ + if ((config_mask & PinmuxOptBitMask_Pm) != 0) { + const auto opt = (config & PinmuxOptBitMask_Pm); + u8 pm = PinmuxPadPm_Safe; + if (opt != PinmuxOpt_Gpio) { + if (opt == PinmuxOpt_Unused) { + this->UpdatePupd(true); + this->UpdateTristate(true); + this->UpdateEInput(0); + } else if (opt <= PinmuxOpt_Unused) { + pm = opt >> PinmuxOptBitOffset_Pm; + } + } + this->UpdatePm(pm); + } + + /* Update pupd. */ + if ((config_mask & PinmuxOptBitMask_Pupd) != 0) { + const auto opt = (config & PinmuxOptBitMask_Pupd); + if (opt == PinmuxOpt_NoPupd || opt == PinmuxOpt_PullDown || opt == PinmuxOpt_PullUp) { + this->UpdatePupd(opt >> PinmuxOptBitOffset_Pupd); + } + } + + /* Update direction. */ + if ((config_mask & PinmuxOptBitMask_Dir) != 0) { + const auto opt = (config & PinmuxOptBitMask_Dir); + if (opt == PinmuxOpt_Output) { + this->UpdateTristate(false); + this->UpdateEInput(false); + if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { + this->UpdateEOd(false); + } + } else if (opt == PinmuxOpt_Input) { + this->UpdateTristate(true); + this->UpdateEInput(true); + if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { + this->UpdateEOd(false); + } + } else if (opt == PinmuxOpt_Bidirection) { + this->UpdateTristate(false); + this->UpdateEInput(true); + if ((this->reg_mask & PinmuxPadMask_EOd) != 0) { + this->UpdateEOd(false); + } + } else if (opt == PinmuxOpt_OpenDrain) { + this->UpdateTristate(false); + this->UpdateEInput(true); + this->UpdateEOd(true); + } + } + + /* Update Lock. */ + if ((config_mask & PinmuxOptBitMask_Lock) != 0) { + const auto opt = (config & PinmuxOptBitMask_Lock); + this->UpdateLock(opt != 0); + } + + /* Update IoReset. */ + if ((config_mask & PinmuxOptBitMask_IoReset) != 0) { + const auto opt = (config & PinmuxOptBitMask_IoReset); + this->UpdateIoReset(opt != 0); + } + + /* Update Park. */ + if ((config_mask & PinmuxOptBitMask_Park) != 0) { + const auto opt = (config & PinmuxOptBitMask_Park); + this->UpdatePark(opt != 0); + } + + /* Update Lpdr. */ + if ((config_mask & PinmuxOptBitMask_Lpdr) != 0) { + const auto opt = (config & PinmuxOptBitMask_Lpdr); + this->UpdateELpdr(opt != 0); + } + + /* Update Hsm. */ + if ((config_mask & PinmuxOptBitMask_Hsm) != 0) { + const auto opt = (config & PinmuxOptBitMask_Hsm); + this->UpdateEHsm(opt != 0); + } + + /* Update IoHv. */ + if ((config_mask & PinmuxOptBitMask_IoHv) != 0) { + const auto opt = (config & PinmuxOptBitMask_IoHv); + this->UpdateEIoHv(opt != 0); + } + + /* Update Schmt. */ + if ((config_mask & PinmuxOptBitMask_Schmt) != 0) { + const auto opt = (config & PinmuxOptBitMask_Schmt); + this->UpdateESchmt(opt != 0); + } + + /* Update Preemp. */ + if ((config_mask & PinmuxOptBitMask_Preemp) != 0) { + const auto opt = (config & PinmuxOptBitMask_Preemp); + this->UpdatePreemp(opt != 0); + } + + /* Update drive type. */ + if ((config_mask & PinmuxOptBitMask_DrvType) != 0) { + const auto opt = (config & PinmuxOptBitMask_DrvType); + this->UpdateDrvType(opt >> PinmuxOptBitOffset_DrvType); + } + + /* Write the updated register value. */ + this->WriteReg(); + } + + void SetCharacter(const PinmuxPadCharacter &character) { + this->reg_address = character.reg_offset + 0x70000000; + this->reg_mask = character.reg_mask; + this->safe_func = character.safe_func; + this->reg_value = this->ReadReg(); + this->pad_name = character.pad_name; + + if ((this->reg_mask & this->reg_value & PinmuxPadMask_Park) != 0) { + this->reg_value &= ~(PinmuxPadMask_Park); + } + + this->WriteReg(); + } + }; + + class PinmuxDrivePad { + private: + u32 reg_address; + u32 reg_mask; + u32 reg_value; + u8 safe_func; + const char *pad_name; + private: + bool IsValidRegisterAddress() const { + return this->reg_address - 0x700008E4 <= 0x288; + } + + uintptr_t GetRegisterAddress() const { + return g_apb_misc_virtual_address + (this->reg_address - 0x70000000); + } + + bool UpdateBits(u32 value, u32 offset, u32 mask) { + if ((this->reg_mask & mask) != 0) { + if ((value & (mask >> offset)) != ((this->reg_value & mask) >> offset)) { + this->reg_value = (this->reg_value & ~mask) | ((value << offset) & mask); + } + return true; + } else { + return false; + } + } + + u32 ReadReg() const { + if (this->IsValidRegisterAddress()) { + return reg::Read(this->GetRegisterAddress()); + } else { + return 0; + } + } + + void WriteReg() const { + if (this->IsValidRegisterAddress()) { + reg::Write(this->GetRegisterAddress(), this->reg_value); + } + } + + bool IsCzDrvDn() const { + return (this->reg_mask & PinmuxDrivePadMask_CzDrvDn) == PinmuxDrivePadMask_CzDrvDn; + } + + bool IsCzDrvUp() const { + return (this->reg_mask & PinmuxDrivePadMask_CzDrvUp) == PinmuxDrivePadMask_CzDrvUp; + } + + void UpdateDrvDn(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_DrvDn, PinmuxDrivePadMask_DrvDn); + } + } + + void UpdateDrvUp(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_DrvUp, PinmuxDrivePadMask_DrvUp); + } + } + + void UpdateCzDrvDn(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_CzDrvDn, PinmuxDrivePadMask_CzDrvDn); + } + } + + void UpdateCzDrvUp(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_CzDrvUp, PinmuxDrivePadMask_CzDrvUp); + } + } + + void UpdateSlwR(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_SlwR, PinmuxDrivePadMask_SlwR); + } + } + + void UpdateSlwF(u8 v) { + if (v != 0xFF) { + this->UpdateBits(v, PinmuxDrivePadBitOffset_SlwF, PinmuxDrivePadMask_SlwF); + } + } + public: + constexpr PinmuxDrivePad() : reg_address(), reg_mask(), reg_value(), pad_name() { /* ... */ } + + void UpdatePinmuxDrivePad(u32 config, u32 config_mask) { + /* Update register value. */ + this->reg_value = this->ReadReg(); + + /* Update drvdn. */ + if ((config_mask & PinmuxDriveOptBitMask_DrvDn) != 0) { + if (this->IsCzDrvDn()) { + const auto opt = (config & PinmuxDriveOptBitMask_CzDrvDn); + this->UpdateCzDrvDn(opt >> PinmuxDriveOptBitOffset_CzDrvDn); + } else { + const auto opt = (config & PinmuxDriveOptBitMask_DrvDn); + this->UpdateDrvDn(opt >> PinmuxDriveOptBitOffset_DrvDn); + } + } + + /* Update drvup. */ + if ((config_mask & PinmuxDriveOptBitMask_DrvUp) != 0) { + if (this->IsCzDrvUp()) { + const auto opt = (config & PinmuxDriveOptBitMask_CzDrvUp); + this->UpdateCzDrvUp(opt >> PinmuxDriveOptBitOffset_CzDrvUp); + } else { + const auto opt = (config & PinmuxDriveOptBitMask_DrvUp); + this->UpdateDrvUp(opt >> PinmuxDriveOptBitOffset_DrvUp); + } + } + + /* Update slwr */ + if ((config_mask & PinmuxDriveOptBitMask_SlwR) != 0) { + const auto opt = (config & PinmuxDriveOptBitMask_SlwR); + this->UpdateSlwR(opt >> PinmuxDriveOptBitOffset_SlwR); + } + + /* Update slwf */ + if ((config_mask & PinmuxDriveOptBitMask_SlwR) != 0) { + const auto opt = (config & PinmuxDriveOptBitMask_SlwF); + this->UpdateSlwF(opt >> PinmuxDriveOptBitOffset_SlwF); + } + + /* Write the updated register value. */ + this->WriteReg(); + } + + void SetCharacter(const PinmuxDrivePadCharacter &character) { + this->reg_address = character.reg_offset + 0x70000000; + this->reg_mask = character.reg_mask; + this->reg_value = this->ReadReg(); + this->pad_name = character.pad_name; + } + }; + + constinit std::array g_pinmux_pads{}; + constinit std::array g_pinmux_drive_pads{}; + + } + + void InitializePlatformPads() { + /* Initialize all pads. */ + for (size_t i = 0; i < NumPinmuxPadCharacters; ++i) { + g_pinmux_pads[i].SetCharacter(PinmuxPadCharacters[i]); + } + + /* Update all drive pads. */ + for (size_t i = 0; i < NumPinmuxDrivePadCharacters; ++i) { + g_pinmux_drive_pads[i].SetCharacter(PinmuxDrivePadCharacters[i]); + } + } + + void UpdateSinglePinmuxPad(const PinmuxPadConfig &config) { + if (IsInitialized()) { + g_pinmux_pads[config.index].UpdatePinmuxPad(config.option, config.option_mask); + } + } + + void UpdateSinglePinmuxDrivePad(const PinmuxDrivePadConfig &config) { + if (IsInitialized()) { + g_pinmux_drive_pads[config.index].UpdatePinmuxDrivePad(config.option, config.option_mask); + } + } + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.hpp b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.hpp new file mode 100644 index 000000000..144bf52d0 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/board/nintendo_nx/pinmux_platform_pads.hpp @@ -0,0 +1,38 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::pinmux::driver::board::nintendo_nx { + + struct PinmuxPadConfig { + u32 index; + u32 option; + u32 option_mask; + }; + + struct PinmuxDrivePadConfig { + u32 index; + u32 option; + u32 option_mask; + }; + + void InitializePlatformPads(); + + void UpdateSinglePinmuxPad(const PinmuxPadConfig &config); + void UpdateSinglePinmuxDrivePad(const PinmuxDrivePadConfig &config); + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/pinmux_driver_api.cpp b/libraries/libstratosphere/source/pinmux/driver/pinmux_driver_api.cpp new file mode 100644 index 000000000..fc7e35936 --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/pinmux_driver_api.cpp @@ -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 . + */ +#include +#include "pinmux_select_board_impl.hpp" + +namespace ams::pinmux::driver { + + namespace { + + constinit os::SdkMutex g_init_mutex; + constinit int g_init_count = 0; + + } + + void Initialize() { + std::scoped_lock lk(g_init_mutex); + + if ((g_init_count++) == 0) { + if (!board::IsInitialized()) { + board::Initialize(); + } + } + } + + void Finalize() { + std::scoped_lock lk(g_init_mutex); + + if ((--g_init_count) == 0) { + AMS_ASSERT(board::IsInitialized()); + board::Finalize(); + } + } + + void SetInitialConfig() { + board::SetInitialConfig(); + } + + void SetInitialDrivePadConfig() { + board::SetInitialDrivePadConfig(); + } + +} diff --git a/libraries/libstratosphere/source/pinmux/driver/pinmux_select_board_impl.hpp b/libraries/libstratosphere/source/pinmux/driver/pinmux_select_board_impl.hpp new file mode 100644 index 000000000..1bf2c9abd --- /dev/null +++ b/libraries/libstratosphere/source/pinmux/driver/pinmux_select_board_impl.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 . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include "board/nintendo_nx/pinmux_board_driver_api.hpp" + namespace ams::pinmux::driver::board { + using namespace ams::pinmux::driver::board::nintendo_nx; + } + +#else + + #error "Unknown board for pinmux driver" + +#endif \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.cpp b/libraries/libstratosphere/source/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.cpp new file mode 100644 index 000000000..aa49183be --- /dev/null +++ b/libraries/libstratosphere/source/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.cpp @@ -0,0 +1,74 @@ +/* + * 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 . + */ +#include + +namespace ams::powctl::driver::impl { + + namespace { + + constexpr inline const PowerState AcceptablePowerStates[] = { + PowerState::FullAwake, + PowerState::MinimumAwake, + PowerState::SleepCharge, + PowerState::SleepDischarge, + PowerState::ShutdownChargeMain, + }; + + constexpr inline const PowerState AcceptablePowerStatesForNotAwakeCharge[] = { + PowerState::SleepCharge, + PowerState::ShutdownChargeMain, + }; + + constexpr inline const int Min = std::numeric_limits::min(); + constexpr inline const int Max = std::numeric_limits::max(); + + constexpr inline const UnknownParameterX UnknownXTableForBatteryVersion2[] = { + { 20000, 4320, 95.0, 100.4 }, + { 30000, 4304, 94.0, 99.7 }, + { 40000, 4288, 93.0, 98.4 }, + { 50000, 4272, 92.0, 97.0 }, + { 60000, 4256, 90.0, 95.7 }, + { 80000, 4240, 89.0, 94.2 }, + { 100000, 4224, 88.0, 93.0 }, + { Max, 4192, 85.0, 90.0 }, + }; + + /* Include automatically extracted charger parameters. */ + #include "powctl_charger_parameters.board.nintendo_nx.inc" + + } + + const ChargeParameters &GetChargeParameters() { + /* Get the battery version. */ + u8 battery_version; + if (R_FAILED(cal::GetBatteryVersion(std::addressof(battery_version)))) { + battery_version = 0; + } + + if (battery_version == 2) { + return ChargeParametersForBatteryVersion2; + } else if (battery_version == 1) { + return ChargeParametersForBatteryVersion1; + } else { + if (spl::GetHardwareType() == spl::HardwareType::_Five_) { + return ChargeParametersForBatteryVersion0ForFive; + } else { + return ChargeParametersForBatteryVersion0; + } + } + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.inc b/libraries/libstratosphere/source/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.inc new file mode 100644 index 000000000..70f86ac09 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/driver/impl/powctl_charger_parameters.board.nintendo_nx.inc @@ -0,0 +1,79 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by charger_parameters.py, do not edit manually. */ + +constexpr inline const ChargeParametersRule ChargeParametersRulesForBatteryVersion0[] = { + { BatteryTemperatureLevel::TooLow, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 512, 0, 0 }, + { BatteryTemperatureLevel::TooLow, 3320, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 768, 0, 0 }, + { BatteryTemperatureLevel::Low, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 512, 0, 0 }, + { BatteryTemperatureLevel::Low, 3320, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 768, 0, 0 }, + { BatteryTemperatureLevel::Medium, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 512, 0, 0 }, + { BatteryTemperatureLevel::Medium, 3320, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 2048, 0, 0 }, + { BatteryTemperatureLevel::High, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 3952, 512, 0, 0 }, + { BatteryTemperatureLevel::High, 3320, 4050, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 3952, 2048, 0, 0 }, + { BatteryTemperatureLevel::High, 4050, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 2048, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, Min, 3320, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 3952, 512, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, 3320, 4050, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 3952, 2048, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, 4050, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 2048, 0, 0 }, +}; + +constexpr inline const ChargeParametersRule ChargeParametersRulesForBatteryVersion1[] = { + { BatteryTemperatureLevel::TooLow, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 576, 0, 0 }, + { BatteryTemperatureLevel::Low, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 576, 0, 0 }, + { BatteryTemperatureLevel::Medium, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 1536, 0, 0 }, + { BatteryTemperatureLevel::High, Min, 3984, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 3984, 1536, 0, 0 }, + { BatteryTemperatureLevel::High, 3984, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 1536, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, Min, 3984, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 3984, 1536, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, 3984, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 1536, 0, 0 }, +}; + +constexpr inline const ChargeParametersRule ChargeParametersRulesForBatteryVersion2[] = { + { BatteryTemperatureLevel::TooLow, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4320, 640, 0, 0 }, + { BatteryTemperatureLevel::Low, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4320, 640, 0, 0 }, + { BatteryTemperatureLevel::Medium, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4320, 1664, 0, 0 }, + { BatteryTemperatureLevel::High, Min, 4080, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4080, 1664, 0, 0 }, + { BatteryTemperatureLevel::High, 4080, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4320, 1664, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, Min, 4080, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4080, 1664, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, 4080, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4320, 1664, 0, 0 }, +}; + +constexpr inline const ChargeParametersRule ChargeParametersRulesForBatteryVersion0ForFive[] = { + { BatteryTemperatureLevel::TooLow, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 768, 0, 0 }, + { BatteryTemperatureLevel::Low, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 768, 0, 0 }, + { BatteryTemperatureLevel::Medium, Min, Max, Min, 4001, 2049, Max, AcceptablePowerStatesForNotAwakeCharge, util::size(AcceptablePowerStatesForNotAwakeCharge), true, true, 4000, 3072, 40, 112 }, + { BatteryTemperatureLevel::Medium, Min, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 4208, 2048, 0, 0 }, + { BatteryTemperatureLevel::High, Min, 4050, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, true, 3952, 2048, 0, 0 }, + { BatteryTemperatureLevel::High, 4050, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 2048, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, Min, 4050, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 3952, 2048, 0, 0 }, + { BatteryTemperatureLevel::TooHigh, 4050, Max, Min, Max, Min, Max, AcceptablePowerStates, util::size(AcceptablePowerStates), false, false, 4208, 2048, 0, 0 }, +}; + +constexpr inline const ChargeParameters ChargeParametersForBatteryVersion0 = { + 4, 17, 51, 60, 512, 4208, nullptr, 0, 95.0, 99.0, ChargeParametersRulesForBatteryVersion0, util::size(ChargeParametersRulesForBatteryVersion0) +}; + +constexpr inline const ChargeParameters ChargeParametersForBatteryVersion1 = { + 1, 19, 48, 59, 1536, 4208, nullptr, 0, 95.0, 99.0, ChargeParametersRulesForBatteryVersion1, util::size(ChargeParametersRulesForBatteryVersion1) +}; + +constexpr inline const ChargeParameters ChargeParametersForBatteryVersion2 = { + 1, 19, 48, 59, 1664, 4320, UnknownXTableForBatteryVersion2, util::size(UnknownXTableForBatteryVersion2), 95.0, 100.4, ChargeParametersRulesForBatteryVersion2, util::size(ChargeParametersRulesForBatteryVersion2) +}; + +constexpr inline const ChargeParameters ChargeParametersForBatteryVersion0ForFive = { + 4, 17, 51, 60, 512, 4208, nullptr, 0, 95.0, 99.0, ChargeParametersRulesForBatteryVersion0ForFive, util::size(ChargeParametersRulesForBatteryVersion0ForFive) +}; diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp new file mode 100644 index 000000000..f3460258b --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.cpp @@ -0,0 +1,474 @@ +/* + * 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 . + */ +#include +#include "../../powctl_device_management.hpp" +#include "powctl_retry_helper.hpp" +#include "powctl_battery_driver.hpp" +#include "powctl_max17050_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace { + + constinit std::optional g_battery_device; + + Max17050Driver &GetMax17050Driver() { + static Max17050Driver s_max17050_driver; + return s_max17050_driver; + } + + constexpr inline const double SenseResistorValue = 0.005; + + } + + BatteryDevice::BatteryDevice(bool ev) : use_event_handler(ev), event_handler() { + if (this->use_event_handler) { + /* Create the system event. */ + os::CreateSystemEvent(std::addressof(this->system_event), os::EventClearMode_ManualClear, true); + + /* Create the handler. */ + this->event_handler.emplace(this); + + /* Register the event handler. */ + powctl::impl::RegisterInterruptHandler(std::addressof(*this->event_handler)); + } + } + + /* Generic API. */ + void BatteryDriver::InitializeDriver() { + /* Initialize gpio library. */ + gpio::Initialize(); + + /* Create battery device. */ + g_battery_device.emplace(this->IsEventHandlerEnabled()); + + /* Initialize the Max17050Driver. */ + { + size_t battery_vendor_size; + char battery_vendor[0x18] = {}; + if (R_FAILED(cal::GetBatteryVendor(std::addressof(battery_vendor_size), battery_vendor, sizeof(battery_vendor)))) { + battery_vendor[7] = 'A'; + battery_vendor_size = 0; + } + + u8 battery_version = 0; + if (R_FAILED(cal::GetBatteryVersion(std::addressof(battery_version)))) { + battery_version = 0; + } + + GetMax17050Driver().Initialize(battery_vendor, battery_version); + } + + /* Register our device. */ + this->RegisterDevice(std::addressof(*g_battery_device)); + + /* Register the charger device's code. */ + R_ABORT_UNLESS(powctl::impl::RegisterDeviceCode(powctl::DeviceCode_Max17050, std::addressof(*g_battery_device))); + + } + + void BatteryDriver::FinalizeDriver() { + /* Unregister the charger device code. */ + powctl::impl::UnregisterDeviceCode(powctl::DeviceCode_Max17050); + + /* Unregister our device. */ + this->UnregisterDevice(std::addressof(*g_battery_device)); + + /* Finalize Max17050Driver. */ + GetMax17050Driver().Finalize(); + + /* Destroy the charger device. */ + g_battery_device = std::nullopt; + + /* Finalize gpio library. */ + gpio::Finalize(); + } + + Result BatteryDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Check that we support event handlers. */ + R_UNLESS(this->IsEventHandlerEnabled(), powctl::ResultNotAvailable()); + + *out = device->SafeCastTo().GetSystemEvent(); + return ResultSuccess(); + } + + Result BatteryDriver::SetDeviceInterruptEnabled(IDevice *device, bool enable) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the interrupt enable. */ + device->SafeCastTo().SetInterruptEnabled(enable); + + return ResultSuccess(); + } + + Result BatteryDriver::GetDeviceErrorStatus(u32 *out, IDevice *device) { + /* TODO */ + AMS_ABORT(); + } + + Result BatteryDriver::SetDeviceErrorStatus(IDevice *device, u32 status) { + /* TODO */ + AMS_ABORT(); + } + + Result BatteryDriver::GetBatterySocRep(float *out_percent, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetSocRep(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatterySocVf(float *out_percent, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetSocVf(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryFullCapacity(int *out_mah, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double mah; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetFullCapacity(std::addressof(mah), SenseResistorValue)); + + /* Set output. */ + *out_mah = mah; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryRemainingCapacity(int *out_mah, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mah != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double mah; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetRemainingCapacity(std::addressof(mah), SenseResistorValue)); + + /* Set output. */ + *out_mah = mah; + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageMinimumAlertThreshold(percentage)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageMaximumAlertThreshold(percentage)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryPercentageFullThreshold(IDevice *device, float percentage) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetPercentageFullThreshold(percentage)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryAverageCurrent(int *out_ma, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ma; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageCurrent(std::addressof(ma), SenseResistorValue)); + + /* Set output. */ + *out_ma = ma; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryCurrent(int *out_ma, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ma; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetCurrent(std::addressof(ma), SenseResistorValue)); + + /* Set output. */ + *out_ma = ma; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(dst != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(out_size != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(dst_size == sizeof(max17050::InternalState), powctl::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(reinterpret_cast(dst), alignof(max17050::InternalState)), powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().ReadInternalState()); + GetMax17050Driver().GetInternalState(static_cast(dst)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(src != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(src_size == sizeof(max17050::InternalState), powctl::ResultInvalidArgument()); + R_UNLESS(util::IsAligned(reinterpret_cast(src), alignof(max17050::InternalState)), powctl::ResultInvalidArgument()); + + GetMax17050Driver().SetInternalState(*static_cast(src)); + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().WriteInternalState()); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetNeedToRestoreParameters(out)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryNeedToRestoreParameters(IDevice *device, bool en) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetNeedToRestoreParameters(en)); + + return ResultSuccess(); + } + + Result BatteryDriver::IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().IsI2cShutdownEnabled(out)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryI2cShutdownEnabled(IDevice *device, bool en) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetI2cShutdownEnabled(en)); + + return ResultSuccess(); + } + + Result BatteryDriver::IsBatteryPresent(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the battery status. */ + u16 status; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetStatus(std::addressof(status))); + + /* Set output. */ + *out = (status & 0x0008) == 0; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryCycles(int *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the battery cycles. */ + u16 cycles; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetCycles(std::addressof(cycles))); + + /* Set output. */ + *out = cycles; + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryCycles(IDevice *device, int cycles) { + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(cycles == 0, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().ResetCycles()); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryAge(float *out_percent, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_percent != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAge(std::addressof(percent))); + + /* Set output. */ + *out_percent = percent; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryTemperature(float *out_c, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double temp; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetTemperature(std::addressof(temp))); + + /* Set output. */ + *out_c = temp; + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryMaximumTemperature(float *out_c, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_c != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + u8 max_temp; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetMaximumTemperature(std::addressof(max_temp))); + + /* Set output. */ + *out_c = static_cast(max_temp); + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMinimumAlertThreshold(c)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetTemperatureMaximumAlertThreshold(c)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryVCell(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetVCell(out_mv)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryAverageVCell(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCell(out_mv)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + double ms; + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetAverageVCellTime(std::addressof(ms))); + + /* Set output. */ + *out = TimeSpan::FromMicroSeconds(static_cast(ms * 1000.0)); + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMinimumAlertThreshold(mv)); + + return ResultSuccess(); + } + + Result BatteryDriver::GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Get the value. */ + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().GetOpenCircuitVoltage(out_mv)); + + return ResultSuccess(); + } + + Result BatteryDriver::SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetMax17050Driver().SetVoltageMaximumAlertThreshold(mv)); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.hpp new file mode 100644 index 000000000..995b7dc26 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_battery_driver.hpp @@ -0,0 +1,145 @@ +/* + * 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 . + */ +#pragma once +#include +#include "../../powctl_i_power_control_driver.hpp" +#include "powctl_interrupt_event_handler.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + class BatteryDevice : public powctl::impl::IDevice { + NON_COPYABLE(BatteryDevice); + NON_MOVEABLE(BatteryDevice); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::BatteryDevice, ::ams::powctl::impl::IDevice); + private: + bool use_event_handler; + std::optional event_handler; + os::SystemEventType system_event; + public: + BatteryDevice(bool ev); + + os::SystemEventType *GetSystemEvent() { return std::addressof(this->system_event); } + + void SetInterruptEnabled(bool en) { + if (this->use_event_handler) { + this->event_handler->SetInterruptEnabled(en); + } + } + }; + + class BatteryDriver : public IPowerControlDriver { + NON_COPYABLE(BatteryDriver); + NON_MOVEABLE(BatteryDriver); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::BatteryDriver, ::ams::powctl::impl::IPowerControlDriver); + public: + BatteryDriver(bool ev) : IPowerControlDriver(ev) { /* ... */ } + + /* Generic API. */ + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) override; + virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) override; + + virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) override; + virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) override; + + /* Battery API. */ + virtual Result GetBatterySocRep(float *out_percent, IDevice *device) override; + + virtual Result GetBatterySocVf(float *out_percent, IDevice *device) override; + + virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) override; + virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) override; + + virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) override; + virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) override; + virtual Result SetBatteryPercentageFullThreshold(IDevice *device, float percentage) override; + + virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) override; + virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) override; + + virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) override; + virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) override; + + virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) override; + virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) override; + + virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) override; + virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) override; + + virtual Result IsBatteryPresent(bool *out, IDevice *device) override; + + virtual Result GetBatteryCycles(int *out, IDevice *device) override; + virtual Result SetBatteryCycles(IDevice *device, int cycles) override; + + virtual Result GetBatteryAge(float *out_percent, IDevice *device) override; + + virtual Result GetBatteryTemperature(float *out_c, IDevice *device) override; + virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) override; + + virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) override; + virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) override; + + virtual Result GetBatteryVCell(int *out_mv, IDevice *device) override; + virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) override; + + virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) override; + + virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) override; + + virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) override; + + virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) override; + + /* Unsupported Charger API. */ + virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + + virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) override { return powctl::ResultNotSupported(); } + + virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerHiZEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } + + virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + + virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } + + virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) override { return powctl::ResultNotSupported(); } + virtual Result ResetChargerWatchdogTimer(IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) override { return powctl::ResultNotSupported(); } + + virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetChargerVoltageClamp(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.cpp new file mode 100644 index 000000000..c22f7ed81 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.cpp @@ -0,0 +1,73 @@ +/* + * 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 . + */ +#include +#include "../../powctl_device_management.hpp" +#include "powctl_board_impl.hpp" +#include "powctl_battery_driver.hpp" +#include "powctl_charger_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace { + + constinit std::optional g_charger_driver; + constinit std::optional g_battery_driver; + + void InitializeChargerDriver(bool use_event_handlers) { + /* Create the charger driver. */ + g_charger_driver.emplace(use_event_handlers); + + /* Register the driver. */ + powctl::impl::RegisterDriver(std::addressof(*g_charger_driver)); + } + + void InitializeBatteryDriver(bool use_event_handlers) { + /* Create the battery driver. */ + g_battery_driver.emplace(use_event_handlers); + + /* Register the driver. */ + powctl::impl::RegisterDriver(std::addressof(*g_battery_driver)); + } + + void FinalizeChargerDriver() { + /* Unregister the driver. */ + powctl::impl::UnregisterDriver(std::addressof(*g_charger_driver)); + + /* Destroy the battery driver. */ + g_charger_driver = std::nullopt; + } + + void FinalizeBatteryDriver() { + /* Unregister the driver. */ + powctl::impl::UnregisterDriver(std::addressof(*g_battery_driver)); + + /* Destroy the battery driver. */ + g_battery_driver = std::nullopt; + } + + } + + void Initialize(bool use_event_handlers) { + InitializeChargerDriver(use_event_handlers); + InitializeBatteryDriver(use_event_handlers); + } + + void Finalize() { + FinalizeBatteryDriver(); + FinalizeChargerDriver(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.hpp new file mode 100644 index 000000000..902c5e99f --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_board_impl.hpp @@ -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 . + */ +#pragma once +#include + +#include "powctl_interrupt_event_handler.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + void Initialize(bool use_event_handlers); + void Finalize(); + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.cpp new file mode 100644 index 000000000..32fa8a940 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.cpp @@ -0,0 +1,413 @@ +/* + * 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 . + */ +#include +#include "powctl_bq24193_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace bq24193 { + + constexpr inline u8 InputSourceControl = 0x00; + constexpr inline u8 PowerOnConfiguration = 0x01; + constexpr inline u8 ChargeCurrentControl = 0x02; + constexpr inline u8 PreChargeTerminationCurrentControl = 0x03; + constexpr inline u8 ChargeVoltageControl = 0x04; + constexpr inline u8 ChargeTerminationTimerControl = 0x05; + constexpr inline u8 IrCompensationThermalRegulationControl = 0x06; + constexpr inline u8 MiscOperationControl = 0x07; + constexpr inline u8 SystemStatus = 0x08; + constexpr inline u8 Fault = 0x09; + constexpr inline u8 VendorPartRevisionStatus = 0x0A; + + constexpr u8 EncodePreChargeCurrentLimit(int ma) { + constexpr int Minimum = 128; + constexpr int Maximum = 2048; + ma = std::max(std::min(ma, Maximum), Minimum); + + return static_cast((static_cast(ma - Minimum) >> 7) << 4); + } + + constexpr u8 EncodeTerminationCurrentLimit(int ma) { + constexpr int Minimum = 128; + constexpr int Maximum = 2048; + ma = std::max(std::min(ma, Maximum), Minimum); + + return static_cast((static_cast(ma - Minimum) >> 7) << 0); + } + + constexpr u8 EncodeMinimumSystemVoltageLimit(int mv) { + constexpr int Minimum = 3000; + constexpr int Maximum = 3700; + mv = std::max(std::min(mv, Maximum), Minimum); + + return static_cast(((mv - Minimum) / 100) << 1); + } + + constexpr u8 EncodeFastChargeCurrentLimit(int ma) { + constexpr int Minimum = 512; + constexpr int Maximum = 4544; + ma = std::max(std::min(ma, Maximum), Minimum); + + return static_cast((static_cast(ma - Minimum) >> 6) << 2); + } + + constexpr int DecodeFastChargeCurrentLimit(u8 reg) { + constexpr int Minimum = 512; + + return Minimum + (static_cast(reg & 0xFC) << 4); + } + + static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(512)) == 512); + static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(4544)) == 4544); + static_assert(DecodeFastChargeCurrentLimit(EncodeFastChargeCurrentLimit(576)) == 576); + + constexpr u8 EncodeChargeVoltageLimit(int mv) { + constexpr int Minimum = 3504; + constexpr int Maximum = 4400; + mv = std::max(std::min(mv, Maximum), Minimum); + + return static_cast((static_cast(mv - Minimum) >> 4) << 2); + } + + constexpr int DecodeChargeVoltageLimit(u8 reg) { + constexpr int Minimum = 3504; + + return Minimum + (static_cast(reg & 0xFC) << 2); + } + + static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(3504)) == 3504); + static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(4400)) == 4400); + static_assert(DecodeChargeVoltageLimit(EncodeChargeVoltageLimit(3520)) == 3520); + + constexpr u8 EncodeChargerConfiguration(bq24193::ChargerConfiguration cfg) { + switch (cfg) { + case ChargerConfiguration_ChargeDisable: return 0x00; + case ChargerConfiguration_ChargeBattery: return 0x10; + case ChargerConfiguration_Otg: return 0x20; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + constexpr u8 EncodeWatchdogTimerSetting(int seconds) { + if (seconds == 0) { + return 0x00; + } else if (seconds < 80) { + return 0x10; + } else if (seconds < 160) { + return 0x20; + } else { + return 0x30; + } + } + + constexpr u8 EncodeBatteryCompensation(int mo) { + constexpr int Minimum = 0; + constexpr int Maximum = 70; + mo = std::max(std::min(mo, Maximum), Minimum); + + return static_cast((static_cast(mo - Minimum) / 10) << 5); + } + + constexpr int DecodeBatteryCompensation(u8 reg) { + constexpr int Minimum = 0; + + return Minimum + (static_cast(reg & 0xE0) >> 5) * 10; + } + + static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(0)) == 0); + static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(70)) == 70); + static_assert(DecodeBatteryCompensation(EncodeBatteryCompensation(30)) == 30); + + constexpr u8 EncodeVoltageClamp(int mv) { + constexpr int Minimum = 0; + constexpr int Maximum = 112; + mv = std::max(std::min(mv, Maximum), Minimum); + + return static_cast((static_cast(mv - Minimum) >> 4) << 2); + } + + constexpr int DecodeVoltageClamp(u8 reg) { + constexpr int Minimum = 0; + + return Minimum + (static_cast(reg & 0x1C) << 2); + } + + static_assert(DecodeVoltageClamp(EncodeVoltageClamp(0)) == 0); + static_assert(DecodeVoltageClamp(EncodeVoltageClamp(112)) == 112); + static_assert(DecodeVoltageClamp(EncodeVoltageClamp(64)) == 64); + + constexpr u8 EncodeInputCurrentLimit(int ma) { + if (ma < 150) { + return 0; + } else if (ma < 500) { + return 1; + } else if (ma < 900) { + return 2; + } else if (ma < 1200) { + return 3; + } else if (ma < 1500) { + return 4; + } else if (ma < 2000) { + return 5; + } else if (ma < 3000) { + return 6; + } else{ + return 7; + } + } + + constexpr int DecodeInputCurrentLimit(u8 reg) { + switch (reg & 0x07) { + case 0: return 100; + case 1: return 150; + case 2: return 500; + case 3: return 900; + case 4: return 1200; + case 5: return 1500; + case 6: return 2000; + case 7: return 3000; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(100)) == 100); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(150)) == 150); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(500)) == 500); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(900)) == 900); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(1200)) == 1200); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(1500)) == 1500); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(2000)) == 2000); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(3000)) == 3000); + + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(0)) == 100); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(9999)) == 3000); + + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(149)) == 100); + static_assert(DecodeInputCurrentLimit(EncodeInputCurrentLimit(151)) == 150); + + constexpr u8 EncodeInputVoltageLimit(int mv) { + constexpr int Minimum = 3880; + constexpr int Maximum = 5080; + mv = std::max(std::min(mv, Maximum), Minimum); + + return static_cast(((static_cast(mv - Minimum) / 80) & 0xF) << 3); + } + + constexpr u8 EncodeBoostModeCurrentLimit(int ma) { + return ma >= 1300 ? 1 : 0; + } + + constexpr bq24193::ChargerStatus DecodeChargerStatus(u8 reg) { + switch (reg & 0x30) { + case 0x00: return bq24193::ChargerStatus_NotCharging; + case 0x10: return bq24193::ChargerStatus_PreCharge; + case 0x20: return bq24193::ChargerStatus_FastCharging; + case 0x30: return bq24193::ChargerStatus_ChargeTerminationDone; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + } + + namespace { + + ALWAYS_INLINE Result ReadWriteRegister(const i2c::I2cSession &session, u8 address, u8 mask, u8 value) { + /* Read the current value. */ + u8 cur_val; + R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); + + /* Update the value. */ + const u8 new_val = (cur_val & ~mask) | (value & mask); + R_TRY(i2c::WriteSingleRegister(session, address, new_val)); + + return ResultSuccess(); + } + + } + + Result Bq24193Driver::InitializeSession() { + /* Set fast charge current limit. */ + R_TRY(this->SetFastChargeCurrentLimit(512)); + + /* Disable force 20 percent charge. */ + R_TRY(this->SetForce20PercentChargeCurrent(false)); + + /* Set pre-charge current limit. */ + R_TRY(this->SetPreChargeCurrentLimit(128)); + + /* Set termination current limit. */ + R_TRY(this->SetTerminationCurrentLimit(128)); + + /* Set minimum system voltage limit. */ + R_TRY(this->SetMinimumSystemVoltageLimit(3000)); + + /* Set watchdog timer setting. */ + R_TRY(this->SetWatchdogTimerSetting(0)); + + /* Disable charging safety timer. */ + R_TRY(this->SetChargingSafetyTimerEnabled(false)); + + /* Reset the watchdog timer. */ + R_TRY(this->ResetWatchdogTimer()); + + return ResultSuccess(); + } + + Result Bq24193Driver::SetPreChargeCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::PreChargeTerminationCurrentControl, 0xF0, bq24193::EncodePreChargeCurrentLimit(ma)); + } + + Result Bq24193Driver::SetTerminationCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::PreChargeTerminationCurrentControl, 0x0F, bq24193::EncodeTerminationCurrentLimit(ma)); + } + + Result Bq24193Driver::SetMinimumSystemVoltageLimit(int mv) { + return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x0E, bq24193::EncodeMinimumSystemVoltageLimit(mv)); + } + + Result Bq24193Driver::SetChargingSafetyTimerEnabled(bool en) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeTerminationTimerControl, 0x08, en ? 0x08 : 0x00); + } + + Result Bq24193Driver::GetForce20PercentChargeCurrent(bool *out) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeCurrentControl, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x01) != 0; + return ResultSuccess(); + } + + Result Bq24193Driver::SetForce20PercentChargeCurrent(bool en) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeCurrentControl, 0x01, en ? 0x01 : 0x00); + } + + Result Bq24193Driver::GetFastChargeCurrentLimit(int *out_ma) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeCurrentControl, std::addressof(val))); + + /* Extract the value. */ + *out_ma = bq24193::DecodeFastChargeCurrentLimit(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetFastChargeCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeCurrentControl, 0xFC, bq24193::EncodeFastChargeCurrentLimit(ma)); + } + + Result Bq24193Driver::GetChargeVoltageLimit(int *out_mv) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::ChargeVoltageControl, std::addressof(val))); + + /* Extract the value. */ + *out_mv = bq24193::DecodeChargeVoltageLimit(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetChargeVoltageLimit(int mv) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeVoltageControl, 0xFC, bq24193::EncodeChargeVoltageLimit(mv)); + } + + Result Bq24193Driver::SetChargerConfiguration(bq24193::ChargerConfiguration cfg) { + return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x30, bq24193::EncodeChargerConfiguration(cfg)); + } + + Result Bq24193Driver::IsHiZEnabled(bool *out) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::InputSourceControl, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x80) != 0; + return ResultSuccess(); + } + + Result Bq24193Driver::SetHiZEnabled(bool en) { + return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x80, en ? 0x80 : 0x00); + } + + Result Bq24193Driver::GetInputCurrentLimit(int *out_ma) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::InputSourceControl, std::addressof(val))); + + /* Extract the value. */ + *out_ma = bq24193::DecodeInputCurrentLimit(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetInputCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x07, bq24193::EncodeInputCurrentLimit(ma)); + } + + Result Bq24193Driver::SetInputVoltageLimit(int mv) { + return ReadWriteRegister(this->i2c_session, bq24193::InputSourceControl, 0x78, bq24193::EncodeInputVoltageLimit(mv)); + } + + Result Bq24193Driver::SetBoostModeCurrentLimit(int ma) { + return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x01, bq24193::EncodeBoostModeCurrentLimit(ma)); + } + + Result Bq24193Driver::GetChargerStatus(bq24193::ChargerStatus *out) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::SystemStatus, std::addressof(val))); + + /* Extract the value. */ + *out = bq24193::DecodeChargerStatus(val); + return ResultSuccess(); + } + + Result Bq24193Driver::ResetWatchdogTimer() { + return ReadWriteRegister(this->i2c_session, bq24193::PowerOnConfiguration, 0x40, 0x40); + } + + Result Bq24193Driver::SetWatchdogTimerSetting(int seconds) { + return ReadWriteRegister(this->i2c_session, bq24193::ChargeTerminationTimerControl, 0x30, bq24193::EncodeWatchdogTimerSetting(seconds)); + } + + Result Bq24193Driver::GetBatteryCompensation(int *out_mo) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, std::addressof(val))); + + /* Extract the value. */ + *out_mo = bq24193::DecodeBatteryCompensation(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetBatteryCompensation(int mo) { + return ReadWriteRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, 0xE0, bq24193::EncodeBatteryCompensation(mo)); + } + + Result Bq24193Driver::GetVoltageClamp(int *out_mv) { + /* Get the register. */ + u8 val; + R_TRY(i2c::ReadSingleRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, std::addressof(val))); + + /* Extract the value. */ + *out_mv = bq24193::DecodeVoltageClamp(val); + return ResultSuccess(); + } + + Result Bq24193Driver::SetVoltageClamp(int mv) { + return ReadWriteRegister(this->i2c_session, bq24193::IrCompensationThermalRegulationControl, 0x1C, bq24193::EncodeVoltageClamp(mv)); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp new file mode 100644 index 000000000..01d9b906c --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_bq24193_driver.hpp @@ -0,0 +1,115 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace bq24193 { + + enum ChargerConfiguration { + ChargerConfiguration_ChargeDisable = 0, + ChargerConfiguration_ChargeBattery = 1, + ChargerConfiguration_Otg = 2, + }; + + enum ChargerStatus { + ChargerStatus_NotCharging = 0, + ChargerStatus_PreCharge = 1, + ChargerStatus_FastCharging = 2, + ChargerStatus_ChargeTerminationDone = 3, + }; + + } + + class Bq24193Driver { + private: + os::SdkMutex mutex; + int init_count; + i2c::I2cSession i2c_session; + private: + Result InitializeSession(); + public: + Bq24193Driver() : mutex(), init_count(0), i2c_session() { + /* ... */ + } + + void Initialize() { + std::scoped_lock lk(this->mutex); + if ((this->init_count++) == 0) { + /* Initialize i2c library. */ + i2c::InitializeEmpty(); + + /* Open session. */ + R_ABORT_UNLESS(i2c::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Bq24193)); + + /* Initialize session. */ + R_ABORT_UNLESS(this->InitializeSession()); + } + } + + void Finalize() { + std::scoped_lock lk(this->mutex); + if ((--this->init_count) == 0) { + /* Close session. */ + i2c::CloseSession(this->i2c_session); + + /* Finalize i2c library. */ + i2c::Finalize(); + } + } + + Result SetPreChargeCurrentLimit(int ma); + Result SetTerminationCurrentLimit(int ma); + + Result SetMinimumSystemVoltageLimit(int mv); + + Result SetChargingSafetyTimerEnabled(bool en); + + Result GetForce20PercentChargeCurrent(bool *out); + Result SetForce20PercentChargeCurrent(bool en); + + Result GetFastChargeCurrentLimit(int *out_ma); + Result SetFastChargeCurrentLimit(int ma); + + Result GetChargeVoltageLimit(int *out_mv); + Result SetChargeVoltageLimit(int mv); + + Result SetChargerConfiguration(bq24193::ChargerConfiguration cfg); + + Result IsHiZEnabled(bool *out); + Result SetHiZEnabled(bool en); + + Result GetInputCurrentLimit(int *out_ma); + Result SetInputCurrentLimit(int ma); + + Result SetInputVoltageLimit(int mv); + + Result SetBoostModeCurrentLimit(int ma); + + Result GetChargerStatus(bq24193::ChargerStatus *out); + + Result ResetWatchdogTimer(); + Result SetWatchdogTimerSetting(int seconds); + + Result GetBatteryCompensation(int *out_mo); + Result SetBatteryCompensation(int mo); + + Result GetVoltageClamp(int *out_mv); + Result SetVoltageClamp(int mv); + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp new file mode 100644 index 000000000..3771bbb44 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.cpp @@ -0,0 +1,369 @@ +/* + * 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 . + */ +#include +#include "../../powctl_device_management.hpp" +#include "powctl_retry_helper.hpp" +#include "powctl_charger_driver.hpp" +#include "powctl_bq24193_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace { + + constinit std::optional g_charger_device; + + Bq24193Driver &GetBq24193Driver() { + static Bq24193Driver s_bq24193_driver; + return s_bq24193_driver; + } + + } + + ChargerDevice::ChargerDevice(bool ev) : gpio_pad_session(), watchdog_timer_enabled(false), watchdog_timer_timeout(0), use_event_handler(ev), event_handler() { + if (this->use_event_handler) { + /* Create the system event. */ + os::CreateSystemEvent(std::addressof(this->system_event), os::EventClearMode_ManualClear, true); + + /* Create the handler. */ + this->event_handler.emplace(this); + + /* Register the event handler. */ + powctl::impl::RegisterInterruptHandler(std::addressof(*this->event_handler)); + } + } + + /* Generic API. */ + void ChargerDriver::InitializeDriver() { + /* Initialize Bq24193Driver */ + GetBq24193Driver().Initialize(); + + /* Initialize gpio library. */ + gpio::Initialize(); + + /* Create charger device. */ + g_charger_device.emplace(this->IsEventHandlerEnabled()); + + /* Open the device's gpio session. */ + R_ABORT_UNLESS(gpio::OpenSession(g_charger_device->GetPadSession(), gpio::DeviceCode_BattChgEnableN)); + + /* Configure the gpio session as output. */ + gpio::SetDirection(g_charger_device->GetPadSession(), gpio::Direction_Output); + + /* Register our device. */ + this->RegisterDevice(std::addressof(*g_charger_device)); + + /* Register the charger device's code. */ + R_ABORT_UNLESS(powctl::impl::RegisterDeviceCode(powctl::DeviceCode_Bq24193, std::addressof(*g_charger_device))); + } + + void ChargerDriver::FinalizeDriver() { + /* Unregister the charger device code. */ + powctl::impl::UnregisterDeviceCode(powctl::DeviceCode_Bq24193); + + /* Unregister our device. */ + this->UnregisterDevice(std::addressof(*g_charger_device)); + + /* Close the device's gpio session. */ + gpio::CloseSession(g_charger_device->GetPadSession()); + + /* Destroy the charger device. */ + g_charger_device = std::nullopt; + + /* Finalize gpio library. */ + gpio::Finalize(); + + /* Finalize Bq24193Driver. */ + GetBq24193Driver().Finalize(); + } + + Result ChargerDriver::GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Check that we support event handlers. */ + R_UNLESS(this->IsEventHandlerEnabled(), powctl::ResultNotAvailable()); + + *out = device->SafeCastTo().GetSystemEvent(); + return ResultSuccess(); + } + + Result ChargerDriver::SetDeviceInterruptEnabled(IDevice *device, bool enable) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Set the interrupt enable. */ + device->SafeCastTo().SetInterruptEnabled(enable); + + return ResultSuccess(); + } + + Result ChargerDriver::GetDeviceErrorStatus(u32 *out, IDevice *device) { + /* TODO */ + AMS_ABORT(); + } + + Result ChargerDriver::SetDeviceErrorStatus(IDevice *device, u32 status) { + /* TODO */ + AMS_ABORT(); + } + + /* Charger API. */ + Result ChargerDriver::GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + /* Check if we're not charging. */ + if (gpio::GetValue(device->SafeCastTo().GetPadSession()) == gpio::GpioValue_High) { + *out = ChargeCurrentState_NotCharging; + } else { + /* Get force 20 percent charge state. */ + bool force_20_percent; + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetForce20PercentChargeCurrent(std::addressof(force_20_percent))); + + /* Set output appropriately. */ + if (force_20_percent) { + *out = ChargeCurrentState_ChargingForce20Percent; + } else { + *out = ChargeCurrentState_Charging; + } + } + + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + switch (state) { + case ChargeCurrentState_NotCharging: + gpio::SetValue(device->SafeCastTo().GetPadSession(), gpio::GpioValue_High); + break; + case ChargeCurrentState_ChargingForce20Percent: + case ChargeCurrentState_Charging: + gpio::SetValue(device->SafeCastTo().GetPadSession(), gpio::GpioValue_Low); + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetForce20PercentChargeCurrent(state == ChargeCurrentState_ChargingForce20Percent)); + break; + case ChargeCurrentState_Unknown: + return powctl::ResultInvalidArgument(); + } + + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetFastChargeCurrentLimit(out_ma)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerFastChargeCurrentLimit(IDevice *device, int ma) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetFastChargeCurrentLimit(ma)); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetChargeVoltageLimit(out_mv)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerChargeVoltageLimit(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetChargeVoltageLimit(mv)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + bq24193::ChargerConfiguration bq_cfg; + switch (cfg) { + case ChargerConfiguration_ChargeDisable: bq_cfg = bq24193::ChargerConfiguration_ChargeDisable; break; + case ChargerConfiguration_ChargeBattery: bq_cfg = bq24193::ChargerConfiguration_ChargeBattery; break; + case ChargerConfiguration_Otg: bq_cfg = bq24193::ChargerConfiguration_Otg; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetChargerConfiguration(bq_cfg)); + return ResultSuccess(); + } + + Result ChargerDriver::IsChargerHiZEnabled(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().IsHiZEnabled(out)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerHiZEnabled(IDevice *device, bool en) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetHiZEnabled(en)); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerInputCurrentLimit(int *out_ma, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_ma != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetInputCurrentLimit(out_ma)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerInputCurrentLimit(IDevice *device, int ma) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetInputCurrentLimit(ma)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerInputVoltageLimit(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetInputVoltageLimit(mv)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerBoostModeCurrentLimit(IDevice *device, int ma) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetBoostModeCurrentLimit(ma)); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerChargerStatus(ChargerStatus *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + bq24193::ChargerStatus bq_status; + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetChargerStatus(std::addressof(bq_status))); + + switch (bq_status) { + case bq24193::ChargerStatus_NotCharging: + *out = ChargerStatus_NotCharging; + break; + case bq24193::ChargerStatus_PreCharge: + case bq24193::ChargerStatus_FastCharging: + *out = ChargerStatus_Charging; + break; + case bq24193::ChargerStatus_ChargeTerminationDone: + *out = ChargerStatus_ChargeTerminationDone; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + Result ChargerDriver::IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + *out = device->SafeCastTo().IsWatchdogTimerEnabled(); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerWatchdogTimerEnabled(IDevice *device, bool en) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + auto &charger_device = device->SafeCastTo(); + + if (en) { + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().ResetWatchdogTimer()); + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetWatchdogTimerSetting(charger_device.GetWatchdogTimerTimeout().GetSeconds())); + } else { + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetWatchdogTimerSetting(0)); + } + + charger_device.SetWatchdogTimerEnabled(en); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + device->SafeCastTo().SetWatchdogTimerTimeout(timeout); + return ResultSuccess(); + } + + Result ChargerDriver::ResetChargerWatchdogTimer(IDevice *device) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().ResetWatchdogTimer()); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerBatteryCompensation(int *out_mo, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mo != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetBatteryCompensation(out_mo)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerBatteryCompensation(IDevice *device, int mo) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetBatteryCompensation(mo)); + return ResultSuccess(); + } + + Result ChargerDriver::GetChargerVoltageClamp(int *out_mv, IDevice *device) { + /* Validate arguments. */ + R_UNLESS(out_mv != nullptr, powctl::ResultInvalidArgument()); + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().GetVoltageClamp(out_mv)); + return ResultSuccess(); + } + + Result ChargerDriver::SetChargerVoltageClamp(IDevice *device, int mv) { + /* Validate arguments. */ + R_UNLESS(device != nullptr, powctl::ResultInvalidArgument()); + + AMS_POWCTL_R_TRY_WITH_RETRY(GetBq24193Driver().SetVoltageClamp(mv)); + return ResultSuccess(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.hpp new file mode 100644 index 000000000..9b133e14a --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_charger_driver.hpp @@ -0,0 +1,156 @@ +/* + * 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 . + */ +#pragma once +#include +#include "../../powctl_i_power_control_driver.hpp" +#include "powctl_interrupt_event_handler.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + class ChargerDevice : public powctl::impl::IDevice { + NON_COPYABLE(ChargerDevice); + NON_MOVEABLE(ChargerDevice); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::ChargerDevice, ::ams::powctl::impl::IDevice); + private: + gpio::GpioPadSession gpio_pad_session; + bool watchdog_timer_enabled; + TimeSpan watchdog_timer_timeout; + bool use_event_handler; + std::optional event_handler; + os::SystemEventType system_event; + public: + ChargerDevice(bool ev); + + bool IsWatchdogTimerEnabled() const { return this->watchdog_timer_enabled; } + void SetWatchdogTimerEnabled(bool en) { this->watchdog_timer_enabled = en; } + + TimeSpan GetWatchdogTimerTimeout() const { return this->watchdog_timer_timeout; } + void SetWatchdogTimerTimeout(TimeSpan ts) { this->watchdog_timer_timeout = ts; } + + gpio::GpioPadSession *GetPadSession() { return std::addressof(this->gpio_pad_session); } + + os::SystemEventType *GetSystemEvent() { return std::addressof(this->system_event); } + + void SetInterruptEnabled(bool en) { + if (this->use_event_handler) { + this->event_handler->SetInterruptEnabled(en); + } + } + }; + + class ChargerDriver : public IPowerControlDriver { + NON_COPYABLE(ChargerDriver); + NON_MOVEABLE(ChargerDriver); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::board::nintendo_nx::ChargerDriver, ::ams::powctl::impl::IPowerControlDriver); + public: + ChargerDriver(bool ev) : IPowerControlDriver(ev) { /* ... */ } + + /* Generic API. */ + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) override; + virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) override; + + virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) override; + virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) override; + + /* Charger API. */ + virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) override; + virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) override; + + virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) override; + virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) override; + + virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) override; + virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) override; + + virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) override; + + virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) override; + virtual Result SetChargerHiZEnabled(IDevice *device, bool en) override; + + virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) override; + virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) override; + + virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) override; + + virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) override; + + virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) override; + + virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) override; + virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) override; + + virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) override; + virtual Result ResetChargerWatchdogTimer(IDevice *device) override; + + virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) override; + virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) override; + + virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) override; + virtual Result SetChargerVoltageClamp(IDevice *device, int mv) override; + + /* Unsupported Battery API. */ + virtual Result GetBatterySocRep(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatterySocVf(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryPercentageFullThreshold(IDevice *device, float percentage) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } + + virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) override { return powctl::ResultNotSupported(); } + + virtual Result IsBatteryPresent(bool *out, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryCycles(int *out, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryCycles(IDevice *device, int cycles) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryAge(float *out_percent, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryTemperature(float *out_c, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) override { return powctl::ResultNotSupported(); } + virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryVCell(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + + virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) override { return powctl::ResultNotSupported(); } + + virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) override { return powctl::ResultNotSupported(); } + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.cpp new file mode 100644 index 000000000..360abece6 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.cpp @@ -0,0 +1,31 @@ +/* + * 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 . + */ +#include +#include "powctl_interrupt_event_handler.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + void ChargerInterruptEventHandler::SignalEvent(IDevice *device) { + /* TODO */ + AMS_ABORT(); + } + + void BatteryInterruptEventHandler::SignalEvent(IDevice *device) { + /* TODO */ + AMS_ABORT(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.hpp new file mode 100644 index 000000000..40be3dc32 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_interrupt_event_handler.hpp @@ -0,0 +1,104 @@ +/* + * 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 . + */ +#pragma once +#include +#include "../../powctl_i_power_control_driver.hpp" + +namespace ams::powctl::impl::board::nintendo_nx { + + template + class InterruptEventHandler : public ddsf::IEventHandler { + private: + IDevice *device; + gpio::GpioPadSession gpio_session; + os::SystemEventType gpio_system_event; + os::SdkMutex mutex; + public: + InterruptEventHandler(IDevice *dv) : IEventHandler(), device(dv), mutex() { + /* Initialize the gpio session. */ + Derived::Initialize(std::addressof(this->gpio_session), std::addressof(this->gpio_system_event)); + + /* Initialize ourselves as an event handler. */ + IEventHandler::Initialize(std::addressof(this->gpio_system_event)); + } + + os::SystemEventType *GetSystemEvent() { + return std::addressof(this->gpio_system_event); + } + + void SetInterruptEnabled(bool en) { + std::scoped_lock lk(this->mutex); + + gpio::SetInterruptEnable(std::addressof(this->gpio_session), en); + } + + virtual void HandleEvent() override final { + /* Acquire exclusive access to ourselves. */ + std::scoped_lock lk(this->mutex); + + /* Clear our interrupt status. */ + gpio::ClearInterruptStatus(std::addressof(this->gpio_session)); + + /* Clear our system event. */ + os::ClearSystemEvent(std::addressof(this->gpio_system_event)); + + /* Signal the event. */ + static_cast(this)->SignalEvent(this->device); + } + }; + + class ChargerInterruptEventHandler : public InterruptEventHandler { + friend class InterruptEventHandler; + private: + static void Initialize(gpio::GpioPadSession *session, os::SystemEventType *event) { + /* Open the gpio session. */ + R_ABORT_UNLESS(gpio::OpenSession(session, gpio::DeviceCode_Bq24190Irq)); + + /* Configure the gpio session. */ + gpio::SetDirection(session, gpio::Direction_Input); + gpio::SetInterruptMode(session, gpio::InterruptMode_FallingEdge); + gpio::SetInterruptEnable(session, true); + + /* Bind the interrupt event. */ + R_ABORT_UNLESS(gpio::BindInterrupt(event, session)); + } + + void SignalEvent(IDevice *device); + public: + ChargerInterruptEventHandler(IDevice *dv) : InterruptEventHandler(dv) { /* ... */ } + }; + + class BatteryInterruptEventHandler : public InterruptEventHandler { + friend class InterruptEventHandler; + private: + static void Initialize(gpio::GpioPadSession *session, os::SystemEventType *event) { + /* Open the gpio session. */ + R_ABORT_UNLESS(gpio::OpenSession(session, gpio::DeviceCode_BattMgicIrq)); + + /* Configure the gpio session. */ + gpio::SetDirection(session, gpio::Direction_Input); + gpio::SetInterruptMode(session, gpio::InterruptMode_LowLevel); + + /* Bind the interrupt event. */ + R_ABORT_UNLESS(gpio::BindInterrupt(event, session)); + } + + void SignalEvent(IDevice *device); + public: + BatteryInterruptEventHandler(IDevice *dv) : InterruptEventHandler(dv) { /* ... */ } + }; + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc new file mode 100644 index 000000000..03c5a02f5 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_custom_parameters.inc @@ -0,0 +1,200 @@ +/* + * 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 . + */ + +/* NOTE: This file is auto-generated by max17050_parameters_gen.py, do not edit manually. */ + +constexpr inline const CustomParameters CustomParameters0A = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0053, + .tempco = 0x1C22, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5786, + .qresidual10 = 0x3184, + .qresidual20 = 0x1E00, + .qresidual30 = 0x1502, + .fullcap = 0x2476, + .vffullcap = 0x2476, + .modeltbl = { + 0x9FF0, 0xAD30, 0xB5D0, 0xB9C0, 0xBAD0, 0xBBE0, 0xBC30, 0xBC90, + 0xBCE0, 0xBD40, 0xBE70, 0xC0E0, 0xC4E0, 0xC890, 0xCC90, 0xD0F0, + 0x0170, 0x0480, 0x0590, 0x0BE0, 0x0A00, 0x3C00, 0x3810, 0x3A00, + 0x3A30, 0x19F0, 0x0EF0, 0x0AF0, 0x0BD0, 0x07F0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters0R = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0048, + .tempco = 0x2034, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5A00, + .qresidual10 = 0x3B00, + .qresidual20 = 0x0F80, + .qresidual30 = 0x0B02, + .fullcap = 0x2466, + .vffullcap = 0x2466, + .modeltbl = { + 0x9C50, 0xAD90, 0xB270, 0xB6A0, 0xB8F0, 0xBB10, 0xBC00, 0xBD00, + 0xBD70, 0xBE70, 0xBF50, 0xC1F0, 0xC380, 0xC590, 0xC8E0, 0xD0B0, + 0x00D0, 0x0150, 0x0300, 0x0D00, 0x0E00, 0x1900, 0x2AC0, 0x2830, + 0x1760, 0x18F0, 0x0DF0, 0x0BC0, 0x0DF0, 0x0BF0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters0M = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0085, + .tempco = 0x1625, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x3100, + .qresidual10 = 0x1B00, + .qresidual20 = 0x1000, + .qresidual30 = 0x0C81, + .fullcap = 0x227A, + .vffullcap = 0x227A, + .modeltbl = { + 0xA340, 0xB840, 0xB900, 0xBB70, 0xBC90, 0xBD20, 0xBDC0, 0xBEA0, + 0xBF70, 0xC030, 0xC210, 0xC3F0, 0xC800, 0xC9E0, 0xCCA0, 0xD090, + 0x0160, 0x3800, 0x0800, 0x1E00, 0x2550, 0x3060, 0x15D0, 0x1810, + 0x1490, 0x0B80, 0x0BF0, 0x0AF0, 0x0CB0, 0x06F0, 0x09D0, 0x09D0, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1D2A, +}; + +constexpr inline const CustomParameters CustomParameters1 = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0040, + .tempco = 0x1624, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4690, + .qresidual10 = 0x2605, + .qresidual20 = 0x1605, + .qresidual30 = 0x0F05, + .fullcap = 0x1AE4, + .vffullcap = 0x1AE4, + .modeltbl = { + 0x8B50, 0x9C20, 0xACF0, 0xB160, 0xB3A0, 0xB5B0, 0xB950, 0xBBE0, + 0xBDC0, 0xBEF0, 0xC140, 0xC250, 0xC600, 0xC960, 0xCCE0, 0xD060, + 0x0070, 0x00F0, 0x0440, 0x0400, 0x0500, 0x0400, 0x0D00, 0x3270, + 0x0FB0, 0x0AF0, 0x10F0, 0x0CE0, 0x09E0, 0x07F0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5F00, + .iavgempty = 0x1584, +}; + +constexpr inline const CustomParameters CustomParameters2A = { + .relaxcfg = 0x203B, + .rcomp0 = 0x004A, + .tempco = 0x1D23, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4000, + .qresidual10 = 0x1E80, + .qresidual20 = 0x0D83, + .qresidual30 = 0x0783, + .fullcap = 0x1C20, + .vffullcap = 0x1C20, + .modeltbl = { + 0x8040, 0x9A30, 0xB430, 0xB770, 0xBAB0, 0xBBC0, 0xBD00, 0xBE50, + 0xBF70, 0xC0D0, 0xC300, 0xC590, 0xC960, 0xCD40, 0xD1F0, 0xD5C0, + 0x0040, 0x0060, 0x0510, 0x0D30, 0x16C0, 0x2160, 0x1380, 0x1A10, + 0x0EC0, 0x0CE0, 0x08F0, 0x0940, 0x0920, 0x06F0, 0x06C0, 0x06C0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x1680, +}; + +constexpr inline const CustomParameters CustomParameters2R = { + .relaxcfg = 0x203B, + .rcomp0 = 0x004C, + .tempco = 0x2D32, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x5900, + .qresidual10 = 0x2900, + .qresidual20 = 0x1100, + .qresidual30 = 0x0B00, + .fullcap = 0x1CCE, + .vffullcap = 0x1CCE, + .modeltbl = { + 0x8E10, 0x9FC0, 0xA880, 0xB750, 0xBA10, 0xBB30, 0xBD20, 0xBE80, + 0xC0A0, 0xC350, 0xC670, 0xC8C0, 0xCCF0, 0xD050, 0xD140, 0xD5F0, + 0x0020, 0x00D0, 0x0200, 0x0E00, 0x1300, 0x1B00, 0x1930, 0x1150, + 0x0BF0, 0x07E0, 0x0AD0, 0x06F0, 0x07F0, 0x0EF0, 0x04F0, 0x04F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x170B, +}; + +constexpr inline const CustomParameters CustomParameters2M = { + .relaxcfg = 0x203B, + .rcomp0 = 0x0049, + .tempco = 0x222A, + .ichgterm = 0x0333, + .tgain = 0xE1F6, + .toff = 0x2BF2, + .vempty = 0xA05F, + .qresidual00 = 0x4F00, + .qresidual10 = 0x2680, + .qresidual20 = 0x1205, + .qresidual30 = 0x0C87, + .fullcap = 0x1C68, + .vffullcap = 0x1C68, + .modeltbl = { + 0x8E40, 0xB570, 0xB8F0, 0xBB00, 0xBC20, 0xBCC0, 0xBE30, 0xBFE0, + 0xC200, 0xC400, 0xC720, 0xCB50, 0xCF00, 0xD100, 0xD480, 0xD5C0, + 0x00C0, 0x0C00, 0x0A10, 0x1800, 0x2C00, 0x1C10, 0x12D0, 0x09F0, + 0x0AF0, 0x0850, 0x09F0, 0x06F0, 0x06B0, 0x07E0, 0x01D0, 0x01D0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + .fullsocthr = 0x5500, + .iavgempty = 0x16B9, +}; + diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp new file mode 100644 index 000000000..424c65b59 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.cpp @@ -0,0 +1,744 @@ +/* + * 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 . + */ +#include +#include "powctl_max17050_driver.hpp" + +#if defined(ATMOSPHERE_ARCH_ARM64) +#include +#endif + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace max17050 { + + constexpr inline u8 Status = 0x00; + constexpr inline u8 VAlrtThreshold = 0x01; + constexpr inline u8 TAlrtThreshold = 0x02; + constexpr inline u8 SocAlrtThreshold = 0x03; + constexpr inline u8 AtRate = 0x04; + constexpr inline u8 RemCapRep = 0x05; + constexpr inline u8 SocRep = 0x06; + constexpr inline u8 Age = 0x07; + constexpr inline u8 Temperature = 0x08; + constexpr inline u8 VCell = 0x09; + constexpr inline u8 Current = 0x0A; + constexpr inline u8 AverageCurrent = 0x0B; + + constexpr inline u8 SocMix = 0x0D; + constexpr inline u8 SocAv = 0x0E; + constexpr inline u8 RemCapMix = 0x0F; + constexpr inline u8 FullCap = 0x10; + constexpr inline u8 Tte = 0x11; + constexpr inline u8 QResidual00 = 0x12; + constexpr inline u8 FullSocThr = 0x13; + + + constexpr inline u8 AverageTemp = 0x16; + constexpr inline u8 Cycles = 0x17; + constexpr inline u8 DesignCap = 0x18; + constexpr inline u8 AverageVCell = 0x19; + constexpr inline u8 MaxMinTemp = 0x1A; + constexpr inline u8 MaxMinVoltage = 0x1B; + constexpr inline u8 MaxMinCurrent = 0x1C; + constexpr inline u8 Config = 0x1D; + constexpr inline u8 IChgTerm = 0x1E; + constexpr inline u8 RemCapAv = 0x1F; + + constexpr inline u8 Version = 0x21; + constexpr inline u8 QResidual10 = 0x22; + constexpr inline u8 FullCapNom = 0x23; + constexpr inline u8 TempNom = 0x24; + constexpr inline u8 TempLim = 0x25; + + constexpr inline u8 Ain = 0x27; + constexpr inline u8 LearnCfg = 0x28; + constexpr inline u8 FilterCfg = 0x29; + constexpr inline u8 RelaxCfg = 0x2A; + constexpr inline u8 MiscCfg = 0x2B; + constexpr inline u8 TGain = 0x2C; + constexpr inline u8 TOff = 0x2D; + constexpr inline u8 CGain = 0x2E; + constexpr inline u8 COff = 0x2F; + + + constexpr inline u8 QResidual20 = 0x32; + + + constexpr inline u8 FullCap0 = 0x35; + constexpr inline u8 IAvgEmpty = 0x36; + constexpr inline u8 FCtc = 0x37; + constexpr inline u8 RComp0 = 0x38; + constexpr inline u8 TempCo = 0x39; + constexpr inline u8 VEmpty = 0x3A; + + + constexpr inline u8 FStat = 0x3D; + constexpr inline u8 Timer = 0x3E; + constexpr inline u8 ShdnTimer = 0x3F; + + + constexpr inline u8 QResidual30 = 0x42; + + + constexpr inline u8 DQAcc = 0x45; + constexpr inline u8 DPAcc = 0x46; + + constexpr inline u8 SocVf0 = 0x48; + + constexpr inline u8 Qh0 = 0x4C; + constexpr inline u8 Qh = 0x4D; + + constexpr inline u8 SocVfAccess = 0x60; + + constexpr inline u8 ModelAccess0 = 0x62; + constexpr inline u8 ModelAccess1 = 0x63; + + constexpr inline u8 ModelChrTblStart = 0x80; + constexpr inline u8 ModelChrTblEnd = 0xB0; + + + constexpr inline u8 VFocV = 0xFB; + constexpr inline u8 SocVf = 0xFF; + + constexpr inline size_t ModelChrTblSize = ModelChrTblEnd - ModelChrTblStart; + + namespace { + + struct CustomParameters { + u16 relaxcfg; + u16 rcomp0; + u16 tempco; + u16 ichgterm; + u16 tgain; + u16 toff; + u16 vempty; + u16 qresidual00; + u16 qresidual10; + u16 qresidual20; + u16 qresidual30; + u16 fullcap; + u16 vffullcap; + u16 modeltbl[ModelChrTblSize]; + u16 fullsocthr; + u16 iavgempty; + }; + + #include "powctl_max17050_custom_parameters.inc" + + const CustomParameters &GetCustomParameters(const char *battery_vendor, u8 battery_version) { + if (battery_version == 2) { + if (battery_vendor[7] == 'M') { + return CustomParameters2M; + } else if (battery_vendor[7] == 'R') { + return CustomParameters2R; + } else /* if (battery_vendor[7] == 'A') */ { + return CustomParameters2A; + } + } else if (battery_version == 1) { + return CustomParameters1; + } else /* if (battery_version == 0) */ { + if (battery_vendor[7] == 'M') { + return CustomParameters0M; + } else if (battery_vendor[7] == 'R') { + return CustomParameters0R; + } else /* if (battery_vendor[7] == 'A') */ { + return CustomParameters0A; + } + } + } + + } + + } + + namespace { + + ALWAYS_INLINE Result ReadWriteRegister(const i2c::I2cSession &session, u8 address, u16 mask, u16 value) { + /* Read the current value. */ + u16 cur_val; + R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); + + /* Update the value. */ + const u16 new_val = (cur_val & ~mask) | (value & mask); + R_TRY(i2c::WriteSingleRegister(session, address, new_val)); + + return ResultSuccess(); + } + + ALWAYS_INLINE Result ReadRegister(const i2c::I2cSession &session, u8 address, u16 *out) { + return i2c::ReadSingleRegister(session, address, out); + } + + ALWAYS_INLINE Result WriteRegister(const i2c::I2cSession &session, u8 address, u16 val) { + return i2c::WriteSingleRegister(session, address, val); + } + + ALWAYS_INLINE bool WriteValidateRegister(const i2c::I2cSession &session, u8 address, u16 val) { + /* Write the value. */ + R_ABORT_UNLESS(WriteRegister(session, address, val)); + + /* Give it time to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(3)); + + /* Read it back. */ + u16 new_val; + R_ABORT_UNLESS(ReadRegister(session, address, std::addressof(new_val))); + + return new_val == val; + } + + ALWAYS_INLINE Result ReadWriteValidateRegister(const i2c::I2cSession &session, u8 address, u16 mask, u16 value) { + /* Read the current value. */ + u16 cur_val; + R_TRY(i2c::ReadSingleRegister(session, address, std::addressof(cur_val))); + + /* Update the value. */ + const u16 new_val = (cur_val & ~mask) | (value & mask); + while (!WriteValidateRegister(session, address, new_val)) { /* ... */ } + + return ResultSuccess(); + } + + double CoerceToDouble(u64 value) { + static_assert(sizeof(value) == sizeof(double)); + + double d; + __builtin_memcpy(std::addressof(d), std::addressof(value), sizeof(d)); + return d; + } + + double ExponentiateTwoToPower(s16 exponent, double scale) { + if (exponent >= 1024) { + exponent = exponent - 1023; + scale = scale * 8.98846567e307; + if (exponent >= 1024) { + exponent = std::min(exponent, 2046) - 1023; + scale = scale * 8.98846567e307; + } + } else if (exponent <= -1023) { + exponent = exponent + 969; + scale = scale * 2.00416836e-292; + if (exponent <= -1023) { + exponent = std::max(exponent, -1991) + 969; + scale = scale * 2.00416836e-292; + } + } + return scale * CoerceToDouble(static_cast(exponent + 1023) << 52); + } + + } + + Result Max17050Driver::InitializeSession(const char *battery_vendor, u8 battery_version) { + /* Get the custom parameters. */ + const auto ¶ms = max17050::GetCustomParameters(battery_vendor, battery_version); + + /* We only want to write the parameters on power on reset. */ + R_SUCCEED_IF(!this->IsPowerOnReset()); + + /* Set that we need to restore parameters. */ + R_TRY(this->SetNeedToRestoreParameters(true)); + + /* Wait for our configuration to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(500)); + + /* Write initial config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::Config, 0x7210)); + + /* Write initial filter config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::FilterCfg, 0x8784)); + + /* Write relax config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::RelaxCfg, params.relaxcfg)); + + /* Write initial learn config. */ + R_TRY(WriteRegister(this->i2c_session, max17050::LearnCfg, 0x2603)); + + /* Write fullsocthr. */ + R_TRY(WriteRegister(this->i2c_session, max17050::FullSocThr, params.fullsocthr)); + + /* Write iavgempty. */ + R_TRY(WriteRegister(this->i2c_session, max17050::IAvgEmpty, params.iavgempty)); + + /* Unlock model table, write model table. */ + do { + R_TRY(this->UnlockModelTable()); + R_TRY(this->SetModelTable(params.modeltbl)); + } while (!this->IsModelTableSet(params.modeltbl)); + + /* Lock the model table, trying up to ten times. */ + { + size_t i = 0; + while (true) { + ++i; + + R_TRY(this->LockModelTable()); + + if (this->IsModelTableLocked()) { + break; + } + + R_SUCCEED_IF(i >= 10); + } + } + + /* Write and validate rcomp0 */ + while (!WriteValidateRegister(this->i2c_session, max17050::RComp0, params.rcomp0)) { /* ... */ } + + /* Write and validate tempco */ + while (!WriteValidateRegister(this->i2c_session, max17050::TempCo, params.tempco)) { /* ... */ } + + /* Write ichgterm. */ + R_TRY(WriteRegister(this->i2c_session, max17050::IChgTerm, params.ichgterm)); + + /* Write tgain. */ + R_TRY(WriteRegister(this->i2c_session, max17050::TGain, params.tgain)); + + /* Write toff. */ + R_TRY(WriteRegister(this->i2c_session, max17050::TOff, params.toff)); + + /* Write and validate vempty. */ + while (!WriteValidateRegister(this->i2c_session, max17050::VEmpty, params.vempty)) { /* ... */ } + + /* Write and validate qresidual. */ + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual00, params.qresidual00)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual10, params.qresidual10)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual20, params.qresidual20)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual30, params.qresidual30)) { /* ... */ } + + /* Write capacity parameters. */ + while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ } + R_TRY(WriteRegister(this->i2c_session, max17050::DesignCap, params.vffullcap)); + while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ } + + /* Give some time for configuration to take. */ + os::SleepThread(TimeSpan::FromMilliSeconds(350)); + + /* Write vfsoc to vfsoc0, qh, to qh0. */ + u16 vfsoc, qh; + { + R_TRY(ReadRegister(this->i2c_session, max17050::SocVf, std::addressof(vfsoc))); + R_TRY(this->UnlockVfSoc()); + while (!WriteValidateRegister(this->i2c_session, max17050::SocVf0, vfsoc)) { /* ... */ } + R_TRY(ReadRegister(this->i2c_session, max17050::Qh, std::addressof(qh))); + R_TRY(WriteRegister(this->i2c_session, max17050::Qh0, qh)); + R_TRY(this->LockVfSoc()); + } + + /* Reset cycles. */ + while (!WriteValidateRegister(this->i2c_session, max17050::Cycles, 0x0060)) { /* ... */ } + + /* Load new capacity parameters. */ + const u16 remcap = static_cast((vfsoc * params.vffullcap) / 0x6400); + const u16 repcap = static_cast(remcap * (params.fullcap / params.vffullcap)); + const u16 dpacc = 0x0C80; + const u16 dqacc = params.vffullcap / 0x10; + while (!WriteValidateRegister(this->i2c_session, max17050::RemCapMix, remcap)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::RemCapRep, repcap)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DPAcc, dpacc)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DQAcc, dqacc)) { /* ... */ } + + /* Write capacity parameters. */ + while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, params.fullcap)) { /* ... */ } + R_TRY(WriteRegister(this->i2c_session, max17050::DesignCap, params.vffullcap)); + while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, params.vffullcap)) { /* ... */ } + + /* Write soc rep. */ + R_TRY(WriteRegister(this->i2c_session, max17050::SocRep, vfsoc)); + + /* Clear power on reset. */ + R_TRY(ReadWriteValidateRegister(this->i2c_session, max17050::Status, 0x0002, 0x0000)); + + /* Set cgain. */ + R_TRY(WriteRegister(this->i2c_session, max17050::CGain, 0x7FFF)); + + return ResultSuccess(); + } + + Result Max17050Driver::SetMaximumShutdownTimerThreshold() { + return WriteRegister(this->i2c_session, max17050::ShdnTimer, 0xE000); + } + + bool Max17050Driver::IsPowerOnReset() { + /* Get the register. */ + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::Status, std::addressof(val))); + + /* Extract the value. */ + return (val & 0x0002) != 0; + } + + Result Max17050Driver::LockVfSoc() { + return WriteRegister(this->i2c_session, max17050::SocVfAccess, 0x0000); + } + + Result Max17050Driver::UnlockVfSoc() { + return WriteRegister(this->i2c_session, max17050::SocVfAccess, 0x0080); + } + + Result Max17050Driver::LockModelTable() { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess0, 0x0000)); + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess1, 0x0000)); + return ResultSuccess(); + } + + Result Max17050Driver::UnlockModelTable() { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess0, 0x0059)); + R_TRY(WriteRegister(this->i2c_session, max17050::ModelAccess1, 0x00C4)); + return ResultSuccess(); + } + + bool Max17050Driver::IsModelTableLocked() { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::ModelChrTblStart + i, std::addressof(val))); + + if (val != 0) { + return false; + } + } + + return true; + } + + Result Max17050Driver::SetModelTable(const u16 *model_table) { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + R_TRY(WriteRegister(this->i2c_session, max17050::ModelChrTblStart + i, model_table[i])); + } + + return ResultSuccess(); + } + + bool Max17050Driver::IsModelTableSet(const u16 *model_table) { + for (size_t i = 0; i < max17050::ModelChrTblSize; ++i) { + u16 val; + R_ABORT_UNLESS(ReadRegister(this->i2c_session, max17050::ModelChrTblStart + i, std::addressof(val))); + + if (val != model_table[i]) { + return false; + } + } + + return true; + } + + Result Max17050Driver::ReadInternalState() { + R_TRY(ReadRegister(this->i2c_session, max17050::RComp0, std::addressof(this->internal_state.rcomp0))); + R_TRY(ReadRegister(this->i2c_session, max17050::TempCo, std::addressof(this->internal_state.tempco))); + R_TRY(ReadRegister(this->i2c_session, max17050::FullCap, std::addressof(this->internal_state.fullcap))); + R_TRY(ReadRegister(this->i2c_session, max17050::Cycles, std::addressof(this->internal_state.cycles))); + R_TRY(ReadRegister(this->i2c_session, max17050::FullCapNom, std::addressof(this->internal_state.fullcapnom))); + R_TRY(ReadRegister(this->i2c_session, max17050::IAvgEmpty, std::addressof(this->internal_state.iavgempty))); + R_TRY(ReadRegister(this->i2c_session, max17050::QResidual00, std::addressof(this->internal_state.qresidual00))); + R_TRY(ReadRegister(this->i2c_session, max17050::QResidual10, std::addressof(this->internal_state.qresidual10))); + R_TRY(ReadRegister(this->i2c_session, max17050::QResidual20, std::addressof(this->internal_state.qresidual20))); + R_TRY(ReadRegister(this->i2c_session, max17050::QResidual30, std::addressof(this->internal_state.qresidual30))); + return ResultSuccess(); + } + + Result Max17050Driver::WriteInternalState() { + while (!WriteValidateRegister(this->i2c_session, max17050::RComp0, this->internal_state.rcomp0)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::TempCo, this->internal_state.tempco)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::FullCapNom, this->internal_state.fullcapnom)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::IAvgEmpty, this->internal_state.iavgempty)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual00, this->internal_state.qresidual00)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual10, this->internal_state.qresidual10)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual20, this->internal_state.qresidual20)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::QResidual30, this->internal_state.qresidual30)) { /* ... */ } + + os::SleepThread(TimeSpan::FromMilliSeconds(350)); + + u16 fullcap0, socmix; + R_TRY(ReadRegister(this->i2c_session, max17050::FullCap0, std::addressof(fullcap0))); + R_TRY(ReadRegister(this->i2c_session, max17050::SocMix, std::addressof(socmix))); + + while (!WriteValidateRegister(this->i2c_session, max17050::RemCapMix, static_cast((fullcap0 * socmix) / 0x6400))) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::FullCap, this->internal_state.fullcap)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DPAcc, 0x0C80)) { /* ... */ } + while (!WriteValidateRegister(this->i2c_session, max17050::DQAcc, this->internal_state.fullcapnom / 0x10)) { /* ... */ } + + os::SleepThread(TimeSpan::FromMilliSeconds(350)); + + while (!WriteValidateRegister(this->i2c_session, max17050::Cycles, this->internal_state.cycles)) { /* ... */ } + if (this->internal_state.cycles >= 0x100) { + while (!WriteValidateRegister(this->i2c_session, max17050::LearnCfg, 0x2673)) { /* ... */ } + } + + return ResultSuccess(); + } + + Result Max17050Driver::GetSocRep(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::SocRep, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val) * 0.00390625; + return ResultSuccess(); + } + + Result Max17050Driver::GetSocVf(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::SocVf, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val) * 0.00390625; + return ResultSuccess(); + } + + Result Max17050Driver::GetFullCapacity(double *out, double sense_resistor) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(sense_resistor > 0.0); + + /* Read the values. */ + u16 cgain, fullcap; + R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); + R_TRY(ReadRegister(this->i2c_session, max17050::FullCap, std::addressof(fullcap))); + + /* Set output. */ + *out = ((static_cast(fullcap) * 0.005) / sense_resistor) / (static_cast(cgain) * 0.0000610351562); + return ResultSuccess(); + } + + Result Max17050Driver::GetRemainingCapacity(double *out, double sense_resistor) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(sense_resistor > 0.0); + + /* Read the values. */ + u16 cgain, remcap; + R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); + R_TRY(ReadRegister(this->i2c_session, max17050::RemCapRep, std::addressof(remcap))); + + /* Set output. */ + *out = ((static_cast(remcap) * 0.005) / sense_resistor) / (static_cast(cgain) * 0.0000610351562); + return ResultSuccess(); + } + + Result Max17050Driver::SetPercentageMinimumAlertThreshold(int percentage) { + return ReadWriteRegister(this->i2c_session, max17050::SocAlrtThreshold, 0x00FF, static_cast(percentage)); + } + + Result Max17050Driver::SetPercentageMaximumAlertThreshold(int percentage) { + return ReadWriteRegister(this->i2c_session, max17050::SocAlrtThreshold, 0xFF00, static_cast(static_cast(percentage)) << 8); + } + + Result Max17050Driver::SetPercentageFullThreshold(double percentage) { + #if defined(ATMOSPHERE_ARCH_ARM64) + const u16 val = vcvtd_n_s64_f64(percentage, BITSIZEOF(u8)); + #else + #error "Unknown architecture for floating point -> fixed point" + #endif + + return WriteRegister(this->i2c_session, max17050::FullSocThr, val); + } + + Result Max17050Driver::GetAverageCurrent(double *out, double sense_resistor) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(sense_resistor > 0.0); + + /* Read the values. */ + u16 cgain, coff, avg_current; + R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); + R_TRY(ReadRegister(this->i2c_session, max17050::COff, std::addressof(coff))); + R_TRY(ReadRegister(this->i2c_session, max17050::AverageCurrent, std::addressof(avg_current))); + + /* Set output. */ + *out = (((static_cast(avg_current) - (static_cast(coff) + static_cast(coff))) / (static_cast(cgain) * 0.0000610351562)) * 1.5625) / (sense_resistor * 1000.0); + return ResultSuccess(); + } + + Result Max17050Driver::GetCurrent(double *out, double sense_resistor) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + AMS_ABORT_UNLESS(sense_resistor > 0.0); + + /* Read the values. */ + u16 cgain, coff, current; + R_TRY(ReadRegister(this->i2c_session, max17050::CGain, std::addressof(cgain))); + R_TRY(ReadRegister(this->i2c_session, max17050::COff, std::addressof(coff))); + R_TRY(ReadRegister(this->i2c_session, max17050::Current, std::addressof(current))); + + /* Set output. */ + *out = (((static_cast(current) - (static_cast(coff) + static_cast(coff))) / (static_cast(cgain) * 0.0000610351562)) * 1.5625) / (sense_resistor * 1000.0); + return ResultSuccess(); + } + + Result Max17050Driver::GetNeedToRestoreParameters(bool *out) { + /* Get the register. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::MiscCfg, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x8000) != 0; + return ResultSuccess(); + } + + Result Max17050Driver::SetNeedToRestoreParameters(bool en) { + return ReadWriteRegister(this->i2c_session, max17050::MiscCfg, 0x8000, en ? 0x8000 : 0); + } + + Result Max17050Driver::IsI2cShutdownEnabled(bool *out) { + /* Get the register. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::Config, std::addressof(val))); + + /* Extract the value. */ + *out = (val & 0x0040) != 0; + return ResultSuccess(); + } + + Result Max17050Driver::SetI2cShutdownEnabled(bool en) { + return ReadWriteRegister(this->i2c_session, max17050::Config, 0x0040, en ? 0x0040 : 0); + } + + Result Max17050Driver::GetStatus(u16 *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + return ReadRegister(this->i2c_session, max17050::Status, out); + } + + Result Max17050Driver::GetCycles(u16 *out) { + /* Get the register. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::Cycles, std::addressof(val))); + + /* Extract the value. */ + *out = std::max(val, 0x60) - 0x60; + return ResultSuccess(); + } + + Result Max17050Driver::ResetCycles() { + return WriteRegister(this->i2c_session, max17050::Cycles, 0x0060); + } + + Result Max17050Driver::GetAge(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::Age, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val) * 0.00390625; + return ResultSuccess(); + } + + Result Max17050Driver::GetTemperature(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::Temperature, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val) * 0.00390625; + return ResultSuccess(); + } + + Result Max17050Driver::GetMaximumTemperature(u8 *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::MaxMinTemp, std::addressof(val))); + + /* Set output. */ + *out = static_cast(val >> 8); + return ResultSuccess(); + } + + Result Max17050Driver::SetTemperatureMinimumAlertThreshold(int c) { + return ReadWriteRegister(this->i2c_session, max17050::TAlrtThreshold, 0x00FF, static_cast(c)); + } + + Result Max17050Driver::SetTemperatureMaximumAlertThreshold(int c) { + return ReadWriteRegister(this->i2c_session, max17050::TAlrtThreshold, 0xFF00, static_cast(static_cast(c)) << 8); + } + + Result Max17050Driver::GetVCell(int *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::VCell, std::addressof(val))); + + /* Set output. */ + *out = (625 * (val >> 3)) / 1000; + return ResultSuccess(); + } + + Result Max17050Driver::GetAverageVCell(int *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::AverageVCell, std::addressof(val))); + + /* Set output. */ + *out = (625 * (val >> 3)) / 1000; + return ResultSuccess(); + } + + Result Max17050Driver::GetAverageVCellTime(double *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::FilterCfg, std::addressof(val))); + + /* Set output. */ + *out = 175.8 * ExponentiateTwoToPower(6 + ((val >> 4) & 7), 1.0); + return ResultSuccess(); + } + + Result Max17050Driver::GetOpenCircuitVoltage(int *out) { + /* Validate parameters. */ + AMS_ABORT_UNLESS(out != nullptr); + + /* Read the value. */ + u16 val; + R_TRY(ReadRegister(this->i2c_session, max17050::VFocV, std::addressof(val))); + + /* Set output. */ + *out = (1250 * (val >> 4)) / 1000; + return ResultSuccess(); + } + + Result Max17050Driver::SetVoltageMinimumAlertThreshold(int mv) { + return ReadWriteRegister(this->i2c_session, max17050::VAlrtThreshold, 0x00FF, static_cast(util::DivideUp(mv, 20))); + } + + Result Max17050Driver::SetVoltageMaximumAlertThreshold(int mv) { + return ReadWriteRegister(this->i2c_session, max17050::VAlrtThreshold, 0xFF00, static_cast(static_cast(mv / 20)) << 8); + } + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp new file mode 100644 index 000000000..7d01e30f2 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_max17050_driver.hpp @@ -0,0 +1,144 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::powctl::impl::board::nintendo_nx { + + namespace max17050 { + + struct InternalState { + u16 rcomp0; + u16 tempco; + u16 fullcap; + u16 cycles; + u16 fullcapnom; + u16 iavgempty; + u16 qresidual00; + u16 qresidual10; + u16 qresidual20; + u16 qresidual30; + }; + + } + + class Max17050Driver { + private: + os::SdkMutex mutex; + int init_count; + i2c::I2cSession i2c_session; + max17050::InternalState internal_state; + private: + Result InitializeSession(const char *battery_vendor, u8 battery_version); + Result SetMaximumShutdownTimerThreshold(); + + bool IsPowerOnReset(); + Result LockVfSoc(); + Result UnlockVfSoc(); + Result LockModelTable(); + Result UnlockModelTable(); + bool IsModelTableLocked(); + Result SetModelTable(const u16 *model_table); + bool IsModelTableSet(const u16 *model_table); + public: + Max17050Driver() : mutex(), init_count(0), i2c_session(), internal_state() { + /* ... */ + } + + void Initialize(const char *battery_vendor, u8 battery_version) { + std::scoped_lock lk(this->mutex); + if ((this->init_count++) == 0) { + /* Initialize i2c library. */ + i2c::InitializeEmpty(); + + /* Open session. */ + R_ABORT_UNLESS(i2c::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max17050)); + + /* Initialize session. */ + R_ABORT_UNLESS(this->InitializeSession(battery_vendor, battery_version)); + + /* Set shutdown timer threshold to the maximum value. */ + R_ABORT_UNLESS(this->SetMaximumShutdownTimerThreshold()); + } + } + + void Finalize() { + std::scoped_lock lk(this->mutex); + if ((--this->init_count) == 0) { + /* Close session. */ + i2c::CloseSession(this->i2c_session); + + /* Finalize i2c library. */ + i2c::Finalize(); + } + } + + Result ReadInternalState(); + Result WriteInternalState(); + + void GetInternalState(max17050::InternalState *dst) { + *dst = this->internal_state; + } + + void SetInternalState(const max17050::InternalState &src) { + this->internal_state = src; + } + + Result GetSocRep(double *out); + Result GetSocVf(double *out); + + Result GetFullCapacity(double *out, double sense_resistor); + Result GetRemainingCapacity(double *out, double sense_resistor); + + Result SetPercentageMinimumAlertThreshold(int percentage); + Result SetPercentageMaximumAlertThreshold(int percentage); + + Result SetPercentageFullThreshold(double percentage); + + Result GetAverageCurrent(double *out, double sense_resistor); + Result GetCurrent(double *out, double sense_resistor); + + Result GetNeedToRestoreParameters(bool *out); + Result SetNeedToRestoreParameters(bool en); + + Result IsI2cShutdownEnabled(bool *out); + Result SetI2cShutdownEnabled(bool en); + + Result GetStatus(u16 *out); + + Result GetCycles(u16 *out); + Result ResetCycles(); + + Result GetAge(double *out); + + Result GetTemperature(double *out); + + Result GetMaximumTemperature(u8 *out); + + Result SetTemperatureMinimumAlertThreshold(int c); + Result SetTemperatureMaximumAlertThreshold(int c); + + Result GetVCell(int *out); + Result GetAverageVCell(int *out); + Result GetAverageVCellTime(double *out); + + Result GetOpenCircuitVoltage(int *out); + + Result SetVoltageMinimumAlertThreshold(int mv); + Result SetVoltageMaximumAlertThreshold(int mv); + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_retry_helper.hpp b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_retry_helper.hpp new file mode 100644 index 000000000..d43109976 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/board/nintendo_nx/powctl_retry_helper.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::powctl::impl { + + constexpr inline const TimeSpan PowerControlRetryTimeout = TimeSpan::FromSeconds(10); + constexpr inline const TimeSpan PowerControlRetryInterval = TimeSpan::FromMilliSeconds(20); + + #define AMS_POWCTL_R_TRY_WITH_RETRY(__EXPR__) \ + ({ \ + TimeSpan __powctl_retry_current_time = 0; \ + while (true) { \ + const Result __powctl_retry_result = (__EXPR__); \ + if (R_SUCCEEDED(__powctl_retry_result)) { \ + break; \ + } \ + \ + __powctl_retry_current_time += PowerControlRetryInterval; \ + R_UNLESS(__powctl_retry_current_time < PowerControlRetryTimeout, __powctl_retry_result); \ + \ + os::SleepThread(PowerControlRetryInterval); \ + } \ + }) + + +} diff --git a/libraries/libstratosphere/source/powctl/impl/powctl_device_management.cpp b/libraries/libstratosphere/source/powctl/impl/powctl_device_management.cpp new file mode 100644 index 000000000..cb75f5337 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/powctl_device_management.cpp @@ -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 . + */ +#include +#include "powctl_device_management.hpp" + +namespace ams::powctl::impl { + + namespace { + + os::ThreadType g_interrupt_thread; + + constexpr inline size_t InterruptThreadStackSize = os::MemoryPageSize; + alignas(os::MemoryPageSize) u8 g_interrupt_thread_stack[InterruptThreadStackSize]; + + constinit u8 g_unit_heap_memory[2_KB]; + constinit lmem::HeapHandle g_unit_heap_handle; + constinit sf::UnitHeapMemoryResource g_unit_heap_memory_resource; + + IPowerControlDriver::List &GetDriverList() { + static IPowerControlDriver::List s_driver_list; + return s_driver_list; + } + + ddsf::EventHandlerManager &GetInterruptHandlerManager() { + static ddsf::EventHandlerManager s_interrupt_handler_manager; + return s_interrupt_handler_manager; + } + + ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() { + static ddsf::DeviceCodeEntryManager s_device_code_entry_manager = [] { + /* Initialize the entry code heap. */ + g_unit_heap_handle = lmem::CreateUnitHeap(g_unit_heap_memory, sizeof(g_unit_heap_memory), sizeof(ddsf::DeviceCodeEntryHolder), lmem::CreateOption_ThreadSafe); + + /* Initialize the entry code memory resource. */ + g_unit_heap_memory_resource.Attach(g_unit_heap_handle); + + /* Make the entry manager using the newly initialized memory resource. */ + return ddsf::DeviceCodeEntryManager(std::addressof(g_unit_heap_memory_resource)); + }(); + + return s_device_code_entry_manager; + } + + void InterruptThreadFunction(void *arg) { + AMS_UNUSED(arg); + GetInterruptHandlerManager().LoopAuto(); + } + + } + + void InitializeDrivers() { + /* Ensure the event handler manager is initialized. */ + GetInterruptHandlerManager().Initialize(); + + /* Initialize all registered drivers. */ + for (auto &driver : GetDriverList()) { + driver.SafeCastTo().InitializeDriver(); + } + + /* Create the interrupt thread. */ + R_ABORT_UNLESS(os::CreateThread(std::addressof(g_interrupt_thread), InterruptThreadFunction, nullptr, g_interrupt_thread_stack, InterruptThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(powctl, InterruptHandler))); + os::SetThreadNamePointer(std::addressof(g_interrupt_thread), AMS_GET_SYSTEM_THREAD_NAME(powctl, InterruptHandler)); + os::StartThread(std::addressof(g_interrupt_thread)); + + /* Wait for the interrupt thread to enter the loop. */ + GetInterruptHandlerManager().WaitLoopEnter(); + } + + void FinalizeDrivers() { + /* Request the interrupt thread stop. */ + GetInterruptHandlerManager().RequestStop(); + os::WaitThread(std::addressof(g_interrupt_thread)); + os::DestroyThread(std::addressof(g_interrupt_thread)); + + /* Reset all device code entries. */ + GetDeviceCodeEntryManager().Reset(); + + /* Finalize all registered drivers. */ + for (auto &driver : GetDriverList()) { + driver.SafeCastTo().FinalizeDriver(); + } + + /* Finalize the interrupt handler manager. */ + GetInterruptHandlerManager().Finalize(); + } + + void RegisterDriver(IPowerControlDriver *driver) { + AMS_ASSERT(driver != nullptr); + GetDriverList().push_back(*driver); + } + + void UnregisterDriver(IPowerControlDriver *driver) { + AMS_ASSERT(driver != nullptr); + if (driver->IsLinkedToList()) { + auto &list = GetDriverList(); + list.erase(list.iterator_to(*driver)); + } + } + + Result RegisterDeviceCode(DeviceCode device_code, IDevice *device) { + AMS_ASSERT(device != nullptr); + R_TRY(GetDeviceCodeEntryManager().Add(device_code, device)); + return ResultSuccess(); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return GetDeviceCodeEntryManager().Remove(device_code); + } + + void RegisterInterruptHandler(ddsf::IEventHandler *handler) { + AMS_ASSERT(handler != nullptr); + GetInterruptHandlerManager().RegisterHandler(handler); + } + + void UnregisterInterruptHandler(ddsf::IEventHandler *handler) { + AMS_ASSERT(handler != nullptr); + GetInterruptHandlerManager().UnregisterHandler(handler); + } + + Result FindDevice(powctl::impl::IDevice **out, DeviceCode device_code) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Find the device. */ + ddsf::IDevice *device; + R_TRY(GetDeviceCodeEntryManager().FindDevice(std::addressof(device), device_code)); + + /* Set output. */ + *out = device->SafeCastToPointer(); + return ResultSuccess(); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/impl/powctl_device_management.hpp b/libraries/libstratosphere/source/powctl/impl/powctl_device_management.hpp new file mode 100644 index 000000000..2ab5216d9 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/powctl_device_management.hpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#pragma once +#include +#include "powctl_i_power_control_driver.hpp" + +namespace ams::powctl::impl { + + void InitializeDrivers(); + void FinalizeDrivers(); + + void RegisterDriver(IPowerControlDriver *driver); + void UnregisterDriver(IPowerControlDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, IDevice *device); + bool UnregisterDeviceCode(DeviceCode device_code); + + void RegisterInterruptHandler(ddsf::IEventHandler *handler); + void UnregisterInterruptHandler(ddsf::IEventHandler *handler); + + Result FindDevice(IDevice **out, DeviceCode device_code); + +} diff --git a/libraries/libstratosphere/source/powctl/impl/powctl_i_power_control_driver.hpp b/libraries/libstratosphere/source/powctl/impl/powctl_i_power_control_driver.hpp new file mode 100644 index 000000000..ab1eaef75 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/powctl_i_power_control_driver.hpp @@ -0,0 +1,137 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::powctl::impl { + + class IDevice : public ::ams::ddsf::IDevice { + NON_COPYABLE(IDevice); + NON_MOVEABLE(IDevice); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::IDevice, ::ams::ddsf::IDevice); + public: + IDevice() : ddsf::IDevice(false) { /* ... */ } + virtual ~IDevice() { /* ... */ } + }; + + class IPowerControlDriver : public ::ams::ddsf::IDriver { + NON_COPYABLE(IPowerControlDriver); + NON_MOVEABLE(IPowerControlDriver); + AMS_DDSF_CASTABLE_TRAITS(ams::powctl::impl::IPowerControlDriver, ::ams::ddsf::IDriver); + private: + bool event_handler_enabled; + protected: + constexpr bool IsEventHandlerEnabled() const { + return this->event_handler_enabled; + } + public: + IPowerControlDriver(bool ev) : IDriver(), event_handler_enabled(ev) { /* ... */ } + virtual ~IPowerControlDriver() { /* ... */ } + + virtual void InitializeDriver() = 0; + virtual void FinalizeDriver() = 0; + + virtual Result GetDeviceSystemEvent(os::SystemEventType **out, IDevice *device) = 0; + virtual Result SetDeviceInterruptEnabled(IDevice *device, bool enable) = 0; + + /* TODO: Eventually implement proper error status enum? */ + virtual Result GetDeviceErrorStatus(u32 *out, IDevice *device) = 0; + virtual Result SetDeviceErrorStatus(IDevice *device, u32 status) = 0; + + virtual Result GetBatterySocRep(float *out_percent, IDevice *device) = 0; + + virtual Result GetBatterySocVf(float *out_percent, IDevice *device) = 0; + + virtual Result GetBatteryFullCapacity(int *out_mah, IDevice *device) = 0; + virtual Result GetBatteryRemainingCapacity(int *out_mah, IDevice *device) = 0; + + virtual Result SetBatteryPercentageMinimumAlertThreshold(IDevice *device, float percentage) = 0; + virtual Result SetBatteryPercentageMaximumAlertThreshold(IDevice *device, float percentage) = 0; + virtual Result SetBatteryPercentageFullThreshold(IDevice *device, float percentage) = 0; + + virtual Result GetChargerChargeCurrentState(ChargeCurrentState *out, IDevice *device) = 0; + virtual Result SetChargerChargeCurrentState(IDevice *device, ChargeCurrentState state) = 0; + + virtual Result GetChargerFastChargeCurrentLimit(int *out_ma, IDevice *device) = 0; + virtual Result SetChargerFastChargeCurrentLimit(IDevice *device, int ma) = 0; + + virtual Result GetChargerChargeVoltageLimit(int *out_mv, IDevice *device) = 0; + virtual Result SetChargerChargeVoltageLimit(IDevice *device, int mv) = 0; + + virtual Result SetChargerChargerConfiguration(IDevice *device, ChargerConfiguration cfg) = 0; + + virtual Result IsChargerHiZEnabled(bool *out, IDevice *device) = 0; + virtual Result SetChargerHiZEnabled(IDevice *device, bool en) = 0; + + virtual Result GetBatteryAverageCurrent(int *out_ma, IDevice *device) = 0; + virtual Result GetBatteryCurrent(int *out_ma, IDevice *device) = 0; + + virtual Result GetChargerInputCurrentLimit(int *out_ma, IDevice *device) = 0; + virtual Result SetChargerInputCurrentLimit(IDevice *device, int ma) = 0; + + virtual Result SetChargerInputVoltageLimit(IDevice *device, int mv) = 0; + + virtual Result SetChargerBoostModeCurrentLimit(IDevice *device, int ma) = 0; + + virtual Result GetBatteryInternalState(void *dst, size_t *out_size, IDevice *device, size_t dst_size) = 0; + virtual Result SetBatteryInternalState(IDevice *device, const void *src, size_t src_size) = 0; + + virtual Result GetBatteryNeedToRestoreParameters(bool *out, IDevice *device) = 0; + virtual Result SetBatteryNeedToRestoreParameters(IDevice *device, bool en) = 0; + + virtual Result IsBatteryI2cShutdownEnabled(bool *out, IDevice *device) = 0; + virtual Result SetBatteryI2cShutdownEnabled(IDevice *device, bool en) = 0; + + virtual Result IsBatteryPresent(bool *out, IDevice *device) = 0; + + virtual Result GetChargerChargerStatus(ChargerStatus *out, IDevice *device) = 0; + + virtual Result GetBatteryCycles(int *out, IDevice *device) = 0; + virtual Result SetBatteryCycles(IDevice *device, int cycles) = 0; + + virtual Result GetBatteryAge(float *out_percent, IDevice *device) = 0; + + virtual Result GetBatteryTemperature(float *out_c, IDevice *device) = 0; + virtual Result GetBatteryMaximumTemperature(float *out_c, IDevice *device) = 0; + + virtual Result SetBatteryTemperatureMinimumAlertThreshold(IDevice *device, float c) = 0; + virtual Result SetBatteryTemperatureMaximumAlertThreshold(IDevice *device, float c) = 0; + + virtual Result GetBatteryVCell(int *out_mv, IDevice *device) = 0; + virtual Result GetBatteryAverageVCell(int *out_mv, IDevice *device) = 0; + + virtual Result GetBatteryAverageVCellTime(TimeSpan *out, IDevice *device) = 0; + + virtual Result SetBatteryVoltageMinimumAlertThreshold(IDevice *device, int mv) = 0; + + virtual Result GetBatteryOpenCircuitVoltage(int *out_mv, IDevice *device) = 0; + + virtual Result SetBatteryVoltageMaximumAlertThreshold(IDevice *device, int mv) = 0; + + virtual Result IsChargerWatchdogTimerEnabled(bool *out, IDevice *device) = 0; + virtual Result SetChargerWatchdogTimerEnabled(IDevice *device, bool en) = 0; + + virtual Result SetChargerWatchdogTimerTimeout(IDevice *device, TimeSpan timeout) = 0; + virtual Result ResetChargerWatchdogTimer(IDevice *device) = 0; + + virtual Result GetChargerBatteryCompensation(int *out_mo, IDevice *device) = 0; + virtual Result SetChargerBatteryCompensation(IDevice *device, int mo) = 0; + + virtual Result GetChargerVoltageClamp(int *out_mv, IDevice *device) = 0; + virtual Result SetChargerVoltageClamp(IDevice *device, int mv) = 0; + }; + +} diff --git a/libraries/libstratosphere/source/powctl/impl/powctl_select_board_driver.hpp b/libraries/libstratosphere/source/powctl/impl/powctl_select_board_driver.hpp new file mode 100644 index 000000000..6a9623f4f --- /dev/null +++ b/libraries/libstratosphere/source/powctl/impl/powctl_select_board_driver.hpp @@ -0,0 +1,32 @@ +/* + * 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 . + */ +#pragma once +#include +#include "powctl_i_power_control_driver.hpp" + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include "board/nintendo_nx/powctl_board_impl.hpp" + + namespace ams::powctl::impl::board { + using namespace ams::powctl::impl::board::nintendo_nx; + } + +#else + + #error "Unknown board for ams::powctl::impl" + +#endif diff --git a/libraries/libstratosphere/source/powctl/powctl_battery_api.cpp b/libraries/libstratosphere/source/powctl/powctl_battery_api.cpp new file mode 100644 index 000000000..1b83d5939 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/powctl_battery_api.cpp @@ -0,0 +1,439 @@ +/* + * 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 . + */ +#include +#include "impl/powctl_i_power_control_driver.hpp" +#include "impl/powctl_device_management.hpp" + +namespace ams::powctl { + + namespace { + + impl::SessionImpl &GetOpenSessionImpl(Session &session) { + AMS_ASSERT(session.has_session); + auto &impl = GetReference(session.impl_storage); + AMS_ASSERT(impl.IsOpen()); + return impl; + } + + } + + Result GetBatterySocRep(float *out_percent, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatterySocRep(out_percent, std::addressof(device)); + } + + Result GetBatterySocVf(float *out_percent, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatterySocVf(out_percent, std::addressof(device)); + } + + Result GetBatteryFullCapacity(int *out_mah, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryFullCapacity(out_mah, std::addressof(device)); + } + + Result GetBatteryRemainingCapacity(int *out_mah, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryRemainingCapacity(out_mah, std::addressof(device)); + } + + Result SetBatteryPercentageMinimumAlertThreshold(Session &session, float percentage) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryPercentageMinimumAlertThreshold(std::addressof(device), percentage); + } + + Result SetBatteryPercentageMaximumAlertThreshold(Session &session, float percentage) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryPercentageMaximumAlertThreshold(std::addressof(device), percentage); + } + + Result SetBatteryPercentageFullThreshold(Session &session, float percentage) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryPercentageFullThreshold(std::addressof(device), percentage); + } + + Result GetBatteryAverageCurrent(int *out_ma, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryAverageCurrent(out_ma, std::addressof(device)); + } + + Result GetBatteryCurrent(int *out_ma, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryCurrent(out_ma, std::addressof(device)); + } + + Result GetBatteryInternalState(void *dst, size_t *out_size, Session &session, size_t dst_size) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryInternalState(dst, out_size, std::addressof(device), dst_size); + } + + Result SetBatteryInternalState(Session &session, const void *src, size_t src_size) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryInternalState(std::addressof(device), src, src_size); + } + + Result GetBatteryNeedToRestoreParameters(bool *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryNeedToRestoreParameters(out, std::addressof(device)); + } + + Result SetBatteryNeedToRestoreParameters(Session &session, bool en) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryNeedToRestoreParameters(std::addressof(device), en); + } + + Result IsBatteryI2cShutdownEnabled(bool *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().IsBatteryI2cShutdownEnabled(out, std::addressof(device)); + } + + Result SetBatteryI2cShutdownEnabled(Session &session, bool en) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryI2cShutdownEnabled(std::addressof(device), en); + } + + Result IsBatteryPresent(bool *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().IsBatteryPresent(out, std::addressof(device)); + } + + Result GetBatteryCycles(int *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryCycles(out, std::addressof(device)); + } + + Result SetBatteryCycles(Session &session, int cycles) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryCycles(std::addressof(device), cycles); + } + + Result GetBatteryAge(float *out_percent, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryAge(out_percent, std::addressof(device)); + } + + Result GetBatteryTemperature(float *out_c, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryTemperature(out_c, std::addressof(device)); + } + + Result GetBatteryMaximumTemperature(float *out_c, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryMaximumTemperature(out_c, std::addressof(device)); + } + + Result SetBatteryTemperatureMinimumAlertThreshold(Session &session, float c) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryTemperatureMinimumAlertThreshold(std::addressof(device), c); + } + + Result SetBatteryTemperatureMaximumAlertThreshold(Session &session, float c) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryTemperatureMaximumAlertThreshold(std::addressof(device), c); + } + + Result GetBatteryVCell(int *out_mv, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryVCell(out_mv, std::addressof(device)); + } + + Result GetBatteryAverageVCell(int *out_mv, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryAverageVCell(out_mv, std::addressof(device)); + } + + Result GetBatteryAverageVCellTime(TimeSpan *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryAverageVCellTime(out, std::addressof(device)); + } + + Result GetBatteryOpenCircuitVoltage(int *out_mv, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetBatteryOpenCircuitVoltage(out_mv, std::addressof(device)); + } + + Result SetBatteryVoltageMinimumAlertThreshold(Session &session, int mv) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryVoltageMinimumAlertThreshold(std::addressof(device), mv); + } + + Result SetBatteryVoltageMaximumAlertThreshold(Session &session, int mv) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetBatteryVoltageMaximumAlertThreshold(std::addressof(device), mv); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/powctl_charger_api.cpp b/libraries/libstratosphere/source/powctl/powctl_charger_api.cpp new file mode 100644 index 000000000..cce1746a5 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/powctl_charger_api.cpp @@ -0,0 +1,329 @@ +/* + * 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 . + */ +#include +#include "impl/powctl_i_power_control_driver.hpp" +#include "impl/powctl_device_management.hpp" + +namespace ams::powctl { + + namespace { + + impl::SessionImpl &GetOpenSessionImpl(Session &session) { + AMS_ASSERT(session.has_session); + auto &impl = GetReference(session.impl_storage); + AMS_ASSERT(impl.IsOpen()); + return impl; + } + + } + + Result GetChargerChargeCurrentState(ChargeCurrentState *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetChargerChargeCurrentState(out, std::addressof(device)); + } + + Result SetChargerChargeCurrentState(Session &session, ChargeCurrentState state) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerChargeCurrentState(std::addressof(device), state); + } + + Result GetChargerFastChargeCurrentLimit(int *out_ma, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetChargerFastChargeCurrentLimit(out_ma, std::addressof(device)); + } + + Result SetChargerFastChargeCurrentLimit(Session &session, int ma) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerFastChargeCurrentLimit(std::addressof(device), ma); + } + + Result GetChargerChargeVoltageLimit(int *out_mv, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetChargerChargeVoltageLimit(out_mv, std::addressof(device)); + } + + Result SetChargerChargeVoltageLimit(Session &session, int mv) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerChargeVoltageLimit(std::addressof(device), mv); + } + + Result SetChargerChargerConfiguration(Session &session, ChargerConfiguration cfg) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerChargerConfiguration(std::addressof(device), cfg); + } + + Result IsChargerHiZEnabled(bool *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().IsChargerHiZEnabled(out, std::addressof(device)); + } + + Result SetChargerHiZEnabled(Session &session, bool en) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerHiZEnabled(std::addressof(device), en); + } + + Result GetChargerInputCurrentLimit(int *out_ma, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetChargerInputCurrentLimit(out_ma, std::addressof(device)); + } + + Result SetChargerInputCurrentLimit(Session &session, int ma) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerInputCurrentLimit(std::addressof(device), ma); + } + + Result SetChargerInputVoltageLimit(Session &session, int mv) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerInputVoltageLimit(std::addressof(device), mv); + } + + Result SetChargerBoostModeCurrentLimit(Session &session, int ma) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerBoostModeCurrentLimit(std::addressof(device), ma); + } + + Result GetChargerChargerStatus(ChargerStatus *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetChargerChargerStatus(out, std::addressof(device)); + } + + Result IsChargerWatchdogTimerEnabled(bool *out, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().IsChargerWatchdogTimerEnabled(out, std::addressof(device)); + } + + Result SetChargerWatchdogTimerEnabled(Session &session, bool en) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerWatchdogTimerEnabled(std::addressof(device), en); + } + + Result SetChargerWatchdogTimerTimeout(Session &session, TimeSpan timeout) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerWatchdogTimerTimeout(std::addressof(device), timeout); + } + + Result ResetChargerWatchdogTimer(Session &session); + + Result GetChargerBatteryCompensation(int *out_mo, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetChargerBatteryCompensation(out_mo, std::addressof(device)); + } + + Result SetChargerBatteryCompensation(Session &session, int mo) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerBatteryCompensation(std::addressof(device), mo); + } + + Result GetChargerVoltageClamp(int *out_mv, Session &session) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Read), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().GetChargerVoltageClamp(out_mv, std::addressof(device)); + } + + Result SetChargerVoltageClamp(Session &session, int mv) { + /* Get the session impl. */ + auto &impl = GetOpenSessionImpl(session); + + /* Check the access mode. */ + R_UNLESS(impl.CheckAccess(ddsf::AccessMode_Write), ddsf::ResultPermissionDenied()); + + /* Get the device. */ + auto &device = impl.GetDevice().SafeCastTo(); + + /* Call into the driver. */ + return device.GetDriver().SafeCastTo().SetChargerVoltageClamp(std::addressof(device), mv); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/powctl/powctl_driver_api.cpp b/libraries/libstratosphere/source/powctl/powctl_driver_api.cpp new file mode 100644 index 000000000..ad5e2ed20 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/powctl_driver_api.cpp @@ -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 . + */ +#include +#include "impl/powctl_select_board_driver.hpp" +#include "impl/powctl_device_management.hpp" + +namespace ams::powctl { + + void Initialize(bool enable_interrupt_handlers) { + /* Initialize the board driver. */ + impl::board::Initialize(enable_interrupt_handlers); + + /* Initialize drivers. */ + impl::InitializeDrivers(); + + } + + void Finalize() { + /* Finalize drivers. */ + impl::FinalizeDrivers(); + + /* Finalize the board driver. */ + impl::board::Finalize(); + } + +} diff --git a/libraries/libstratosphere/source/powctl/powctl_session_api.cpp b/libraries/libstratosphere/source/powctl/powctl_session_api.cpp new file mode 100644 index 000000000..3b46ab0b6 --- /dev/null +++ b/libraries/libstratosphere/source/powctl/powctl_session_api.cpp @@ -0,0 +1,89 @@ +/* + * 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 . + */ +#include +#include "impl/powctl_device_management.hpp" + +namespace ams::powctl { + + namespace { + + ddsf::AccessMode SanitizeAccessMode(ddsf::AccessMode access_mode) { + switch (access_mode) { + case ddsf::AccessMode_Read: + case ddsf::AccessMode_Write: + case ddsf::AccessMode_ReadWrite: + case ddsf::AccessMode_WriteShared: + case ddsf::AccessMode_ReadWriteShared: + return access_mode; + default: + return ddsf::AccessMode_None; + } + } + + impl::SessionImpl &GetSessionImpl(Session &session) { + return GetReference(session.impl_storage); + } + + void DestroySession(Session &session) { + GetSessionImpl(session).~SessionImpl(); + session.has_session = false; + } + + void DestroySessionIfNecessary(Session &session) { + if (session.has_session) { + DestroySession(session); + } + } + + void CloseSessionIfOpen(Session &session) { + if (session.has_session && GetSessionImpl(session).IsOpen()) { + DestroySession(session); + } + } + + } + + Result OpenSession(Session *out, DeviceCode device_code, ddsf::AccessMode access_mode) { + /* Validate input. */ + AMS_ASSERT(out != nullptr); + access_mode = SanitizeAccessMode(access_mode); + + /* Find the target device. */ + impl::IDevice *device = nullptr; + R_TRY(impl::FindDevice(std::addressof(device), device_code)); + + /* Clean up the session if we have one. */ + DestroySessionIfNecessary(*out); + + /* Construct the session. */ + new (std::addressof(GetSessionImpl(*out))) impl::SessionImpl; + auto guard = SCOPE_GUARD { DestroySessionIfNecessary(*out); }; + + /* Try to open the session. */ + R_TRY(ddsf::OpenSession(device, std::addressof(GetSessionImpl(*out)), access_mode)); + + /* We opened the session! */ + guard.Cancel(); + return ResultSuccess(); + } + + void CloseSession(Session &session) { + /* This seems extremely unnecessary/duplicate, but it's what Nintendo does. */ + CloseSessionIfOpen(session); + DestroySession(session); + } + +} \ No newline at end of file diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.cpp new file mode 100644 index 000000000..1b1849d10 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.cpp @@ -0,0 +1,66 @@ +/* + * 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 . + */ +#include +#include "pwm_impl_pwm_driver_api.hpp" +#include "pwm_pwm_driver_impl.hpp" + +namespace ams::pwm::driver::board::nintendo_nx::impl { + + namespace { + + constexpr inline const dd::PhysicalAddress PwmRegistersPhysicalAddress = 0x7000A000; + constexpr inline size_t PwmRegistersSize = 0x100; + + constexpr const ChannelDefinition SupportedChannels[] = { + { pwm::DeviceCode_LcdBacklight, 0 }, + { pwm::DeviceCode_CpuFan, 1 }, + }; + + } + + Result InitializePwmDriver() { + /* Get the memory resource with which to allocate our driver/devices. */ + auto *memory_resource = ddsf::GetMemoryResource(); + + /* Allocate storage for our driver. */ + auto *driver_storage = memory_resource->Allocate(sizeof(PwmDriverImpl), alignof(PwmDriverImpl)); + AMS_ABORT_UNLESS(driver_storage != nullptr); + + /* Create our driver. */ + auto *driver = new (static_cast(driver_storage)) PwmDriverImpl(PwmRegistersPhysicalAddress, PwmRegistersSize, SupportedChannels, util::size(SupportedChannels)); + + /* Register our driver. */ + pwm::driver::RegisterDriver(driver); + + /* Create our devices. */ + for (const auto &entry : SupportedChannels) { + auto *device_storage = memory_resource->Allocate(sizeof(PwmDriverImpl), alignof(PwmDriverImpl)); + AMS_ABORT_UNLESS(device_storage != nullptr); + + /* Create our driver. */ + auto *device = new (static_cast(device_storage)) PwmDeviceImpl(entry.channel_id); + + /* Register the device with our driver. */ + driver->RegisterDevice(device); + + /* Register the device code with our driver. */ + R_ABORT_UNLESS(pwm::driver::RegisterDeviceCode(entry.device_code, device)); + } + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.hpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.hpp new file mode 100644 index 000000000..9ce8137d8 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_impl_pwm_driver_api.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::pwm::driver::board::nintendo_nx::impl { + + struct ChannelDefinition { + DeviceCode device_code; + int channel_id; + }; + + Result InitializePwmDriver(); + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.cpp new file mode 100644 index 000000000..353148e52 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.cpp @@ -0,0 +1,255 @@ +/* + * 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 . + */ +#include +#include "pwm_pwm_driver_impl.hpp" + +namespace ams::pwm::driver::board::nintendo_nx::impl { + + namespace { + + constexpr inline u32 PwmClockRateHz = 45'333'333; + + constexpr inline TimeSpan DefaultChannelPeriod = TimeSpan::FromMilliSeconds(10); + + constexpr inline int MaxDuty = 0x100; + + template + T DivideRoundUp(T a, T b) { + return (a + (b / 2)) / b; + } + + } + + PwmDriverImpl::PwmDriverImpl(dd::PhysicalAddress paddr, size_t sz, const ChannelDefinition *c, size_t nc) : registers_phys_addr(paddr), registers_size(sz), channels(c), num_channels(nc), registers(0) { + /* ... */ + } + + void PwmDriverImpl::PowerOn() { + /* Initialize pcv driver. */ + pcv::Initialize(); + + /* Setup clock/power for pwm. */ + R_ABORT_UNLESS(pcv::SetReset(pcv::Module_Pwm, true)); + R_ABORT_UNLESS(pcv::SetClockEnabled(pcv::Module_Pwm, true)); + R_ABORT_UNLESS(pcv::SetClockRate(pcv::Module_Pwm, PwmClockRateHz)); + R_ABORT_UNLESS(pcv::SetReset(pcv::Module_Pwm, false)); + } + + void PwmDriverImpl::PowerOff() { + /* Disable clock and hold pwm in reset. */ + /* NOTE: Nintendo does not check this succeeds. */ + pcv::SetClockEnabled(pcv::Module_Pwm, false); + pcv::SetReset(pcv::Module_Pwm, true); + + /* Finalize pcv driver. */ + pcv::Finalize(); + } + + void PwmDriverImpl::InitializeDriver() { + /* Get the registers virtual address. */ + this->registers = dd::QueryIoMapping(this->registers_phys_addr, this->registers_size); + AMS_ABORT_UNLESS(this->registers != 0); + + /* Setup power to pwm. */ + this->PowerOn(); + } + + void PwmDriverImpl::FinalizeDriver() { + /* Shut down power to pwm. */ + this->PowerOff(); + } + + Result PwmDriverImpl::InitializeDevice(IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Configure initial settings. */ + /* NOTE: None of these results are checked. */ + this->SetEnabled(device, false); + this->SetDuty(device, 0); + this->SetPeriod(device, DefaultChannelPeriod); + return ResultSuccess(); + } + + void PwmDriverImpl::FinalizeDevice(IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Nothing to do here. */ + AMS_UNUSED(device); + } + + Result PwmDriverImpl::SetPeriod(IPwmDevice *device, TimeSpan period) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Verify the period is valid. */ + const auto ns = period.GetNanoSeconds(); + R_UNLESS(ns > 0, pwm::ResultInvalidArgument()); + + /* Convert the ns to a desired frequency (rounding up). */ + const auto hz = DivideRoundUp(TimeSpan::FromSeconds(1).GetNanoSeconds(), ns); + R_UNLESS(hz > 0, pwm::ResultInvalidArgument()); + + /* Convert the frequency to a pfm value. */ + const u32 pfm = std::min(std::max(DivideRoundUp(PwmClockRateHz, hz * 256), 1) - 1, 0x1FFF); + + /* Acquire exclusive access to the device registers. */ + std::scoped_lock lk(device->SafeCastTo()); + + /* Update the period. */ + reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_VALUE(PWM_CSR_PFM, pfm)); + + return ResultSuccess(); + } + + Result PwmDriverImpl::GetPeriod(TimeSpan *out, IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(device != nullptr); + + /* Get the pfm value. */ + const u32 pfm = reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PFM)); + + /* Convert it to a frequency. */ + /* pfm = ((ClockRate / (hz * 256)) - 1) -> hz = (ClockRate / ((pfm + 1) * 256)) */ + const auto hz = DivideRoundUp(PwmClockRateHz, (pfm + 1) * 256); + + /* Convert the frequency to a period. */ + const auto ns = DivideRoundUp(TimeSpan::FromSeconds(1).GetNanoSeconds(), hz); + + /* Set the output. */ + *out = TimeSpan::FromNanoSeconds(ns); + return ResultSuccess(); + } + + Result PwmDriverImpl::SetDuty(IPwmDevice *device, int duty) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Validate the duty. */ + R_UNLESS(0 <= duty && duty <= MaxDuty, pwm::ResultInvalidArgument()); + + /* Acquire exclusive access to the device registers. */ + std::scoped_lock lk(device->SafeCastTo()); + + /* Update the duty. */ + reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_VALUE(PWM_CSR_PWM, static_cast(duty))); + + return ResultSuccess(); + } + + Result PwmDriverImpl::GetDuty(int *out, IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(device != nullptr); + + /* Get the duty. */ + *out = static_cast(reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PWM))); + return ResultSuccess(); + } + + Result PwmDriverImpl::SetScale(IPwmDevice *device, double scale) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Convert the scale to a duty. */ + const int duty = static_cast(((scale * 256.0) / 100.0) + 0.5); + + /* Set the duty. */ + return this->SetDuty(device, duty); + } + + Result PwmDriverImpl::GetScale(double *out, IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(device != nullptr); + + /* Get the duty. */ + const int duty = static_cast(reg::GetValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_MASK(PWM_CSR_PWM))); + + /* Convert to scale. */ + *out = (static_cast(duty) * 100.0) / 256.0; + return ResultSuccess(); + } + + Result PwmDriverImpl::SetEnabled(IPwmDevice *device, bool en) { + /* Validate the device. */ + AMS_ASSERT(device != nullptr); + + /* Acquire exclusive access to the device registers. */ + std::scoped_lock lk(device->SafeCastTo()); + + /* Update the enable. */ + reg::ReadWrite(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM_SEL(PWM_CSR_ENB, en, ENABLE, DISABLE)); + + return ResultSuccess(); + } + + Result PwmDriverImpl::GetEnabled(bool *out, IPwmDevice *device) { + /* Validate the device. */ + AMS_ASSERT(out != nullptr); + AMS_ASSERT(device != nullptr); + + /* Get the enable. */ + *out = reg::HasValue(this->GetRegistersFor(device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM(PWM_CSR_ENB, ENABLE)); + return ResultSuccess(); + } + + Result PwmDriverImpl::Suspend() { + /* Suspend each device. */ + this->ForEachDevice([&](ddsf::IDevice &device) -> bool { + /* Convert the device to a pwm device. */ + auto &pwm_device = device.SafeCastTo(); + + /* Cache the suspend value. */ + pwm_device.SetSuspendValue(reg::Read(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR)); + + /* Acquire exclusive access to the device. */ + std::scoped_lock lk(pwm_device); + + /* Disable the device. */ + reg::ReadWrite(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR, PWM_REG_BITS_ENUM(PWM_CSR_ENB, DISABLE)); + + /* Continue to the next device. */ + return true; + }); + + /* Disable clock to pwm. */ + return pcv::SetClockEnabled(pcv::Module_Pwm, false); + } + + void PwmDriverImpl::Resume() { + /* Power on. */ + this->PowerOn(); + + /* Resume each device. */ + this->ForEachDevice([&](ddsf::IDevice &device) -> bool { + /* Convert the device to a pwm device. */ + auto &pwm_device = device.SafeCastTo(); + + /* Acquire exclusive access to the device. */ + std::scoped_lock lk(pwm_device); + + /* Write the device's suspend value. */ + reg::Write(this->GetRegistersFor(pwm_device) + PWM_CONTROLLER_PWM_CSR, pwm_device.GetSuspendValue()); + + /* Continue to the next device. */ + return true; + }); + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.hpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.hpp new file mode 100644 index 000000000..d04e841bd --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/impl/pwm_pwm_driver_impl.hpp @@ -0,0 +1,85 @@ +/* + * 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 . + */ +#pragma once +#include +#include "pwm_impl_pwm_driver_api.hpp" + +namespace ams::pwm::driver::board::nintendo_nx::impl { + + class PwmDeviceImpl : public ::ams::pwm::driver::IPwmDevice { + NON_COPYABLE(PwmDeviceImpl); + NON_MOVEABLE(PwmDeviceImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::board::nintendo_nx::impl::PwmDeviceImpl, ::ams::pwm::driver::IPwmDevice); + private: + os::SdkMutex suspend_mutex; + u32 suspend_value; + public: + PwmDeviceImpl(int channel) : IPwmDevice(channel), suspend_mutex(), suspend_value() { /* ... */ } + + void SetSuspendValue(u32 v) { this->suspend_value = v; } + u32 GetSuspendValue() const { return this->suspend_value; } + + void lock() { return this->suspend_mutex.lock(); } + void unlock() { return this->suspend_mutex.unlock(); } + }; + + class PwmDriverImpl : public ::ams::pwm::driver::IPwmDriver { + NON_COPYABLE(PwmDriverImpl); + NON_MOVEABLE(PwmDriverImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::board::nintendo_nx::impl::PwmDriverImpl, ::ams::pwm::driver::IPwmDriver); + private: + dd::PhysicalAddress registers_phys_addr; + size_t registers_size; + const ChannelDefinition *channels; + size_t num_channels; + uintptr_t registers; + private: + ALWAYS_INLINE uintptr_t GetRegistersFor(IPwmDevice &device) { + return registers + PWM_CONTROLLER_PWM_CHANNEL_OFFSET(device.GetChannelIndex()); + } + + ALWAYS_INLINE uintptr_t GetRegistersFor(IPwmDevice *device) { + return this->GetRegistersFor(*device); + } + + void PowerOn(); + void PowerOff(); + public: + PwmDriverImpl(dd::PhysicalAddress paddr, size_t sz, const ChannelDefinition *c, size_t nsc); + + virtual void InitializeDriver() override; + virtual void FinalizeDriver() override; + + virtual Result InitializeDevice(IPwmDevice *device) override; + virtual void FinalizeDevice(IPwmDevice *device) override; + + virtual Result SetPeriod(IPwmDevice *device, TimeSpan period) override; + virtual Result GetPeriod(TimeSpan *out, IPwmDevice *device) override; + + virtual Result SetDuty(IPwmDevice *device, int duty) override; + virtual Result GetDuty(int *out, IPwmDevice *device) override; + + virtual Result SetScale(IPwmDevice *device, double scale) override; + virtual Result GetScale(double *out, IPwmDevice *device) override; + + virtual Result SetEnabled(IPwmDevice *device, bool en) override; + virtual Result GetEnabled(bool *out, IPwmDevice *device) override; + + virtual Result Suspend() override; + virtual void Resume() override; + }; + +} diff --git a/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/pwm_driver_api.cpp b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/pwm_driver_api.cpp new file mode 100644 index 000000000..0b8442e37 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/board/nintendo_nx/pwm_driver_api.cpp @@ -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 . + */ +#include +#include "impl/pwm_impl_pwm_driver_api.hpp" + +namespace ams::pwm::driver::board::nintendo_nx { + + void Initialize() { + R_ABORT_UNLESS(impl::InitializePwmDriver()); + /* TODO: R_ABORT_UNLESS(impl::InitializePmcDriver()); */ + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/impl/pwm_channel_session_impl.cpp b/libraries/libstratosphere/source/pwm/driver/impl/pwm_channel_session_impl.cpp new file mode 100644 index 000000000..1459246cc --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/impl/pwm_channel_session_impl.cpp @@ -0,0 +1,124 @@ +/* + * 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 . + */ +#include +#include "pwm_driver_core.hpp" +#include "pwm_channel_session_impl.hpp" + +namespace ams::pwm::driver::impl { + + Result ChannelSessionImpl::Open(IPwmDevice *device, ddsf::AccessMode access_mode) { + AMS_ASSERT(device != nullptr); + + /* Check if we're the device's first session. */ + const bool first = !device->HasAnyOpenSession(); + + /* Open the session. */ + R_TRY(ddsf::OpenSession(device, this, access_mode)); + auto guard = SCOPE_GUARD { ddsf::CloseSession(this); }; + + /* If we're the first session, initialize the device. */ + if (first) { + R_TRY(device->GetDriver().SafeCastTo().InitializeDevice(device)); + } + + /* We're opened. */ + guard.Cancel(); + return ResultSuccess(); + } + + void ChannelSessionImpl::Close() { + /* If we're not open, do nothing. */ + if (!this->IsOpen()) { + return; + } + + /* Get the device. */ + auto &device = this->GetDevice().SafeCastTo(); + + /* Close the session. */ + ddsf::CloseSession(this); + + /* If there are no remaining sessions, finalize the device. */ + if (!device.HasAnyOpenSession()) { + device.GetDriver().SafeCastTo().FinalizeDevice(std::addressof(device)); + } + } + + Result ChannelSessionImpl::SetPeriod(TimeSpan period) { + /* Get the device. */ + IPwmDevice &device = this->GetDevice().SafeCastTo(); + + /* Invoke the driver handler. */ + return device.GetDriver().SafeCastTo().SetPeriod(std::addressof(device), period); + } + + Result ChannelSessionImpl::GetPeriod(TimeSpan *out) { + /* Get the device. */ + IPwmDevice &device = this->GetDevice().SafeCastTo(); + + /* Invoke the driver handler. */ + return device.GetDriver().SafeCastTo().GetPeriod(out, std::addressof(device)); + } + + Result ChannelSessionImpl::SetDuty(int duty) { + /* Get the device. */ + IPwmDevice &device = this->GetDevice().SafeCastTo(); + + /* Invoke the driver handler. */ + return device.GetDriver().SafeCastTo().SetDuty(std::addressof(device), duty); + } + + Result ChannelSessionImpl::GetDuty(int *out) { + /* Get the device. */ + IPwmDevice &device = this->GetDevice().SafeCastTo(); + + /* Invoke the driver handler. */ + return device.GetDriver().SafeCastTo().GetDuty(out, std::addressof(device)); + } + + Result ChannelSessionImpl::SetEnabled(bool en) { + /* Get the device. */ + IPwmDevice &device = this->GetDevice().SafeCastTo(); + + /* Invoke the driver handler. */ + return device.GetDriver().SafeCastTo().SetEnabled(std::addressof(device), en); + } + + Result ChannelSessionImpl::GetEnabled(bool *out) { + /* Get the device. */ + IPwmDevice &device = this->GetDevice().SafeCastTo(); + + /* Invoke the driver handler. */ + return device.GetDriver().SafeCastTo().GetEnabled(out, std::addressof(device)); + } + + Result ChannelSessionImpl::SetScale(double scale) { + /* Get the device. */ + IPwmDevice &device = this->GetDevice().SafeCastTo(); + + /* Invoke the driver handler. */ + return device.GetDriver().SafeCastTo().SetScale(std::addressof(device), scale); + } + + Result ChannelSessionImpl::GetScale(double *out) { + /* Get the device. */ + IPwmDevice &device = this->GetDevice().SafeCastTo(); + + /* Invoke the driver handler. */ + return device.GetDriver().SafeCastTo().GetScale(out, std::addressof(device)); + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/impl/pwm_channel_session_impl.hpp b/libraries/libstratosphere/source/pwm/driver/impl/pwm_channel_session_impl.hpp new file mode 100644 index 000000000..6b8a75dfb --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/impl/pwm_channel_session_impl.hpp @@ -0,0 +1,77 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::pwm::driver::impl { + + class ChannelSessionImpl : public ::ams::ddsf::ISession { + NON_COPYABLE(ChannelSessionImpl); + NON_MOVEABLE(ChannelSessionImpl); + AMS_DDSF_CASTABLE_TRAITS(ams::pwm::driver::impl::ChannelSessionImpl, ::ams::ddsf::ISession); + public: + ChannelSessionImpl() { /* ... */ } + + ~ChannelSessionImpl() { + this->Close(); + } + + Result Open(IPwmDevice *device, ddsf::AccessMode access_mode); + void Close(); + + Result SetPeriod(TimeSpan period); + Result GetPeriod(TimeSpan *out); + + Result SetDuty(int duty); + Result GetDuty(int *out); + + Result SetEnabled(bool en); + Result GetEnabled(bool *out); + + Result SetScale(double scale); + Result GetScale(double *out); + }; + static_assert( sizeof(ChannelSessionImpl) <= ChannelSessionSize); + static_assert(alignof(ChannelSessionImpl) <= ChannelSessionAlign); + + struct alignas(ChannelSessionAlign) ChannelSessionImplPadded { + ChannelSessionImpl _impl; + u8 _padding[ChannelSessionSize - sizeof(ChannelSessionImpl)]; + }; + static_assert( sizeof(ChannelSessionImplPadded) == ChannelSessionSize); + static_assert(alignof(ChannelSessionImplPadded) == ChannelSessionAlign); + + ALWAYS_INLINE ChannelSessionImpl &GetChannelSessionImpl(ChannelSession &session) { + return GetReference(session._impl)._impl; + } + + ALWAYS_INLINE const ChannelSessionImpl &GetChannelSessionImpl(const ChannelSession &session) { + return GetReference(session._impl)._impl; + } + + ALWAYS_INLINE ChannelSessionImpl &GetOpenChannelSessionImpl(ChannelSession &session) { + auto &ref = GetReference(session._impl)._impl; + AMS_ASSERT(ref.IsOpen()); + return ref; + } + + ALWAYS_INLINE const ChannelSessionImpl &GetOpenChannelSessionImpl(const ChannelSession &session) { + const auto &ref = GetReference(session._impl)._impl; + AMS_ASSERT(ref.IsOpen()); + return ref; + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/impl/pwm_driver_core.cpp b/libraries/libstratosphere/source/pwm/driver/impl/pwm_driver_core.cpp new file mode 100644 index 000000000..359312dba --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/impl/pwm_driver_core.cpp @@ -0,0 +1,126 @@ +/* + * 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 . + */ +#include +#include "pwm_driver_core.hpp" + +namespace ams::pwm::driver::impl { + + namespace { + + constinit os::SdkMutex g_init_mutex; + constinit int g_init_count = 0; + + pwm::driver::IPwmDriver::List &GetPwmDriverList() { + static pwm::driver::IPwmDriver::List s_driver_list; + return s_driver_list; + } + + ddsf::DeviceCodeEntryManager &GetDeviceCodeEntryManager() { + static ddsf::DeviceCodeEntryManager s_device_code_entry_manager(ddsf::GetDeviceCodeEntryHolderMemoryResource()); + return s_device_code_entry_manager; + } + + } + + + void InitializeDrivers() { + std::scoped_lock lk(g_init_mutex); + + /* Initialize all registered drivers, if this is our first initialization. */ + if ((g_init_count++) == 0) { + for (auto &driver : GetPwmDriverList()) { + driver.SafeCastTo().InitializeDriver(); + } + } + } + + void FinalizeDrivers() { + std::scoped_lock lk(g_init_mutex); + + /* If we have no remaining sessions, close. */ + if ((--g_init_count) == 0) { + /* Reset all device code entries. */ + GetDeviceCodeEntryManager().Reset(); + + /* Finalize all drivers. */ + for (auto &driver : GetPwmDriverList()) { + driver.SafeCastTo().FinalizeDriver(); + } + } + } + + void RegisterDriver(IPwmDriver *driver) { + AMS_ASSERT(driver != nullptr); + GetPwmDriverList().push_back(*driver); + } + + void UnregisterDriver(IPwmDriver *driver) { + AMS_ASSERT(driver != nullptr); + if (driver->IsLinkedToList()) { + auto &list = GetPwmDriverList(); + list.erase(list.iterator_to(*driver)); + } + } + + Result RegisterDeviceCode(DeviceCode device_code, IPwmDevice *device) { + AMS_ASSERT(device != nullptr); + R_TRY(GetDeviceCodeEntryManager().Add(device_code, device)); + return ResultSuccess(); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return GetDeviceCodeEntryManager().Remove(device_code); + } + + Result FindDevice(IPwmDevice **out, DeviceCode device_code) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Find the device. */ + ddsf::IDevice *device; + R_TRY(GetDeviceCodeEntryManager().FindDevice(std::addressof(device), device_code)); + + /* Set output. */ + *out = device->SafeCastToPointer(); + return ResultSuccess(); + } + + Result FindDeviceByChannelIndex(IPwmDevice **out, int channel) { + /* Validate output. */ + AMS_ASSERT(out != nullptr); + + /* Find the device. */ + bool found = false; + GetDeviceCodeEntryManager().ForEachEntry([&](ddsf::DeviceCodeEntry &entry) -> bool { + /* Convert the entry to an IPwmDevice. */ + auto &device = entry.GetDevice().SafeCastTo(); + + /* Check if the device is the one we're looking for. */ + if (device.GetChannelIndex() == channel) { + found = true; + *out = std::addressof(device); + return false; + } + return true; + }); + + /* Check that we found the pad. */ + R_UNLESS(found, ddsf::ResultDeviceCodeNotFound()); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/impl/pwm_driver_core.hpp b/libraries/libstratosphere/source/pwm/driver/impl/pwm_driver_core.hpp new file mode 100644 index 000000000..aca6a821e --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/impl/pwm_driver_core.hpp @@ -0,0 +1,33 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::pwm::driver::impl { + + void InitializeDrivers(); + void FinalizeDrivers(); + + void RegisterDriver(IPwmDriver *driver); + void UnregisterDriver(IPwmDriver *driver); + + Result RegisterDeviceCode(DeviceCode device_code, IPwmDevice *device); + bool UnregisterDeviceCode(DeviceCode device_code); + + Result FindDevice(IPwmDevice **out, DeviceCode device_code); + Result FindDeviceByChannelIndex(IPwmDevice **out, int channel); + +} diff --git a/libraries/libstratosphere/source/pwm/driver/pwm_driver_channel_api.cpp b/libraries/libstratosphere/source/pwm/driver/pwm_driver_channel_api.cpp new file mode 100644 index 000000000..27ab69454 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/pwm_driver_channel_api.cpp @@ -0,0 +1,97 @@ +/* + * 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 . + */ +#include + +#include "impl/pwm_driver_core.hpp" +#include "impl/pwm_channel_session_impl.hpp" + +namespace ams::pwm::driver { + + namespace { + + Result OpenSessionImpl(ChannelSession *out, IPwmDevice *device) { + /* Construct the session. */ + auto *session = new (std::addressof(impl::GetChannelSessionImpl(*out))) impl::ChannelSessionImpl; + auto session_guard = SCOPE_GUARD { session->~ChannelSessionImpl(); }; + + /* Open the session. */ + R_TRY(session->Open(device, ddsf::AccessMode_ReadWrite)); + + /* We succeeded. */ + session_guard.Cancel(); + return ResultSuccess(); + } + + } + + Result OpenSession(ChannelSession *out, DeviceCode device_code) { + AMS_ASSERT(out != nullptr); + + /* Find the device. */ + IPwmDevice *device = nullptr; + R_TRY(impl::FindDevice(std::addressof(device), device_code)); + AMS_ASSERT(device != nullptr); + + /* Open the session. */ + R_TRY(OpenSessionImpl(out, device)); + + return ResultSuccess(); + } + + void CloseSession(ChannelSession &session) { + impl::GetOpenChannelSessionImpl(session).~ChannelSessionImpl(); + } + + void SetPeriod(ChannelSession &session, TimeSpan period) { + R_ABORT_UNLESS(impl::GetOpenChannelSessionImpl(session).SetPeriod(period)); + } + + TimeSpan GetPeriod(ChannelSession &session) { + TimeSpan out_val; + R_ABORT_UNLESS(impl::GetOpenChannelSessionImpl(session).GetPeriod(std::addressof(out_val))); + return out_val; + } + + void SetDuty(ChannelSession &session, int duty) { + R_ABORT_UNLESS(impl::GetOpenChannelSessionImpl(session).SetDuty(duty)); + } + + int GetDuty(ChannelSession &session) { + int out_val; + R_ABORT_UNLESS(impl::GetOpenChannelSessionImpl(session).GetDuty(std::addressof(out_val))); + return out_val; + } + + void SetEnabled(ChannelSession &session, bool en) { + R_ABORT_UNLESS(impl::GetOpenChannelSessionImpl(session).SetEnabled(en)); + } + + bool GetEnabled(ChannelSession &session) { + bool out_val; + R_ABORT_UNLESS(impl::GetOpenChannelSessionImpl(session).GetEnabled(std::addressof(out_val))); + return out_val; + } + + void SetScale(ChannelSession &session, double scale) { + R_ABORT_UNLESS(impl::GetOpenChannelSessionImpl(session).SetScale(scale)); + } + + double GetScale(ChannelSession &session) { + double out_val; + R_ABORT_UNLESS(impl::GetOpenChannelSessionImpl(session).GetScale(std::addressof(out_val))); + return out_val; + } +} diff --git a/libraries/libstratosphere/source/pwm/driver/pwm_driver_client_api.cpp b/libraries/libstratosphere/source/pwm/driver/pwm_driver_client_api.cpp new file mode 100644 index 000000000..7126b7f3f --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/pwm_driver_client_api.cpp @@ -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 . + */ +#include + +#include "impl/pwm_driver_core.hpp" + +namespace ams::pwm::driver { + + void Initialize() { + return impl::InitializeDrivers(); + } + + void Finalize() { + return impl::FinalizeDrivers(); + } + +} diff --git a/libraries/libstratosphere/source/pwm/driver/pwm_driver_service_api.cpp b/libraries/libstratosphere/source/pwm/driver/pwm_driver_service_api.cpp new file mode 100644 index 000000000..5955c009b --- /dev/null +++ b/libraries/libstratosphere/source/pwm/driver/pwm_driver_service_api.cpp @@ -0,0 +1,38 @@ +/* + * 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 . + */ +#include + +#include "impl/pwm_driver_core.hpp" + +namespace ams::pwm::driver { + + void RegisterDriver(IPwmDriver *driver) { + return impl::RegisterDriver(driver); + } + + void UnregisterDriver(IPwmDriver *driver) { + return impl::UnregisterDriver(driver); + } + + Result RegisterDeviceCode(DeviceCode device_code, IPwmDevice *device) { + return impl::RegisterDeviceCode(device_code, device); + } + + bool UnregisterDeviceCode(DeviceCode device_code) { + return impl::UnregisterDeviceCode(device_code); + } + +} diff --git a/libraries/libstratosphere/source/pwm/pwm_api.cpp b/libraries/libstratosphere/source/pwm/pwm_api.cpp new file mode 100644 index 000000000..4b843c993 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/pwm_api.cpp @@ -0,0 +1,126 @@ +/* + * 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 . + */ +#include + +namespace ams::pwm { + + namespace { + + constinit os::SdkMutex g_init_mutex; + constinit int g_initialize_count = 0; + + std::shared_ptr g_pwm_manager; + + using InternalSession = std::shared_ptr; + + InternalSession &GetInterface(const ChannelSession &session) { + AMS_ASSERT(session._session != nullptr); + return *static_cast(session._session); + } + } + + void InitializeWith(std::shared_ptr &&sp) { + std::scoped_lock lk(g_init_mutex); + + AMS_ABORT_UNLESS(g_initialize_count == 0); + + g_pwm_manager = std::move(sp); + + g_initialize_count = 1; + } + + void Finalize() { + std::scoped_lock lk(g_init_mutex); + + AMS_ASSERT(g_initialize_count > 0); + + if ((--g_initialize_count) == 0) { + g_pwm_manager.reset(); + } + } + + Result OpenSession(ChannelSession *out, 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_6_0_0) { + R_TRY(g_pwm_manager->OpenSession2(std::addressof(object_holder), device_code)); + } else { + R_TRY(g_pwm_manager->OpenSession(std::addressof(object_holder), ConvertToChannelName(device_code))); + } + *internal_session = object_holder.GetServiceObject(); + } + + /* Set output. */ + out->_session = internal_session; + + /* We succeeded. */ + session_guard.Cancel(); + return ResultSuccess(); + } + + void CloseSession(ChannelSession &session) { + /* Close the session. */ + delete std::addressof(GetInterface(session)); + session._session = nullptr; + } + + void SetPeriod(ChannelSession &session, TimeSpan period) { + R_ABORT_UNLESS(GetInterface(session)->SetPeriod(period)); + } + + TimeSpan GetPeriod(ChannelSession &session) { + TimeSpanType out_val; + R_ABORT_UNLESS(GetInterface(session)->GetPeriod(std::addressof(out_val))); + return out_val; + } + + void SetDuty(ChannelSession &session, int duty) { + R_ABORT_UNLESS(GetInterface(session)->SetDuty(duty)); + } + + int GetDuty(ChannelSession &session) { + int out_val; + R_ABORT_UNLESS(GetInterface(session)->GetDuty(std::addressof(out_val))); + return out_val; + } + + void SetEnabled(ChannelSession &session, bool en) { + R_ABORT_UNLESS(GetInterface(session)->SetEnabled(en)); + } + + bool GetEnabled(ChannelSession &session) { + bool out_val; + R_ABORT_UNLESS(GetInterface(session)->GetEnabled(std::addressof(out_val))); + return out_val; + } + + void SetScale(ChannelSession &session, double scale) { + R_ABORT_UNLESS(GetInterface(session)->SetScale(scale)); + } + + double GetScale(ChannelSession &session) { + double out_val; + R_ABORT_UNLESS(GetInterface(session)->GetScale(std::addressof(out_val))); + return out_val; + } + +} diff --git a/libraries/libstratosphere/source/pwm/server/pwm_server_api.cpp b/libraries/libstratosphere/source/pwm/server/pwm_server_api.cpp new file mode 100644 index 000000000..0b9155e04 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/server/pwm_server_api.cpp @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#include +#include "pwm_server_manager_impl.hpp" + +namespace ams::pwm::server { + + namespace { + + ManagerImpl g_manager_impl; + + std::shared_ptr GetManagerServiceObject() { + static std::shared_ptr s_sp = ams::sf::GetSharedPointerTo(g_manager_impl); + return s_sp; + } + + } + + std::shared_ptr GetServiceObject() { + return GetManagerServiceObject(); + } + +} diff --git a/libraries/libstratosphere/source/pwm/server/pwm_server_channel_session_impl.hpp b/libraries/libstratosphere/source/pwm/server/pwm_server_channel_session_impl.hpp new file mode 100644 index 000000000..c70ac4cb0 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/server/pwm_server_channel_session_impl.hpp @@ -0,0 +1,88 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::pwm::server { + + class ManagerImpl; + + class ChannelSessionImpl { + private: + ManagerImpl *parent; /* NOTE: this is an sf::SharedPointer<> in Nintendo's code. */ + pwm::driver::ChannelSession internal_session; + bool has_session; + public: + explicit ChannelSessionImpl(ManagerImpl *p) : parent(p), has_session(false) { /* ... */ } + + ~ChannelSessionImpl() { + if (this->has_session) { + pwm::driver::CloseSession(this->internal_session); + } + } + + Result OpenSession(DeviceCode device_code) { + AMS_ABORT_UNLESS(!this->has_session); + + R_TRY(pwm::driver::OpenSession(std::addressof(this->internal_session), device_code)); + this->has_session = true; + return ResultSuccess(); + } + public: + /* Actual commands. */ + Result SetPeriod(TimeSpanType period) { + pwm::driver::SetPeriod(this->internal_session, period); + return ResultSuccess(); + } + + Result GetPeriod(ams::sf::Out out) { + out.SetValue(pwm::driver::GetPeriod(this->internal_session)); + return ResultSuccess(); + } + + Result SetDuty(int duty) { + pwm::driver::SetDuty(this->internal_session, duty); + return ResultSuccess(); + } + + Result GetDuty(ams::sf::Out out) { + out.SetValue(pwm::driver::GetDuty(this->internal_session)); + return ResultSuccess(); + } + + Result SetEnabled(bool enabled) { + pwm::driver::SetEnabled(this->internal_session, enabled); + return ResultSuccess(); + } + + Result GetEnabled(ams::sf::Out out) { + out.SetValue(pwm::driver::GetEnabled(this->internal_session)); + return ResultSuccess(); + } + + Result SetScale(double scale) { + pwm::driver::SetScale(this->internal_session, scale); + return ResultSuccess(); + } + + Result GetScale(ams::sf::Out out) { + out.SetValue(pwm::driver::GetScale(this->internal_session)); + return ResultSuccess(); + } + }; + static_assert(pwm::sf::IsIChannelSession); + +} diff --git a/libraries/libstratosphere/source/pwm/server/pwm_server_manager_impl.cpp b/libraries/libstratosphere/source/pwm/server/pwm_server_manager_impl.cpp new file mode 100644 index 000000000..afc232984 --- /dev/null +++ b/libraries/libstratosphere/source/pwm/server/pwm_server_manager_impl.cpp @@ -0,0 +1,51 @@ +/* + * 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 . + */ +#include +#include "pwm_server_manager_impl.hpp" + +namespace ams::pwm::server { + + ManagerImpl::ManagerImpl() : session_memory_resource(), allocator(std::addressof(session_memory_resource)) { + this->heap_handle = lmem::CreateExpHeap(this->heap_buffer, sizeof(this->heap_buffer), lmem::CreateOption_None); + this->session_memory_resource.Attach(this->heap_handle); + } + + ManagerImpl::~ManagerImpl() { + lmem::DestroyExpHeap(this->heap_handle); + } + + Result ManagerImpl::OpenSessionForDev(ams::sf::Out> out, int channel) { + /* TODO */ + AMS_ABORT(); + } + + Result ManagerImpl::OpenSession(ams::sf::Out> out, pwm::ChannelName channel_name) { + return this->OpenSession2(out, ConvertToDeviceCode(channel_name)); + } + + Result ManagerImpl::OpenSession2(ams::sf::Out> out, DeviceCode device_code) { + /* Allocate a session. */ + auto session = ams::sf::AllocateShared(this->allocator, this); + + /* Open the session. */ + R_TRY(session->GetImpl().OpenSession(device_code)); + + /* We succeeded. */ + out.SetValue(std::move(session)); + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/pwm/server/pwm_server_manager_impl.hpp b/libraries/libstratosphere/source/pwm/server/pwm_server_manager_impl.hpp new file mode 100644 index 000000000..53662153d --- /dev/null +++ b/libraries/libstratosphere/source/pwm/server/pwm_server_manager_impl.hpp @@ -0,0 +1,40 @@ +/* + * 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 . + */ +#pragma once +#include +#include "pwm_server_channel_session_impl.hpp" + +namespace ams::pwm::server { + + class ManagerImpl { + private: + lmem::HeapHandle heap_handle; + ams::sf::ExpHeapMemoryResource session_memory_resource; + typename ams::sf::ServiceObjectAllocator allocator; + u8 heap_buffer[4_KB]; + public: + ManagerImpl(); + + ~ManagerImpl(); + public: + /* Actual commands. */ + Result OpenSessionForDev(ams::sf::Out> out, int channel); + Result OpenSession(ams::sf::Out> out, pwm::ChannelName channel_name); + Result OpenSession2(ams::sf::Out> out, DeviceCode device_code); + }; + static_assert(pwm::sf::IsIManager); + +} diff --git a/libraries/libstratosphere/source/result/result_on_assertion.cpp b/libraries/libstratosphere/source/result/result_on_assertion.cpp index 7d7d13724..7705c561f 100644 --- a/libraries/libstratosphere/source/result/result_on_assertion.cpp +++ b/libraries/libstratosphere/source/result/result_on_assertion.cpp @@ -33,7 +33,7 @@ namespace ams::result::impl { /* TODO: ams::fatal:: */ fatalThrow(result.GetValue()); - while (true) { /* ... */ } + AMS_INFINITE_LOOP(); } NORETURN WEAK_SYMBOL void OnResultAbort(Result result) { @@ -50,7 +50,7 @@ namespace ams::result::impl { /* TODO: ams::fatal:: */ fatalThrow(result.GetValue()); - while (true) { /* ... */ } + AMS_INFINITE_LOOP(); } NORETURN WEAK_SYMBOL void OnResultAssertion(Result result) { diff --git a/libraries/libstratosphere/source/service_guard.h b/libraries/libstratosphere/source/service_guard.h index 588a0c995..d7ce9a7a9 100644 --- a/libraries/libstratosphere/source/service_guard.h +++ b/libraries/libstratosphere/source/service_guard.h @@ -1,14 +1,15 @@ #pragma once + +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include #include #include -#ifdef __cplusplus -extern "C" { -#endif - typedef struct ServiceGuard { Mutex mutex; u32 refCount; diff --git a/libraries/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp b/libraries/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp index 868ba493a..92ad96395 100644 --- a/libraries/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp +++ b/libraries/libstratosphere/source/sf/cmif/sf_cmif_service_dispatch.cpp @@ -43,16 +43,19 @@ namespace ams::sf::cmif { CmifOutHeader *out_header = nullptr; Result command_result = cmd_handler(&out_header, ctx, in_message_raw_data); - /* Forward forwardable results, otherwise ensure we can send result to user. */ - R_TRY_CATCH(command_result) { - R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged) - R_CATCH_ALL() { AMS_ABORT_UNLESS(out_header != nullptr); } - } R_END_TRY_CATCH; + /* Forward any meta-context change result. */ + if (sf::impl::ResultRequestContextChanged::Includes(command_result)) { + return command_result; + } + + /* Otherwise, ensure that we're able to write the output header. */ + if (out_header == nullptr) { + AMS_ABORT_UNLESS(R_FAILED(command_result)); + return command_result; + } /* Write output header to raw data. */ - if (out_header != nullptr) { - *out_header = CmifOutHeader{CMIF_OUT_HEADER_MAGIC, 0, command_result.GetValue(), 0}; - } + *out_header = CmifOutHeader{CMIF_OUT_HEADER_MAGIC, 0, command_result.GetValue(), 0}; return ResultSuccess(); } @@ -87,19 +90,24 @@ namespace ams::sf::cmif { CmifOutHeader *out_header = nullptr; Result command_result = cmd_handler(&out_header, ctx, in_message_raw_data); - /* Forward forwardable results, otherwise ensure we can send result to user. */ - R_TRY_CATCH(command_result) { - R_CATCH(sm::mitm::ResultShouldForwardToSession) { - return ctx.session->ForwardRequest(ctx); - } - R_CATCH_RETHROW(sf::impl::ResultRequestContextChanged) - R_CATCH_ALL() { AMS_ABORT_UNLESS(out_header != nullptr); } - } R_END_TRY_CATCH; + /* If we should, forward the request to the forward session. */ + if (sm::mitm::ResultShouldForwardToSession::Includes(command_result)) { + return ctx.session->ForwardRequest(ctx); + } + + /* Forward any meta-context change result. */ + if (sf::impl::ResultRequestContextChanged::Includes(command_result)) { + return command_result; + } + + /* Otherwise, ensure that we're able to write the output header. */ + if (out_header == nullptr) { + AMS_ABORT_UNLESS(R_FAILED(command_result)); + return command_result; + } /* Write output header to raw data. */ - if (out_header != nullptr) { - *out_header = CmifOutHeader{CMIF_OUT_HEADER_MAGIC, 0, command_result.GetValue(), 0}; - } + *out_header = CmifOutHeader{CMIF_OUT_HEADER_MAGIC, 0, command_result.GetValue(), 0}; return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp index c763dbae2..37b786262 100644 --- a/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp +++ b/libraries/libstratosphere/source/sf/hipc/sf_hipc_server_manager.cpp @@ -27,6 +27,10 @@ namespace ams::sf::hipc { /* Register the query handle. */ impl::RegisterMitmQueryHandle(query_handle, query_func); + + /* Clear future declarations if any, now that our query handler is present. */ + R_ABORT_UNLESS(sm::mitm::ClearFutureMitm(service_name)); + return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/sm/sm_ams.c b/libraries/libstratosphere/source/sm/sm_ams.c index 233c79378..1ceb23f88 100644 --- a/libraries/libstratosphere/source/sm/sm_ams.c +++ b/libraries/libstratosphere/source/sm/sm_ams.c @@ -107,6 +107,10 @@ Result smAtmosphereMitmDeclareFuture(SmServiceName name) { return _smAtmosphereCmdInServiceNameNoOut(name, smGetServiceSession(), 65006); } +Result smAtmosphereMitmClearFuture(SmServiceName name) { + return _smAtmosphereCmdInServiceNameNoOut(name, smGetServiceSession(), 65007); +} + Result smAtmosphereMitmAcknowledgeSession(Service *srv_out, void *_out, SmServiceName name) { struct { u64 process_id; diff --git a/libraries/libstratosphere/source/sm/sm_ams.h b/libraries/libstratosphere/source/sm/sm_ams.h index 3036cde94..3a4885ccb 100644 --- a/libraries/libstratosphere/source/sm/sm_ams.h +++ b/libraries/libstratosphere/source/sm/sm_ams.h @@ -5,9 +5,7 @@ * @copyright libnx Authors */ #pragma once -#include -#include -#include +#include #ifdef __cplusplus extern "C" { @@ -28,6 +26,7 @@ void smAtmosphereCloseSession(Service *srv); Result smAtmosphereMitmInstall(Service *fwd_srv, Handle *handle_out, Handle *query_out, SmServiceName name); Result smAtmosphereMitmUninstall(SmServiceName name); Result smAtmosphereMitmDeclareFuture(SmServiceName name); +Result smAtmosphereMitmClearFuture(SmServiceName name); Result smAtmosphereMitmAcknowledgeSession(Service *srv_out, void *info_out, SmServiceName name); #ifdef __cplusplus diff --git a/libraries/libstratosphere/source/sm/sm_mitm_api.cpp b/libraries/libstratosphere/source/sm/sm_mitm_api.cpp index 431c3b96c..8e5c80023 100644 --- a/libraries/libstratosphere/source/sm/sm_mitm_api.cpp +++ b/libraries/libstratosphere/source/sm/sm_mitm_api.cpp @@ -37,6 +37,12 @@ namespace ams::sm::mitm { }); } + Result ClearFutureMitm(ServiceName name) { + return impl::DoWithUserSession([&]() { + return smAtmosphereMitmClearFuture(impl::ConvertName(name)); + }); + } + Result AcknowledgeSession(Service *out_service, MitmProcessInfo *out_info, ServiceName name) { return impl::DoWithMitmAcknowledgementSession([&]() { return smAtmosphereMitmAcknowledgeSession(out_service, reinterpret_cast(out_info), impl::ConvertName(name)); diff --git a/libraries/libstratosphere/source/spl/smc/spl_smc.cpp b/libraries/libstratosphere/source/spl/smc/spl_smc.cpp index 02934c7a9..affed9de4 100644 --- a/libraries/libstratosphere/source/spl/smc/spl_smc.cpp +++ b/libraries/libstratosphere/source/spl/smc/spl_smc.cpp @@ -17,12 +17,12 @@ namespace ams::spl::smc { - Result SetConfig(spl::ConfigItem which, const u64 *value, size_t num_qwords) { + Result SetConfig(spl::ConfigItem which, const void *address, const u64 *value, size_t num_qwords) { SecmonArgs args; args.X[0] = static_cast(FunctionId::SetConfig); args.X[1] = static_cast(which); - args.X[2] = 0; + args.X[2] = reinterpret_cast(address); for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) { args.X[3 + i] = value[i]; } diff --git a/libraries/libstratosphere/source/spl/spl_api.cpp b/libraries/libstratosphere/source/spl/spl_api.cpp index f53b75379..ed5de7854 100644 --- a/libraries/libstratosphere/source/spl/spl_api.cpp +++ b/libraries/libstratosphere/source/spl/spl_api.cpp @@ -29,7 +29,7 @@ namespace ams::spl { Manu }; - os::Mutex g_mutex(false); + os::SdkMutex g_mutex; s32 g_initialize_count = 0; InitializeMode g_initialize_mode = InitializeMode::None; diff --git a/libraries/libstratosphere/source/updater/updater_api.cpp b/libraries/libstratosphere/source/updater/updater_api.cpp index 014ea117e..879a0eeec 100644 --- a/libraries/libstratosphere/source/updater/updater_api.cpp +++ b/libraries/libstratosphere/source/updater/updater_api.cpp @@ -158,9 +158,16 @@ namespace ams::updater { R_TRY(boot0_accessor.Initialize()); ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + /* Detect the use of custom public key. */ + /* If custom public key is present, we want to validate BCT Sub but not Main */ + bool custom_public_key = false; + R_TRY(boot0_accessor.DetectCustomPublicKey(std::addressof(custom_public_key), work_buffer, boot_image_update_type)); + /* Compare BCT hashes. */ - R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalMain)); - R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + if (!custom_public_key) { + R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalMain)); + R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctNormalMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + } /* Compare BCT Sub hashes. */ R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctNormalSub)); @@ -213,10 +220,16 @@ namespace ams::updater { R_TRY(boot1_accessor.Initialize()); ON_SCOPE_EXIT { boot1_accessor.Finalize(); }; + /* Detect the use of custom public key. */ + /* If custom public key is present, we want to validate BCT Sub but not Main */ + bool custom_public_key = false; + R_TRY(boot0_accessor.DetectCustomPublicKey(std::addressof(custom_public_key), work_buffer, boot_image_update_type)); /* Compare BCT hashes. */ - R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeMain)); - R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + if (!custom_public_key) { + R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeMain)); + R_TRY(ValidateBctFileHash(boot0_accessor, Boot0Partition::BctSafeMain, nand_hash, work_buffer, work_buffer_size, boot_image_update_type)); + } /* Compare BCT Sub hashes. */ R_TRY(boot0_accessor.GetHash(nand_hash, BctSize, work_buffer, work_buffer_size, Boot0Partition::BctSafeSub)); @@ -260,6 +273,11 @@ namespace ams::updater { R_TRY(boot0_accessor.Initialize()); ON_SCOPE_EXIT { boot0_accessor.Finalize(); }; + /* Detect the use of custom public key. */ + /* If custom public key is present, we want to update BCT Sub but not Main */ + bool custom_public_key = false; + R_TRY(boot0_accessor.DetectCustomPublicKey(std::addressof(custom_public_key), work_buffer, boot_image_update_type)); + /* Write Package1 sub. */ R_TRY(boot0_accessor.Clear(work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); R_TRY(boot0_accessor.Write(GetPackage1Path(boot_image_update_type), work_buffer, work_buffer_size, Boot0Partition::Package1NormalSub)); @@ -282,11 +300,15 @@ namespace ams::updater { if (HasAutoRcmPreserve(boot_image_update_type) && !exosphere::IsRcmBugPatched()) { R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalSub)); R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)); - R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalMain)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + if (!custom_public_key) { + R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctNormalMain)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + } } else { R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalSub)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + if (!custom_public_key) { + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctNormalMain)); + } } } @@ -321,6 +343,10 @@ namespace ams::updater { R_TRY(boot1_accessor.Initialize()); ON_SCOPE_EXIT { boot1_accessor.Finalize(); }; + /* Detect the use of custom public key. */ + /* If custom public key is present, we want to update BCT Sub but not Main */ + bool custom_public_key = false; + R_TRY(boot0_accessor.DetectCustomPublicKey(std::addressof(custom_public_key), work_buffer, boot_image_update_type)); /* Write Package1 sub. */ R_TRY(boot1_accessor.Clear(work_buffer, work_buffer_size, Boot1Partition::Package1SafeSub)); @@ -343,11 +369,15 @@ namespace ams::updater { if (HasAutoRcmPreserve(boot_image_update_type) && !exosphere::IsRcmBugPatched()) { R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeSub)); R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)); - R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeMain)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + if (!custom_public_key) { + R_TRY(boot0_accessor.PreserveAutoRcm(bct, work, Boot0Partition::BctSafeMain)); + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + } } else { R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeSub)); - R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + if (!custom_public_key) { + R_TRY(boot0_accessor.Write(bct, BctSize, Boot0Partition::BctSafeMain)); + } } } diff --git a/libraries/libstratosphere/source/updater/updater_bis_management.cpp b/libraries/libstratosphere/source/updater/updater_bis_management.cpp index b643634bf..0822f8ec7 100644 --- a/libraries/libstratosphere/source/updater/updater_bis_management.cpp +++ b/libraries/libstratosphere/source/updater/updater_bis_management.cpp @@ -18,6 +18,36 @@ namespace ams::updater { + namespace { + + /* Recognize special public key (https://gist.github.com/SciresM/16b63ac1d80494522bdba2c57995257c). */ + /* P = 19 */ + /* Q = 1696986749729493925354392349339746171297507422986462747526968361144447230710192316397327889522451749459854070558277878297255552508603806832852079596337539247651161831569525505882103311631577368514276343192042634740927726070847704397913856975832811679847928433261678072951551065705680482548543833651752439700272736498378724153330763357721354498194000536297732323628263256733931353143625854828275237159155585342783077681713929284136658773985266864804093157854331138230313706015557050002740810464618031715670281442110238274404626065924786185264268216336867948322976979393032640085259926883014490947373494538254895109731 */ + /* N = 0xFF696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696959 */ + /* E = 0x10001 */ + /* D = 6512128715229088976470211610075969347035078304643231077895577077900787352712063823560162578441773733649014439616165727455431015055675770987914713980812453585413988983206576233689754710500864883529402371292948326392791238474661859182717295176679567362482790015587820446999760239570255254879359445627372805817473978644067558931078225451477635089763009580492462097185005355990612929951162366081631888011031830742459571000203341001926135389508196521518687349554188686396554248868403128728646457247197407637887195043486221295751496667162366700477934591694110831002874992896076061627516220934290742867397720103040314639313 */ + + constexpr const u8 CustomPublicKey[0x100] = { + 0x59, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0xFF, + }; + + } + Result BisAccessor::Initialize() { return fs::OpenBisPartition(std::addressof(this->storage), this->partition_id); } @@ -133,11 +163,36 @@ namespace ams::updater { size_t read_size; R_TRY(this->Read(&read_size, work_buffer, BctSize, which)); - void *dst_pubk = reinterpret_cast(reinterpret_cast(dst_bct) + BctPubkOffset); - void *src_pubk = reinterpret_cast(reinterpret_cast(work_buffer) + BctPubkOffset); + /* NOTE: AutoRcm is only viable on Erista, so hardcode erista offsets. */ + void *dst_pubk = reinterpret_cast(reinterpret_cast(dst_bct) + BctPubkOffsetErista); + void *src_pubk = reinterpret_cast(reinterpret_cast(work_buffer) + BctPubkOffsetErista); std::memcpy(dst_pubk, src_pubk, BctPubkSize); return ResultSuccess(); } + Result Boot0Accessor::DetectCustomPublicKey(bool *out, void *work_buffer, BootImageUpdateType boot_image_update_type) { + std::memset(work_buffer, 0, BctSize); + + const size_t pubk_offset = GetBctPubkOffset(boot_image_update_type); + + size_t read_size; + R_TRY(this->Read(&read_size, work_buffer, BctSize, Boot0Partition::BctNormalMain)); + + if (std::memcmp(reinterpret_cast(reinterpret_cast(work_buffer) + pubk_offset), CustomPublicKey, sizeof(CustomPublicKey)) != 0) { + *out = false; + return ResultSuccess(); + } + + R_TRY(this->Read(&read_size, work_buffer, BctSize, Boot0Partition::BctSafeMain)); + + if (std::memcmp(reinterpret_cast(reinterpret_cast(work_buffer) + pubk_offset), CustomPublicKey, sizeof(CustomPublicKey)) != 0) { + *out = false; + return ResultSuccess(); + } + + *out = true; + return ResultSuccess(); + } + } diff --git a/libraries/libstratosphere/source/updater/updater_bis_management.hpp b/libraries/libstratosphere/source/updater/updater_bis_management.hpp index c98333f6e..868285f00 100644 --- a/libraries/libstratosphere/source/updater/updater_bis_management.hpp +++ b/libraries/libstratosphere/source/updater/updater_bis_management.hpp @@ -199,7 +199,8 @@ namespace ams::updater { class Boot0Accessor : public PartitionAccessor { public: static constexpr fs::BisPartitionId PartitionId = fs::BisPartitionId::BootPartition1Root; - static constexpr size_t BctPubkOffset = 0x210; + static constexpr size_t BctPubkOffsetErista = 0x210; + static constexpr size_t BctPubkOffsetMariko = 0x10; static constexpr size_t BctPubkSize = 0x100; static constexpr size_t BctEksOffset = 0x450; static constexpr size_t BctVersionOffset = 0x2330; @@ -210,10 +211,22 @@ namespace ams::updater { static size_t GetBootloaderVersion(void *bct); static size_t GetEksIndex(size_t bootloader_version); static void CopyEks(void *dst_bct, const void *src_eks, size_t eks_index); + + static size_t GetBctPubkOffset(BootImageUpdateType boot_image_update_type) { + switch (boot_image_update_type) { + case BootImageUpdateType::Erista: + return BctPubkOffsetErista; + case BootImageUpdateType::Mariko: + return BctPubkOffsetMariko; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } public: Result UpdateEks(void *dst_bct, void *eks_work_buffer); Result UpdateEksManually(void *dst_bct, const void *src_eks); Result PreserveAutoRcm(void *dst_bct, void *work_buffer, Boot0Partition which); + + Result DetectCustomPublicKey(bool *out, void *work_buffer, BootImageUpdateType boot_image_update_type); }; class Boot1Accessor : public PartitionAccessor { diff --git a/libraries/libstratosphere/source/wec/wec_api.cpp b/libraries/libstratosphere/source/wec/wec_api.cpp new file mode 100644 index 000000000..0b3f3213e --- /dev/null +++ b/libraries/libstratosphere/source/wec/wec_api.cpp @@ -0,0 +1,112 @@ +/* + * 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 . + */ +#include + +/* TODO: How much of this should be namespaced under BOARD_NINTENDO_NX? */ + +namespace ams::wec { + + namespace { + + constexpr inline dd::PhysicalAddress ApbdevPmc = 0x7000E400; + + constinit bool g_initialized = false; + + void UpdateControlBit(dd::PhysicalAddress phys_addr, u32 mask, bool flag) { + dd::ReadModifyWriteIoRegister(phys_addr, flag ? ~0u : 0u, mask); + dd::ReadIoRegister(phys_addr); + } + + void Initialize(bool blink) { + /* Initialize WAKE_DEBOUNCE_EN. */ + dd::WriteIoRegister(ApbdevPmc + APBDEV_PMC_WAKE_DEBOUNCE_EN, 0); + dd::ReadIoRegister(ApbdevPmc + APBDEV_PMC_WAKE_DEBOUNCE_EN); + + /* Initialize BLINK_TIMER. */ + dd::WriteIoRegister(ApbdevPmc + APBDEV_PMC_BLINK_TIMER, 0x08008800); + dd::ReadIoRegister(ApbdevPmc + APBDEV_PMC_BLINK_TIMER); + + /* Set control configs. */ + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0800, true); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0400, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0200, true); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0100, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0040, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0020, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL2, 0x4000, true); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL2, 0x0200, false); + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL2, 0x0001, true); + + /* Update blink bit in APBDEV_PMC_CNTRL. */ + UpdateControlBit(ApbdevPmc + APBDEV_PMC_CNTRL, 0x0080, blink); + + /* Update blink bit in APBDEV_PMC_DPD_PADS_ORIDE. */ + UpdateControlBit(ApbdevPmc + APBDEV_PMC_DPD_PADS_ORIDE, 0x100000, blink); + } + + } + + void Initialize() { + /* Set initial wake configuration. */ + if (!g_initialized) { + Initialize(false); + g_initialized = true; + } + } + + void ClearWakeEvents() { + /* TODO */ + AMS_ABORT(); + } + + void WecRestoreForExitSuspend() { + /* TODO */ + AMS_ABORT(); + } + + void SetWakeEventLevel(wec::WakeEvent event, wec::WakeEventLevel level) { + if (g_initialized && event < wec::WakeEvent_Count) { + /* Determine the event index. */ + const bool which = static_cast(event) < BITSIZEOF(u32); + const u32 index = static_cast(event) & (BITSIZEOF(u32) - 1); + + /* Get the level and auto_mask offsets. */ + u32 level_ofs = which ? APBDEV_PMC_WAKE_LVL : APBDEV_PMC_WAKE2_LVL; + u32 auto_mask_ofs = which ? APBDEV_PMC_AUTO_WAKE_LVL_MASK : APBDEV_PMC_AUTO_WAKE2_LVL_MASK; + + /* If the level isn't auto, swap the offsets. */ + if (level != wec::WakeEventLevel_Auto) { + std::swap(level_ofs, auto_mask_ofs); + } + + /* Clear the bit in the level register. */ + UpdateControlBit(ApbdevPmc + level_ofs, (1u << index), false); + + /* Set or clear the bit in the auto mask register. */ + UpdateControlBit(ApbdevPmc + auto_mask_ofs, (1u << index), level != wec::WakeEventLevel_Low); + } + } + + void SetWakeEventEnabled(wec::WakeEvent event, bool en) { + if (g_initialized && event < wec::WakeEvent_Count) { + /* Set or clear the relevant enabled bit. */ + const u32 offset = static_cast(event) < BITSIZEOF(u32) ? APBDEV_PMC_WAKE_MASK : APBDEV_PMC_WAKE2_MASK; + const u32 index = static_cast(event) & (BITSIZEOF(u32) - 1); + UpdateControlBit(ApbdevPmc + offset, (1u << index), en); + } + } + +} diff --git a/libraries/libvapours/include/freebsd/sys/tree.h b/libraries/libvapours/include/freebsd/sys/tree.h index fecde6913..d18ee1821 100644 --- a/libraries/libvapours/include/freebsd/sys/tree.h +++ b/libraries/libvapours/include/freebsd/sys/tree.h @@ -425,21 +425,35 @@ struct { \ /* Main rb operation. * Moves node close to the key of elm to top */ -#define RB_GENERATE(name, type, field, cmp) \ - RB_GENERATE_INTERNAL(name, type, field, cmp,) -#define RB_GENERATE_STATIC(name, type, field, cmp) \ - RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) -#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ - RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ +#define RB_GENERATE_WITHOUT_COMPARE(name, type, field) \ + RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field,) +#define RB_GENERATE_WITHOUT_COMPARE_STATIC(name, type, field) \ + RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, __unused static) +#define RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, attr) \ RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \ - RB_GENERATE_INSERT(name, type, field, cmp, attr) \ RB_GENERATE_REMOVE(name, type, field, attr) \ - RB_GENERATE_FIND(name, type, field, cmp, attr) \ - RB_GENERATE_NFIND(name, type, field, cmp, attr) \ RB_GENERATE_NEXT(name, type, field, attr) \ RB_GENERATE_PREV(name, type, field, attr) \ RB_GENERATE_MINMAX(name, type, field, attr) +#define RB_GENERATE_WITH_COMPARE(name, type, field, cmp) \ + RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_WITH_COMPARE_STATIC(name, type, field, cmp) \ + RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, attr) \ + RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ + RB_GENERATE_INSERT(name, type, field, cmp, attr) \ + RB_GENERATE_FIND(name, type, field, cmp, attr) \ + RB_GENERATE_NFIND(name, type, field, cmp, attr) + +#define RB_GENERATE_ALL(name, type, field, cmp) \ + RB_GENERATE_ALL_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_ALL_STATIC(name, type, field, cmp) \ + RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, __unused static) +#define RB_GENERATE_ALL_INTERNAL(name, type, field, cmp, attr) \ + RB_GENERATE_WITHOUT_COMPARE_INTERNAL(name, type, field, attr) \ + RB_GENERATE_WITH_COMPARE_INTERNAL(name, type, field, cmp, attr) + #define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ diff --git a/libraries/libvapours/include/vapours.hpp b/libraries/libvapours/include/vapours.hpp index 9da48965b..a47ec53bc 100644 --- a/libraries/libvapours/include/vapours.hpp +++ b/libraries/libvapours/include/vapours.hpp @@ -20,10 +20,22 @@ #include #include +#include #include #include #include #include +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) +#include +#endif + #include #include + +#include + +#include +#include \ No newline at end of file diff --git a/libraries/libvapours/include/vapours/ams/ams_api_version.h b/libraries/libvapours/include/vapours/ams/ams_api_version.h index 0a087ea81..b91673a31 100644 --- a/libraries/libvapours/include/vapours/ams/ams_api_version.h +++ b/libraries/libvapours/include/vapours/ams/ams_api_version.h @@ -16,11 +16,11 @@ #pragma once #define ATMOSPHERE_RELEASE_VERSION_MAJOR 0 -#define ATMOSPHERE_RELEASE_VERSION_MINOR 14 -#define ATMOSPHERE_RELEASE_VERSION_MICRO 1 +#define ATMOSPHERE_RELEASE_VERSION_MINOR 15 +#define ATMOSPHERE_RELEASE_VERSION_MICRO 0 #define ATMOSPHERE_RELEASE_VERSION ATMOSPHERE_RELEASE_VERSION_MAJOR, ATMOSPHERE_RELEASE_VERSION_MINOR, ATMOSPHERE_RELEASE_VERSION_MICRO #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MAJOR 10 -#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 1 +#define ATMOSPHERE_SUPPORTED_HOS_VERSION_MINOR 2 #define ATMOSPHERE_SUPPORTED_HOS_VERSION_MICRO 0 diff --git a/libraries/libvapours/include/vapours/ams/ams_fatal_error_context.hpp b/libraries/libvapours/include/vapours/ams/ams_fatal_error_context.hpp new file mode 100644 index 000000000..986d6c48f --- /dev/null +++ b/libraries/libvapours/include/vapours/ams/ams_fatal_error_context.hpp @@ -0,0 +1,66 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::impl { + + struct FatalErrorContext { + static constexpr size_t MaxStackTrace = 0x20; + static constexpr size_t MaxStackDumpSize = 0x100; + static constexpr size_t ThreadLocalSize = 0x100; + static constexpr size_t NumGprs = 29; + static constexpr uintptr_t StdAbortMagicAddress = 0x8; + static constexpr u64 StdAbortMagicValue = 0xA55AF00DDEADCAFEul; + static constexpr u32 StdAbortErrorDesc = 0xFFE; + static constexpr u32 StackOverflowErrorDesc = 0xFFD; + static constexpr u32 KernelPanicDesc = 0xF00; + static constexpr u32 DataAbortErrorDesc = 0x101; + static constexpr u32 Magic = util::FourCC<'A', 'F', 'E', '2'>::Code; + + u32 magic; + u32 error_desc; + u64 program_id; + union { + u64 gprs[32]; + struct { + u64 _gprs[29]; + u64 fp; + u64 lr; + u64 sp; + }; + }; + u64 pc; + u64 module_base; + u32 pstate; + u32 afsr0; + u32 afsr1; + u32 esr; + u64 far; + u64 report_identifier; /* Normally just system tick. */ + u64 stack_trace_size; + u64 stack_dump_size; + u64 stack_trace[MaxStackTrace]; + u8 stack_dump[MaxStackDumpSize]; + u8 tls[ThreadLocalSize]; + }; + + static_assert(sizeof(FatalErrorContext) == 0x450); + static_assert(std::is_standard_layout::value); + static_assert(std::is_trivial::value); + +} diff --git a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h index 0e9de3cb3..010a83198 100644 --- a/libraries/libvapours/include/vapours/ams/ams_target_firmware.h +++ b/libraries/libvapours/include/vapours/ams/ams_target_firmware.h @@ -55,8 +55,9 @@ #define ATMOSPHERE_TARGET_FIRMWARE_10_0_4 ATMOSPHERE_TARGET_FIRMWARE(10, 0, 4) #define ATMOSPHERE_TARGET_FIRMWARE_10_1_0 ATMOSPHERE_TARGET_FIRMWARE(10, 1, 0) #define ATMOSPHERE_TARGET_FIRMWARE_10_1_1 ATMOSPHERE_TARGET_FIRMWARE(10, 1, 1) +#define ATMOSPHERE_TARGET_FIRMWARE_10_2_0 ATMOSPHERE_TARGET_FIRMWARE(10, 2, 0) -#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_10_1_1 +#define ATMOSPHERE_TARGET_FIRMWARE_CURRENT ATMOSPHERE_TARGET_FIRMWARE_10_2_0 #define ATMOSPHERE_TARGET_FIRMWARE_MIN ATMOSPHERE_TARGET_FIRMWARE(0, 0, 0) #define ATMOSPHERE_TARGET_FIRMWARE_MAX ATMOSPHERE_TARGET_FIRMWARE_CURRENT @@ -104,6 +105,7 @@ namespace ams { TargetFirmware_10_0_4 = ATMOSPHERE_TARGET_FIRMWARE_10_0_4, TargetFirmware_10_1_0 = ATMOSPHERE_TARGET_FIRMWARE_10_1_0, TargetFirmware_10_1_1 = ATMOSPHERE_TARGET_FIRMWARE_10_1_1, + TargetFirmware_10_2_0 = ATMOSPHERE_TARGET_FIRMWARE_10_2_0, TargetFirmware_Current = ATMOSPHERE_TARGET_FIRMWARE_CURRENT, diff --git a/libraries/libvapours/include/vapours/assert.hpp b/libraries/libvapours/include/vapours/assert.hpp index 8cd61bd61..79882bd44 100644 --- a/libraries/libvapours/include/vapours/assert.hpp +++ b/libraries/libvapours/include/vapours/assert.hpp @@ -16,15 +16,6 @@ #pragma once #include -namespace ams::impl { - - template - constexpr ALWAYS_INLINE void UnusedImpl(ArgTypes... args) { - (static_cast(args), ...); - } - -} - namespace ams::diag { NORETURN NOINLINE void AssertionFailureImpl(const char *file, int line, const char *func, const char *expr, u64 value, const char *format, ...) __attribute__((format(printf, 6, 7))); @@ -36,14 +27,12 @@ namespace ams::diag { } -#define AMS_UNUSED(...) ::ams::impl::UnusedImpl(__VA_ARGS__) - -#ifdef AMS_ENABLE_DEBUG_PRINT +#ifdef AMS_ENABLE_DETAILED_ASSERTIONS #define AMS_CALL_ASSERT_FAIL_IMPL(cond, ...) ::ams::diag::AssertionFailureImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, 0, ## __VA_ARGS__) #define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl(__FILE__, __LINE__, __PRETTY_FUNCTION__, cond, 0, ## __VA_ARGS__) #else #define AMS_CALL_ASSERT_FAIL_IMPL(cond, ...) ::ams::diag::AssertionFailureImpl("", 0, "", "", 0) -#define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl() +#define AMS_CALL_ABORT_IMPL(cond, ...) ::ams::diag::AbortImpl(); AMS_UNUSED(cond, ## __VA_ARGS__) #endif #ifdef AMS_ENABLE_ASSERTIONS diff --git a/libraries/libvapours/include/vapours/common.hpp b/libraries/libvapours/include/vapours/common.hpp index 5bd762731..b037b31d4 100644 --- a/libraries/libvapours/include/vapours/common.hpp +++ b/libraries/libvapours/include/vapours/common.hpp @@ -17,15 +17,33 @@ #include #include -#if 0 -#define AMS_BUILD_FOR_AUDITING +#if defined(AMS_FORCE_DISABLE_DETAILED_ASSERTIONS) && defined(AMS_FORCE_ENABLE_DETAILED_ASSERTIONS) + #error "Invalid detailed assertions state" #endif #ifdef AMS_BUILD_FOR_AUDITING -#define AMS_BUILD_FOR_DEBUGGING + + #define AMS_BUILD_FOR_DEBUGGING + + #if !defined(AMS_FORCE_DISABLE_DETAILED_ASSERTIONS) + #define AMS_ENABLE_DETAILED_ASSERTIONS + #endif + #endif #ifdef AMS_BUILD_FOR_DEBUGGING -#define AMS_ENABLE_ASSERTIONS -#define AMS_ENABLE_DEBUG_PRINT + + #define AMS_ENABLE_ASSERTIONS + + #if !defined(AMS_ENABLE_DETAILED_ASSERTIONS) && !defined(AMS_FORCE_DISABLE_DETAILED_ASSERTIONS) + + #if !defined(ATMOSPHERE_IS_EXOSPHERE) || defined(AMS_FORCE_ENABLE_DETAILED_ASSERTIONS) + + #define AMS_ENABLE_DETAILED_ASSERTIONS + + #endif + + #endif + + #endif diff --git a/libraries/libvapours/include/vapours/dd.hpp b/libraries/libvapours/include/vapours/dd.hpp new file mode 100644 index 000000000..52d3e9cd0 --- /dev/null +++ b/libraries/libvapours/include/vapours/dd.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include + +#include +#include +#include diff --git a/libraries/libvapours/include/vapours/dd/dd_cache.hpp b/libraries/libvapours/include/vapours/dd/dd_cache.hpp new file mode 100644 index 000000000..8ea0e200a --- /dev/null +++ b/libraries/libvapours/include/vapours/dd/dd_cache.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::dd { + + void InvalidateDataCache(void *addr, size_t size); + void StoreDataCache(void *addr, size_t size); + void FlushDataCache(void *addr, size_t size); + +} diff --git a/libraries/libvapours/include/vapours/dd/dd_common_types.hpp b/libraries/libvapours/include/vapours/dd/dd_common_types.hpp new file mode 100644 index 000000000..25a533b0f --- /dev/null +++ b/libraries/libvapours/include/vapours/dd/dd_common_types.hpp @@ -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 . + */ + +#pragma once +#include +#include +#include + +namespace ams::dd { + + using PhysicalAddress = u64; + using DeviceVirtualAddress = u64; + + static_assert(std::same_as); + +} diff --git a/libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp b/libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp new file mode 100644 index 000000000..4a3303774 --- /dev/null +++ b/libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp @@ -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 . + */ +#pragma once +#include + +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); + u32 ReadModifyWriteIoRegister(PhysicalAddress phys_addr, u32 value, u32 mask); + +} diff --git a/libraries/libvapours/include/vapours/defines.hpp b/libraries/libvapours/include/vapours/defines.hpp index a4be456b7..82356f1a6 100644 --- a/libraries/libvapours/include/vapours/defines.hpp +++ b/libraries/libvapours/include/vapours/defines.hpp @@ -65,3 +65,20 @@ #define AMS_ASSUME(expr) do { if (!static_cast((expr))) { __builtin_unreachable(); } } while (0) #define AMS_CURRENT_FUNCTION_NAME __FUNCTION__ + +#if defined(__cplusplus) + +namespace ams::impl { + + template + constexpr ALWAYS_INLINE void UnusedImpl(ArgTypes &&... args) { + (static_cast(args), ...); + } + +} + +#endif + +#define AMS_UNUSED(...) ::ams::impl::UnusedImpl(__VA_ARGS__) + +#define AMS_INFINITE_LOOP() do { __asm__ __volatile__("" ::: "memory"); } while (1) diff --git a/libraries/libvapours/include/vapours/device_code.hpp b/libraries/libvapours/include/vapours/device_code.hpp new file mode 100644 index 000000000..c39abcaf2 --- /dev/null +++ b/libraries/libvapours/include/vapours/device_code.hpp @@ -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 . + */ + +#pragma once +#include +#include + +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); + +} diff --git a/libraries/libexosphere/include/exosphere/reg.hpp b/libraries/libvapours/include/vapours/reg.hpp similarity index 58% rename from libraries/libexosphere/include/exosphere/reg.hpp rename to libraries/libvapours/include/vapours/reg.hpp index 1ac314110..7cf770312 100644 --- a/libraries/libexosphere/include/exosphere/reg.hpp +++ b/libraries/libvapours/include/vapours/reg.hpp @@ -18,6 +18,9 @@ namespace ams::reg { + template + concept UnsignedNonConstIntegral = std::unsigned_integral && !std::is_const::value; + using BitsValue = std::tuple; using BitsMask = std::tuple; @@ -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 requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + constexpr ALWAYS_INLINE u32 EncodeMask(const Masks... masks) { + return (EncodeMask(masks) | ...); + } + + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void Write(volatile IntType *reg, std::type_identity_t val) { *reg = val; } + + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void Write(volatile IntType ®, std::type_identity_t val) { reg = val; } + ALWAYS_INLINE void Write(uintptr_t reg, u32 val) { Write(reinterpret_cast(reg), val); } - template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void Write(volatile u32 *reg, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } + template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void Write(volatile IntType *reg, const Values... values) { return Write(reg, static_cast((EncodeValue(values) | ...))); } - template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void Write(volatile u32 ®, const Values... values) { return Write(reg, (EncodeValue(values) | ...)); } + template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void Write(volatile IntType ®, const Values... values) { return Write(reg, static_cast((EncodeValue(values) | ...))); } template requires ((sizeof...(Values) > 0) && (std::is_same::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 requires UnsignedNonConstIntegral + ALWAYS_INLINE IntType Read(volatile IntType *reg) { return *reg; } + + template requires UnsignedNonConstIntegral + ALWAYS_INLINE IntType Read(volatile IntType ®) { return reg; } + ALWAYS_INLINE u32 Read(uintptr_t reg) { return Read(reinterpret_cast(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 requires UnsignedNonConstIntegral + ALWAYS_INLINE IntType Read(volatile IntType *reg, std::type_identity_t mask) { return *reg & mask; } + + template requires UnsignedNonConstIntegral + ALWAYS_INLINE IntType Read(volatile IntType ®, std::type_identity_t mask) { return reg & mask; } + ALWAYS_INLINE u32 Read(uintptr_t reg, u32 mask) { return Read(reinterpret_cast(reg), mask); } - template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE u32 Read(volatile u32 *reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } + template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE IntType Read(volatile IntType *reg, const Masks... masks) { return Read(reg, static_cast((EncodeMask(masks) | ...))); } + + template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE IntType Read(volatile IntType ®, const Masks... masks) { return Read(reg, static_cast((EncodeMask(masks) | ...))); } template requires ((sizeof...(Masks) > 0) && (std::is_same::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 requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE u32 Read(uintptr_t reg, const Masks... masks) { return Read(reg, (EncodeMask(masks) | ...)); } + template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE bool HasValue(volatile IntType *reg, const Values... values) { return Read(reg, static_cast((EncodeMask(values) | ...))) == static_cast(Encode(values...)); } - template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE bool HasValue(volatile u32 *reg, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } - - template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE bool HasValue(volatile u32 ®, const Values... values) { return Read(reg, (EncodeMask(values) | ...)) == Encode(values...); } + template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE bool HasValue(volatile IntType ®, const Values... values) { return Read(reg, static_cast((EncodeMask(values) | ...))) == static_cast(Encode(values...)); } template requires ((sizeof...(Values) > 0) && (std::is_same::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 requires UnsignedNonConstIntegral + ALWAYS_INLINE IntType GetValue(volatile IntType *reg, const BitsMask mask) { return Read(reg, mask) >> GetOffset(mask); } + + template requires UnsignedNonConstIntegral + 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 requires UnsignedNonConstIntegral + ALWAYS_INLINE void ReadWrite(volatile IntType *reg, std::type_identity_t val, std::type_identity_t mask) { *reg = (*reg & (~mask)) | (val & mask); } + + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void ReadWrite(volatile IntType ®, std::type_identity_t val, std::type_identity_t 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(reg), val, mask); } - template requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void ReadWrite(volatile u32 *reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } + template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ReadWrite(volatile IntType *reg, const Values... values) { return ReadWrite(reg, static_cast((EncodeValue(values) | ...)), static_cast((EncodeMask(values) | ...))); } + + template requires UnsignedNonConstIntegral && ((sizeof...(Values) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ReadWrite(volatile IntType ®, const Values... values) { return ReadWrite(reg, static_cast((EncodeValue(values) | ...)), static_cast((EncodeMask(values) | ...))); } template requires ((sizeof...(Values) > 0) && (std::is_same::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 requires ((sizeof...(Values) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void ReadWrite(uintptr_t reg, const Values... values) { return ReadWrite(reg, (EncodeValue(values) | ...), (EncodeMask(values) | ...)); } + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void SetBits(volatile IntType *reg, std::type_identity_t mask) { *reg = *reg | mask; } + + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void SetBits(volatile IntType ®, std::type_identity_t 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(reg), mask); } - template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void SetBits(volatile u32 *reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } + template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void SetBits(volatile IntType *reg, const Masks... masks) { return SetBits(reg, static_cast((EncodeMask(masks) | ...))); } + + template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void SetBits(volatile IntType ®, const Masks... masks) { return SetBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires ((sizeof...(Masks) > 0) && (std::is_same::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 requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void SetBits(uintptr_t reg, const Masks... masks) { return SetBits(reg, (EncodeMask(masks) | ...)); } + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void ClearBits(volatile IntType *reg, std::type_identity_t mask) { *reg = *reg & ~mask; } + + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void ClearBits(volatile IntType ®, std::type_identity_t 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(reg), mask); } - template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void ClearBits(volatile u32 *reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } + template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ClearBits(volatile IntType *reg, const Masks... masks) { return ClearBits(reg, static_cast((EncodeMask(masks) | ...))); } + + template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void ClearBits(volatile IntType ®, const Masks... masks) { return ClearBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires ((sizeof...(Masks) > 0) && (std::is_same::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 requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void ClearBits(uintptr_t reg, const Masks... masks) { return ClearBits(reg, (EncodeMask(masks) | ...)); } + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void MaskBits(volatile IntType *reg, std::type_identity_t mask) { *reg = *reg & mask; } + + template requires UnsignedNonConstIntegral + ALWAYS_INLINE void MaskBits(volatile IntType ®, std::type_identity_t 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(reg), mask); } - template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void MaskBits(volatile u32 *reg, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } + template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void MaskBits(volatile IntType *reg, const Masks... masks) { return MaskBits(reg, static_cast((EncodeMask(masks) | ...))); } + + template requires UnsignedNonConstIntegral && ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) + ALWAYS_INLINE void MaskBits(volatile IntType ®, const Masks... masks) { return MaskBits(reg, static_cast((EncodeMask(masks) | ...))); } template requires ((sizeof...(Masks) > 0) && (std::is_same::value && ...)) - ALWAYS_INLINE void MaskBits(volatile u32 ®, const Masks... masks) { return MaskBits(reg, (EncodeMask(masks) | ...)); } - - template requires ((sizeof...(Masks) > 0) && (std::is_same::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} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 4d4c41a56..f10e5af32 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -26,12 +26,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include +#include #include #include #include @@ -41,10 +44,14 @@ #include #include #include +#include #include #include +#include #include +#include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/dd_results.hpp b/libraries/libvapours/include/vapours/results/dd_results.hpp new file mode 100644 index 000000000..d64a49181 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/dd_results.hpp @@ -0,0 +1,35 @@ +/* + * 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 . + */ + +#pragma once +#include + +namespace ams::dd { + + R_DEFINE_NAMESPACE_RESULT_MODULE(6); + + R_DEFINE_ERROR_RESULT(EndOfQuery, 1); + R_DEFINE_ERROR_RESULT(InvalidCurrentMemory, 2); + R_DEFINE_ERROR_RESULT(NotSingleRegion, 3); + R_DEFINE_ERROR_RESULT(InvalidMemoryState, 4); + R_DEFINE_ERROR_RESULT(OutOfMemory, 5); + R_DEFINE_ERROR_RESULT(OutOfResource, 6); + R_DEFINE_ERROR_RESULT(NotSupported, 7); + R_DEFINE_ERROR_RESULT(InvalidHandle, 8); + + R_DEFINE_ERROR_RESULT(InternalError, 1023); + +} diff --git a/stratosphere/boot/source/pinmux/pinmux_utils.hpp b/libraries/libvapours/include/vapours/results/ddsf_results.hpp similarity index 61% rename from stratosphere/boot/source/pinmux/pinmux_utils.hpp rename to libraries/libvapours/include/vapours/results/ddsf_results.hpp index 06332a57d..9ea4d667b 100644 --- a/stratosphere/boot/source/pinmux/pinmux_utils.hpp +++ b/libraries/libvapours/include/vapours/results/ddsf_results.hpp @@ -13,19 +13,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #pragma once -#include +#include -namespace ams::pinmux { +namespace ams::ddsf { - /* Pinmux Utilities. */ - u32 UpdatePark(u32 pinmux_name); - u32 UpdatePad(u32 pinmux_name, u32 config_val, u32 config_mask); - u32 UpdateDrivePad(u32 pinmux_drivepad_name, u32 config_val, u32 config_mask); - u32 DummyReadDrivePad(u32 pinmux_drivepad_name); + R_DEFINE_NAMESPACE_RESULT_MODULE(30); - /* Helper API. */ - void UpdateAllParks(); - void DummyReadAllDrivePads(); + R_DEFINE_ERROR_RESULT(OutOfResource, 1); + R_DEFINE_ERROR_RESULT(NotSupported, 2); + R_DEFINE_ERROR_RESULT(InvalidArgument, 3); + R_DEFINE_ERROR_RESULT(PermissionDenied, 4); + R_DEFINE_ERROR_RESULT(AccessModeDenied, 5); + R_DEFINE_ERROR_RESULT(DeviceCodeNotFound, 6); } diff --git a/libraries/libvapours/include/vapours/results/gpio_results.hpp b/libraries/libvapours/include/vapours/results/gpio_results.hpp new file mode 100644 index 000000000..546a602e7 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/gpio_results.hpp @@ -0,0 +1,31 @@ +/* + * 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 . + */ + +#pragma once +#include + +namespace ams::gpio { + + R_DEFINE_NAMESPACE_RESULT_MODULE(102); + + R_DEFINE_ERROR_RESULT(AlreadyBound, 1); + R_DEFINE_ERROR_RESULT(AlreadyOpen, 2); + R_DEFINE_ERROR_RESULT(DeviceNotFound, 3); + R_DEFINE_ERROR_RESULT(InvalidArgument, 4); + + R_DEFINE_ERROR_RESULT(NotOpen, 6); + +} diff --git a/libraries/libvapours/include/vapours/results/i2c_results.hpp b/libraries/libvapours/include/vapours/results/i2c_results.hpp index 62cb26eb7..0ba35650b 100644 --- a/libraries/libvapours/include/vapours/results/i2c_results.hpp +++ b/libraries/libvapours/include/vapours/results/i2c_results.hpp @@ -23,8 +23,10 @@ namespace ams::i2c { R_DEFINE_ERROR_RESULT(NoAck, 1); R_DEFINE_ERROR_RESULT(BusBusy, 2); - R_DEFINE_ERROR_RESULT(FullCommandList, 3); - R_DEFINE_ERROR_RESULT(TimedOut, 4); + R_DEFINE_ERROR_RESULT(CommandListFull, 3); + R_DEFINE_ERROR_RESULT(UnknownDevice, 5); + R_DEFINE_ERROR_RESULT(Timeout, 253); + } diff --git a/libraries/libvapours/include/vapours/results/loader_results.hpp b/libraries/libvapours/include/vapours/results/loader_results.hpp index 1ffa7ae13..8f21b6ed8 100644 --- a/libraries/libvapours/include/vapours/results/loader_results.hpp +++ b/libraries/libvapours/include/vapours/results/loader_results.hpp @@ -54,6 +54,7 @@ namespace ams::ldr { R_DEFINE_ERROR_RESULT(InvalidCapabilitySyscallMask, 104); R_DEFINE_ERROR_RESULT(InvalidCapabilityMapRange, 106); R_DEFINE_ERROR_RESULT(InvalidCapabilityMapPage, 107); + R_DEFINE_ERROR_RESULT(InvalidCapabilityMapRegion, 110); R_DEFINE_ERROR_RESULT(InvalidCapabilityInterruptPair, 111); R_DEFINE_ERROR_RESULT(InvalidCapabilityApplicationType, 113); R_DEFINE_ERROR_RESULT(InvalidCapabilityKernelVersion, 114); diff --git a/libraries/libvapours/include/vapours/results/pcv_results.hpp b/libraries/libvapours/include/vapours/results/pcv_results.hpp new file mode 100644 index 000000000..ec0aa6d46 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/pcv_results.hpp @@ -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 . + */ + +#pragma once +#include + +namespace ams::pcv { + + R_DEFINE_NAMESPACE_RESULT_MODULE(133); + + R_DEFINE_ERROR_RESULT(IllegalRequest, 16); + +} diff --git a/libraries/libvapours/include/vapours/results/powctl_results.hpp b/libraries/libvapours/include/vapours/results/powctl_results.hpp new file mode 100644 index 000000000..b071ee1ff --- /dev/null +++ b/libraries/libvapours/include/vapours/results/powctl_results.hpp @@ -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 . + */ + +#pragma once +#include + +namespace ams::powctl { + + R_DEFINE_NAMESPACE_RESULT_MODULE(198); + + R_DEFINE_ERROR_RESULT(NotSupported, 1); + R_DEFINE_ERROR_RESULT(InvalidArgument, 2); + R_DEFINE_ERROR_RESULT(NotAvailable, 3); + +} diff --git a/libraries/libvapours/include/vapours/results/pwm_results.hpp b/libraries/libvapours/include/vapours/results/pwm_results.hpp new file mode 100644 index 000000000..37041beb5 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/pwm_results.hpp @@ -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 . + */ + +#pragma once +#include + +namespace ams::pwm { + + R_DEFINE_NAMESPACE_RESULT_MODULE(189); + + R_DEFINE_ERROR_RESULT(InvalidArgument, 2); + +} diff --git a/libraries/libvapours/include/vapours/results/results_common.hpp b/libraries/libvapours/include/vapours/results/results_common.hpp index 0e2fecc97..13d32aff6 100644 --- a/libraries/libvapours/include/vapours/results/results_common.hpp +++ b/libraries/libvapours/include/vapours/results/results_common.hpp @@ -83,7 +83,7 @@ namespace ams { constexpr Result(typename Base::BaseType v) : value(v) { static_assert(std::is_same::value); } constexpr ALWAYS_INLINE operator ResultSuccess() const; - NX_CONSTEXPR bool CanAccept(Result result) { return true; } + NX_CONSTEXPR bool CanAccept(Result) { return true; } constexpr ALWAYS_INLINE bool IsSuccess() const { return this->GetValue() == Base::SuccessValue; } constexpr ALWAYS_INLINE bool IsFailure() const { return !this->IsSuccess(); } diff --git a/libraries/libvapours/include/vapours/results/ro_results.hpp b/libraries/libvapours/include/vapours/results/ro_results.hpp index fc4dbe16f..8614e400f 100644 --- a/libraries/libvapours/include/vapours/results/ro_results.hpp +++ b/libraries/libvapours/include/vapours/results/ro_results.hpp @@ -30,7 +30,7 @@ namespace ams::ro { R_DEFINE_ERROR_RESULT(TooManyNro, 7); R_DEFINE_ERROR_RESULT(TooManyNrr, 8); R_DEFINE_ERROR_RESULT(NotAuthorized, 9); - R_DEFINE_ERROR_RESULT(InvalidNrrType, 10); + R_DEFINE_ERROR_RESULT(InvalidNrrKind, 10); R_DEFINE_ERROR_RESULT(InternalError, 1023); diff --git a/libraries/libvapours/include/vapours/results/sdmmc_results.hpp b/libraries/libvapours/include/vapours/results/sdmmc_results.hpp new file mode 100644 index 000000000..a6d305cc6 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/sdmmc_results.hpp @@ -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 . + */ + +#pragma once +#include + +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); +} diff --git a/stratosphere/boot/source/boot_registers_gpio.hpp b/libraries/libvapours/include/vapours/sdmmc.hpp similarity index 64% rename from stratosphere/boot/source/boot_registers_gpio.hpp rename to libraries/libvapours/include/vapours/sdmmc.hpp index 321735164..444071a17 100644 --- a/stratosphere/boot/source/boot_registers_gpio.hpp +++ b/libraries/libvapours/include/vapours/sdmmc.hpp @@ -13,13 +13,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #pragma once -#include +#include +#include +#include +#include +#include -static constexpr size_t GPIO_PORT3_CNF_0 = 0x200; -static constexpr size_t GPIO_PORT3_OE_0 = 0x210; -static constexpr size_t GPIO_PORT3_OUT_0 = 0x220; - -static constexpr size_t GPIO_PORT6_CNF_1 = 0x504; -static constexpr size_t GPIO_PORT6_OE_1 = 0x514; -static constexpr size_t GPIO_PORT6_OUT_1 = 0x524; +#include +#include +#include +#include +#include diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp new file mode 100644 index 000000000..97d6560c1 --- /dev/null +++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.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 . + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +#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 diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp new file mode 100644 index 000000000..e06976fce --- /dev/null +++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_common.hpp @@ -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 . + */ + +#pragma once +#include + +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); + +} \ No newline at end of file diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_gc_asic.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_gc_asic.hpp new file mode 100644 index 000000000..a1355de55 --- /dev/null +++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_gc_asic.hpp @@ -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 . + */ +#pragma once +#include + +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); + +} diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_mmc.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_mmc.hpp new file mode 100644 index 000000000..39d9e7390 --- /dev/null +++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_mmc.hpp @@ -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 . + */ + +#pragma once +#include + + +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); + +} \ No newline at end of file diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_sd_card.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_sd_card.hpp new file mode 100644 index 000000000..1c1eff371 --- /dev/null +++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_sd_card.hpp @@ -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 . + */ +#pragma once +#include + +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); + +} diff --git a/libraries/libvapours/include/vapours/svc.hpp b/libraries/libvapours/include/vapours/svc.hpp index 2ce0d36aa..84e84f3f6 100644 --- a/libraries/libvapours/include/vapours/svc.hpp +++ b/libraries/libvapours/include/vapours/svc.hpp @@ -21,4 +21,5 @@ #include #include +#include #include diff --git a/libraries/libvapours/include/vapours/svc/board/generic/svc_device_name.hpp b/libraries/libvapours/include/vapours/svc/board/generic/svc_device_name.hpp new file mode 100644 index 000000000..4de1ba2bd --- /dev/null +++ b/libraries/libvapours/include/vapours/svc/board/generic/svc_device_name.hpp @@ -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 . + */ +#pragma once +#include + +namespace ams::svc::board::generic { + + enum DeviceName { + /* If there is no smmu, there are no device names. */ + DeviceName_Count = 0, + }; + +} diff --git a/libraries/libvapours/include/vapours/svc/board/nintendo/nx/svc_device_name.hpp b/libraries/libvapours/include/vapours/svc/board/nintendo/nx/svc_device_name.hpp index fb289dd83..9700a1929 100644 --- a/libraries/libvapours/include/vapours/svc/board/nintendo/nx/svc_device_name.hpp +++ b/libraries/libvapours/include/vapours/svc/board/nintendo/nx/svc_device_name.hpp @@ -62,4 +62,16 @@ namespace ams::svc::board::nintendo::nx { DeviceName_Count, }; + namespace impl { + + constexpr inline const size_t RequiredNonSecureSystemMemorySizeVi = 0x2238 * 4_KB; + constexpr inline const size_t RequiredNonSecureSystemMemorySizeNvservices = 0x710 * 4_KB; + constexpr inline const size_t RequiredNonSecureSystemMemorySizeMisc = 0x80 * 4_KB; + + } + + constexpr inline const size_t RequiredNonSecureSystemMemorySize = impl::RequiredNonSecureSystemMemorySizeVi + + impl::RequiredNonSecureSystemMemorySizeNvservices + + impl::RequiredNonSecureSystemMemorySizeMisc; + } diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp index de6d25b32..e0f1998c2 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_kernel_svc_wrapper.hpp @@ -353,7 +353,7 @@ namespace ams::svc::codegen::impl { }; template - static constexpr auto GetModifiedOperations(std::tuple ops) { + static constexpr auto GetModifiedOperations(std::tuple) { constexpr size_t ModifyRegister = [] { auto allocator = Allocator; return allocator.AllocateFirstFree(); @@ -535,7 +535,11 @@ namespace ams::svc::codegen::impl { GenerateCodeForMetaCode(); ON_SCOPE_EXIT { GenerateCodeForMetaCode(); }; + /* Cast the generated function to the generic funciton pointer type. */ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wcast-function-type" return reinterpret_cast(Function)(); + #pragma GCC diagnostic pop } #pragma GCC pop_options diff --git a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp index 1b0189925..57584be41 100644 --- a/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp +++ b/libraries/libvapours/include/vapours/svc/codegen/impl/svc_codegen_impl_layout_conversion.hpp @@ -139,7 +139,7 @@ namespace ams::svc::codegen::impl { template static constexpr void GenerateCodeForPrepareForKernelProcedureToSvcInvocation(MetaCodeGenerator &mcg) { static_assert(Operation::Kind == OperationKind::PackAndUnpack); - /* ... */ + AMS_UNUSED(mcg); } template diff --git a/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp b/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp index af3b3cb5c..28c63dbd8 100644 --- a/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp +++ b/libraries/libvapours/include/vapours/svc/ipc/svc_message_buffer.hpp @@ -498,6 +498,7 @@ namespace ams::svc::ipc { } static constexpr ALWAYS_INLINE s32 GetSpecialDataIndex(const MessageHeader &hdr, const SpecialHeader &spc) { + AMS_UNUSED(hdr); return (MessageHeader::GetDataSize() / sizeof(util::BitPack32)) + (spc.GetHeaderSize() / sizeof(util::BitPack32)); } diff --git a/libraries/libvapours/include/vapours/svc/svc_definitions.hpp b/libraries/libvapours/include/vapours/svc/svc_definitions.hpp index 558051a49..51bb6faee 100644 --- a/libraries/libvapours/include/vapours/svc/svc_definitions.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_definitions.hpp @@ -151,6 +151,7 @@ HANDLER(0x7E, Result, SetResourceLimitLimitValue, INPUT(::ams::svc::Handle, resource_limit_handle), INPUT(::ams::svc::LimitableResource, which), INPUT(int64_t, limit_value)) \ HANDLER(0x7F, void, CallSecureMonitor, OUTPUT(::ams::svc::NAMESPACE::SecureMonitorArguments, args)) \ \ + HANDLER(0x2E, Result, LegacyGetFutureThreadInfo, OUTPUT(::ams::svc::NAMESPACE::LastThreadContext, out_context), OUTPUT(::ams::svc::Address, out_tls_address), OUTPUT(uint32_t, out_flags), INPUT(int64_t, ns)) \ HANDLER(0x55, Result, LegacyQueryIoMapping, OUTPUT(::ams::svc::Address, out_address), INPUT(::ams::svc::PhysicalAddress, physical_address), INPUT(::ams::svc::Size, size)) \ HANDLER(0x64, Result, LegacyContinueDebugEvent, INPUT(::ams::svc::Handle, debug_handle), INPUT(uint32_t, flags), INPUT(uint64_t, thread_id)) diff --git a/libraries/libvapours/include/vapours/svc/svc_select_device_name.hpp b/libraries/libvapours/include/vapours/svc/svc_select_device_name.hpp index 5435c6b93..5db94155c 100644 --- a/libraries/libvapours/include/vapours/svc/svc_select_device_name.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_select_device_name.hpp @@ -26,6 +26,9 @@ #else - #error "Unknown board for svc::DeviceName" + #include + namespace ams::svc { + using namespace ams::svc::board::generic; + } #endif diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index 4b8260b7d..29f4fa1eb 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -157,6 +157,8 @@ namespace ams::svc { InfoType_UsedNonSystemMemorySize = 22, InfoType_IsApplication = 23, + InfoType_MesosphereMeta = 65000, + InfoType_ThreadTickCount = 0xF0000002, }; @@ -169,6 +171,11 @@ namespace ams::svc { TickCountInfo_Total = std::numeric_limits::max(), }; + enum MesosphereMetaInfo : u64 { + MesosphereMetaInfo_KernelVersion = 0, + MesosphereMetaInfo_IsKTraceEnabled = 1, + }; + enum SystemInfoType : u32 { SystemInfoType_TotalPhysicalMemorySize = 0, SystemInfoType_UsedPhysicalMemorySize = 1, diff --git a/libraries/libvapours/include/vapours/svc/svc_version.hpp b/libraries/libvapours/include/vapours/svc/svc_version.hpp new file mode 100644 index 000000000..b4ec65d61 --- /dev/null +++ b/libraries/libvapours/include/vapours/svc/svc_version.hpp @@ -0,0 +1,65 @@ +/* + * 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 . + */ + +#pragma once +#include +#include +#include + +namespace ams::svc { + + constexpr inline u32 ConvertToSvcMajorVersion(u32 sdk) { return sdk + 4; } + constexpr inline u32 ConvertToSdkMajorVersion(u32 svc) { return svc - 4; } + + constexpr inline u32 ConvertToSvcMinorVersion(u32 sdk) { return sdk; } + constexpr inline u32 ConvertToSdkMinorVersion(u32 svc) { return svc; } + + struct KernelVersion { + using MinorVersion = util::BitPack32::Field<0, 4>; + using MajorVersion = util::BitPack32::Field; + }; + + constexpr inline u32 EncodeKernelVersion(u32 major, u32 minor) { + util::BitPack32 pack = {}; + pack.Set(minor); + pack.Set(major); + return pack.value; + } + + constexpr inline u32 GetKernelMajorVersion(u32 encoded) { + const util::BitPack32 pack = { encoded }; + return pack.Get(); + } + + constexpr inline u32 GetKernelMinorVersion(u32 encoded) { + const util::BitPack32 pack = { encoded }; + return pack.Get(); + } + + /* Nintendo doesn't support programs targeting SVC versions < 3.0. */ + constexpr inline u32 RequiredKernelMajorVersion = 3; + constexpr inline u32 RequiredKernelMinorVersion = 0; + + constexpr inline u32 RequiredKernelVersion = EncodeKernelVersion(RequiredKernelMajorVersion, RequiredKernelMinorVersion); + + /* This is the highest SVC version supported by Atmosphere, to be updated on new kernel releases. */ + /* NOTE: Official kernel versions have SVC major = SDK major + 4, SVC minor = SDK minor. */ + constexpr inline u32 SupportedKernelMajorVersion = ConvertToSvcMajorVersion(10); + constexpr inline u32 SupportedKernelMinorVersion = ConvertToSvcMinorVersion( 4); + + constexpr inline u32 SupportedKernelVersion = EncodeKernelVersion(SupportedKernelMajorVersion, SupportedKernelMinorVersion); + +} diff --git a/libraries/libvapours/include/vapours/tegra.hpp b/libraries/libvapours/include/vapours/tegra.hpp new file mode 100644 index 000000000..ac69ce1b2 --- /dev/null +++ b/libraries/libvapours/include/vapours/tegra.hpp @@ -0,0 +1,42 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp b/libraries/libvapours/include/vapours/tegra/tegra_ahb_arbc.hpp similarity index 94% rename from libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_ahb_arbc.hpp index b130ada97..37066da05 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_ahb_arbc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_ahb_arbc.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define AHB_ARBC(x) (0x6000c000 + x) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp b/libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp similarity index 71% rename from libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp index 4ad9de8ba..47fa9bcca 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp @@ -14,11 +14,27 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include -#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) + +/* Mariko only */ +#define APB_MISC_GP_DSI_PAD_CONTROL (0xAC0) #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 +57,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); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp b/libraries/libvapours/include/vapours/tegra/tegra_avp_cache.hpp similarity index 93% rename from libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_avp_cache.hpp index 52e4c7748..d0d97738a 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_avp_cache.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_avp_cache.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define AVP_CACHE_ADDRESS(x) (0x50040000 + x) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libvapours/include/vapours/tegra/tegra_clkrst.hpp similarity index 62% rename from libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_clkrst.hpp index ae22a0f70..212000363 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_clkrst.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include /* 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,29 @@ 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_PWM (0x110) +#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_I2C2 (0x198) +#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0) +#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 (0x1B8) +#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_I2C4 (0x3C4) +#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_I2C6 (0x65C) +#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) @@ -133,12 +161,36 @@ DEFINE_CLK_RST_REG_TWO_BIT_ENUM(SPARE_REG0_CLK_M_DIVISOR, 2, CLK_M_DIVISOR1, CLK #define CLK_RST_CONTROLLER_CLK_ENB_V_CLR (0x444) #define CLK_RST_CONTROLLER_CLK_ENB_W_CLR (0x44C) +/* RST_*_INDEX */ +#define CLK_RST_CONTROLLER_RST_I2C1_INDEX (0x0C) +#define CLK_RST_CONTROLLER_RST_I2C2_INDEX (0x16) +#define CLK_RST_CONTROLLER_RST_I2C3_INDEX (0x03) +#define CLK_RST_CONTROLLER_RST_I2C4_INDEX (0x07) +#define CLK_RST_CONTROLLER_RST_I2C5_INDEX (0x0F) +#define CLK_RST_CONTROLLER_RST_I2C6_INDEX (0x06) + +#define CLK_RST_CONTROLLER_RST_PWM_INDEX (0x11) + +#define CLK_RST_CONTROLLER_RST_UARTA_INDEX (0x06) +#define CLK_RST_CONTROLLER_RST_UARTB_INDEX (0x07) +#define CLK_RST_CONTROLLER_RST_UARTC_INDEX (0x17) + +#define CLK_RST_CONTROLLER_RST_ACTMON_INDEX (0x17) + /* CLK_ENB_*_INDEX */ #define CLK_RST_CONTROLLER_CLK_ENB_I2C1_INDEX (0x0C) +#define CLK_RST_CONTROLLER_CLK_ENB_I2C2_INDEX (0x16) +#define CLK_RST_CONTROLLER_CLK_ENB_I2C3_INDEX (0x03) +#define CLK_RST_CONTROLLER_CLK_ENB_I2C4_INDEX (0x07) #define CLK_RST_CONTROLLER_CLK_ENB_I2C5_INDEX (0x0F) +#define CLK_RST_CONTROLLER_CLK_ENB_I2C6_INDEX (0x06) + +#define CLK_RST_CONTROLLER_CLK_ENB_PWM_INDEX (0x11) + #define CLK_RST_CONTROLLER_CLK_ENB_UARTA_INDEX (0x06) #define CLK_RST_CONTROLLER_CLK_ENB_UARTB_INDEX (0x07) #define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) + #define CLK_RST_CONTROLLER_CLK_ENB_ACTMON_INDEX (0x17) /* RST_CPUG_CMPLX_* */ @@ -158,11 +210,27 @@ DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC2_LEGACY_TMCLK_OVR_ON, 29, O DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC3_LEGACY_TMCLK_OVR_ON, 30, OFF, ON); DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_SDMMC4_LEGACY_TMCLK_OVR_ON, 31, OFF, ON); +DEFINE_CLK_RST_REG(CLK_SOURCE_CLK_DIVISOR, 0, 8); +DEFINE_CLK_RST_REG(CLK_SOURCE_CLK_SOURCE, 29, 3); + DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C1_I2C1_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); 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); @@ -172,12 +240,21 @@ DEFINE_CLK_RST_REG(CLK_SOURCE_MSELECT_MSELECT_CLK_DIVISOR, 0, 8); DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_ACTMON_ACTMON_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, CLK_S, PLLC4_OUT1, CLK_M, PLLC4_OUT2); +DEFINE_CLK_RST_REG(CLK_SOURCE_DSIA_LP_DSIA_LP_CLK_DIVISOR, 0, 8); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DSIA_LP_DSIA_LP_CLK_SRC, 29, PLLP_OUT0, RSVD1, PLLC_OUT0, PLLC4_OUT0, PLLC4_OUT1, PLLC4_OUT2, CLK_M, RSVD7); + DEFINE_CLK_RST_REG(CLK_SOURCE_DVFS_REF_DVFS_REF_DIVISOR, 0, 8); DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2); 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_UART_FST_MIPI_CAL_UART_FST_MIPI_CAL_CLK_DIVISOR, 0, 8); +DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UART_FST_MIPI_CAL_UART_FST_MIPI_CAL_CLK_SRC, 29, PLLP_OUT3, PLLC_OUT0, PLLC2_OUT0_2, RSVD3, PLLC2_OUT0_4, RSVD5, CLK_M, RSVD7); + +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); @@ -193,54 +270,65 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_CORERESET3, 19, DISABLE, ENA DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENABLE); /* TODO: Actually include all devices. */ -#define CLK_RST_FOREACH_DEVICE(HANDLER) \ - HANDLER(L, CPU, 0, 0) \ - HANDLER(L, RTC, 0, 4) \ - HANDLER(L, TMR, 0, 5) \ - HANDLER(L, GPIO, 0, 8) \ - HANDLER(L, USBD, 0, 22) \ - HANDLER(L, CACHE2, 0, 31) \ - HANDLER(H, MEM, 1, 0) \ - HANDLER(H, AHBDMA, 1, 1) \ - HANDLER(H, APBDMA, 1, 2) \ - HANDLER(H, USB2, 1, 26) \ - HANDLER(H, PMC, 1, 6) \ - HANDLER(H, FUSE, 1, 7) \ - HANDLER(H, KFUSE, 1, 8) \ - HANDLER(H, I2C5, 1, 15) \ - HANDLER(H, EMC, 1, 25) \ - HANDLER(U, CSITE, 2, 9) \ - HANDLER(U, IRAMA, 2, 20) \ - HANDLER(U, IRAMB, 2, 21) \ - HANDLER(U, IRAMC, 2, 22) \ - HANDLER(U, IRAMD, 2, 23) \ - HANDLER(U, CRAM2, 2, 24) \ - HANDLER(V, CPUG, 3, 0) \ - HANDLER(V, MSELECT, 3, 3) \ - HANDLER(V, SPDIF_DOUBLER, 3, 22) \ - HANDLER(V, ACTMON, 3, 23) \ - HANDLER(V, TZRAM, 3, 30) \ - HANDLER(V, SE, 3, 31) \ - HANDLER(W, PCIERX0, 4, 2) \ - HANDLER(W, PCIERX1, 4, 3) \ - HANDLER(W, PCIERX2, 4, 4) \ - HANDLER(W, PCIERX3, 4, 5) \ - HANDLER(W, PCIERX4, 4, 6) \ - HANDLER(W, PCIERX5, 4, 7) \ - HANDLER(W, ENTROPY, 4, 21) \ - HANDLER(W, DVFS, 4, 27) \ - HANDLER(W, MC1, 4, 30) \ - HANDLER(X, MC_CAPA, 5, 7) \ - HANDLER(X, MC_CBPA, 5, 8) \ - HANDLER(X, MC_CPU, 5, 9) \ - HANDLER(X, MC_BBC, 5, 10) \ - HANDLER(X, EMC_DLL, 5, 14) \ - HANDLER(X, GPU, 5, 24) \ - HANDLER(X, DBGAPB, 5, 25) \ - HANDLER(X, PLLG_REF, 5, 29) \ - HANDLER(Y, MC_CCPA, 6, 8) \ - HANDLER(Y, MC_CDPA, 6, 9) \ - HANDLER(Y, PLLP_OUT_CPU, 6, 31) +#define CLK_RST_FOREACH_DEVICE(HANDLER) \ + HANDLER(L, CPU, 0, 0) \ + 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, DISP1, 0, 27) \ + HANDLER(L, HOST1X, 0, 28) \ + HANDLER(L, CACHE2, 0, 31) \ + HANDLER(H, MEM, 1, 0) \ + HANDLER(H, AHBDMA, 1, 1) \ + HANDLER(H, APBDMA, 1, 2) \ + HANDLER(H, PMC, 1, 6) \ + HANDLER(H, FUSE, 1, 7) \ + HANDLER(H, KFUSE, 1, 8) \ + HANDLER(H, I2C5, 1, 15) \ + HANDLER(H, DSI, 1, 16) \ + HANDLER(H, MIPI_CAL, 1, 24) \ + 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) \ + HANDLER(U, IRAMC, 2, 22) \ + HANDLER(U, IRAMD, 2, 23) \ + HANDLER(U, CRAM2, 2, 24) \ + HANDLER(V, CPUG, 3, 0) \ + HANDLER(V, MSELECT, 3, 3) \ + HANDLER(V, SPDIF_DOUBLER, 3, 22) \ + HANDLER(V, ACTMON, 3, 23) \ + HANDLER(V, TZRAM, 3, 30) \ + HANDLER(V, SE, 3, 31) \ + HANDLER(W, PCIERX0, 4, 2) \ + HANDLER(W, PCIERX1, 4, 3) \ + HANDLER(W, PCIERX2, 4, 4) \ + HANDLER(W, PCIERX3, 4, 5) \ + HANDLER(W, PCIERX4, 4, 6) \ + HANDLER(W, PCIERX5, 4, 7) \ + HANDLER(W, DSIA_LP, 4, 19) \ + HANDLER(W, ENTROPY, 4, 21) \ + HANDLER(W, DVFS, 4, 27) \ + HANDLER(W, MC1, 4, 30) \ + HANDLER(X, MC_CAPA, 5, 7) \ + HANDLER(X, MC_CBPA, 5, 8) \ + HANDLER(X, MC_CPU, 5, 9) \ + HANDLER(X, MC_BBC, 5, 10) \ + HANDLER(X, EMC_DLL, 5, 14) \ + HANDLER(X, UART_FST_MIPI_CAL, 5, 17) \ + 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) #define CLK_RST_DEFINE_SET_CLR_REG(REGISTER, DEVICE, REGISTER_INDEX, DEVICE_INDEX) \ DEFINE_CLK_RST_REG_BIT_ENUM(CLK_ENB_##REGISTER##_SET_SET_CLK_ENB_##DEVICE, DEVICE_INDEX, DISABLE, ENABLE); \ diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp b/libraries/libvapours/include/vapours/tegra/tegra_emc.hpp similarity index 97% rename from libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_emc.hpp index 7078ea199..1e1560d4c 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_emc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_emc.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define EMC_ADDRESS(x) (0x7001B000 + x) #define EMC0_ADDRESS(x) (0x7001E000 + x) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp b/libraries/libvapours/include/vapours/tegra/tegra_evp.hpp similarity index 85% rename from libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_evp.hpp index 24135720c..16f194021 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_evp.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_evp.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define EVP_CPU_RESET_VECTOR (0x100) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp b/libraries/libvapours/include/vapours/tegra/tegra_flow_ctlr.hpp similarity index 90% rename from libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_flow_ctlr.hpp index ef74c7f78..aa199b9d2 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_flow_ctlr.hpp @@ -14,12 +14,21 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include + +#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) diff --git a/libraries/libvapours/include/vapours/tegra/tegra_i2c.hpp b/libraries/libvapours/include/vapours/tegra/tegra_i2c.hpp new file mode 100644 index 000000000..3f7bd2a15 --- /dev/null +++ b/libraries/libvapours/include/vapours/tegra/tegra_i2c.hpp @@ -0,0 +1,137 @@ +/* + * 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 . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +#define I2C_I2C_CNFG (0x000) +#define I2C_I2C_CMD_ADDR0 (0x004) +#define I2C_I2C_CMD_DATA1 (0x00C) +#define I2C_I2C_STATUS (0x01C) +#define I2C_PACKET_TRANSFER_STATUS (0x058) +#define I2C_FIFO_CONTROL (0x05C) +#define I2C_FIFO_STATUS (0x060) +#define I2C_INTERRUPT_MASK_REGISTER (0x064) +#define I2C_INTERRUPT_STATUS_REGISTER (0x068) +#define I2C_CLK_DIVISOR_REGISTER (0x06C) +#define I2C_BUS_CLEAR_CONFIG (0x084) +#define I2C_BUS_CLEAR_STATUS (0x088) +#define I2C_CONFIG_LOAD (0x08C) +#define I2C_INTERFACE_TIMING_0 (0x094) +#define I2C_INTERFACE_TIMING_1 (0x098) +#define I2C_HS_INTERFACE_TIMING_0 (0x094) +#define I2C_HS_INTERFACE_TIMING_1 (0x098) + +#define I2C_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (I2C, NAME) +#define I2C_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (I2C, NAME, VALUE) +#define I2C_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (I2C, NAME, ENUM) +#define I2C_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(I2C, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_I2C_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (I2C, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_I2C_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_I2C_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_I2C_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_I2C_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (I2C, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +/* I2C_CNFG */ +DEFINE_I2C_REG(I2C_CNFG_LENGTH, 1, 3); +DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_CMD1, 6, WRITE, READ); +DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_SEND, 9, NOP, GO); +DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_PACKET_MODE_EN, 10, NOP, GO); +DEFINE_I2C_REG_BIT_ENUM(I2C_CNFG_NEW_MASTER_FSM, 11, DISABLE, ENABLE); +DEFINE_I2C_REG_THREE_BIT_ENUM(I2C_CNFG_DEBOUNCE_CNT, 12, NO_DEBOUNCE, DEBOUNCE_2T, DEBOUNCE_4T, DEBOUNCE_6T, DEBOUNCE_8T, DEBOUNCE_10T, DEBOUNCE_12T, DEBOUNCE_14T); + +/* I2C_CMD_ADDR0 */ +DEFINE_I2C_REG_BIT_ENUM(I2C_CMD_ADDR0_7BIT_RW, 0, WRITE, READ); +DEFINE_I2C_REG(I2C_CMD_ADDR0_7BIT_ADDR, 1, 7); + +/* I2C_STATUS */ +DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD1_STAT, 0, SL1_XFER_SUCCESSFUL, SL1_NOACK_FOR_BYTE1, SL1_NOACK_FOR_BYTE2, SL1_NOACK_FOR_BYTE3, SL1_NOACK_FOR_BYTE4, SL1_NOACK_FOR_BYTE5, SL1_NOACK_FOR_BYTE6, SL1_NOACK_FOR_BYTE7, SL1_NOACK_FOR_BYTE8, SL1_NOACK_FOR_BYTE9, SL1_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); +DEFINE_I2C_REG_FOUR_BIT_ENUM(I2C_STATUS_CMD2_STAT, 4, SL2_XFER_SUCCESSFUL, SL2_NOACK_FOR_BYTE1, SL2_NOACK_FOR_BYTE2, SL2_NOACK_FOR_BYTE3, SL2_NOACK_FOR_BYTE4, SL2_NOACK_FOR_BYTE5, SL2_NOACK_FOR_BYTE6, SL2_NOACK_FOR_BYTE7, SL2_NOACK_FOR_BYTE8, SL2_NOACK_FOR_BYTE9, SL2_NOACK_FOR_BYTE10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); +DEFINE_I2C_REG_BIT_ENUM(I2C_STATUS_BUSY, 8, NOT_BUSY, BUSY); + +/* PACKET_TRANSFER_STATUS */ +DEFINE_I2C_REG_BIT_ENUM(PACKET_TRANSFER_STATUS_CONTROLLER_BUSY, 0, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(PACKET_TRANSFER_STATUS_ARB_LOST, 1, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_DATA, 2, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(PACKET_TRANSFER_STATUS_NOACK_FOR_ADDR, 3, UNSET, SET); + +/* FIFO_CONTROL */ +DEFINE_I2C_REG_BIT_ENUM(FIFO_CONTROL_RX_FIFO_FLUSH, 0, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(FIFO_CONTROL_TX_FIFO_FLUSH, 1, UNSET, SET); + +DEFINE_I2C_REG_TWO_BIT_ENUM(FIFO_CONTROL_FIFO_FLUSH, 0, RX_UNSET_TX_UNSET, RX_SET_TX_UNSET, RX_UNSET_TX_SET, RX_SET_TX_SET); + +DEFINE_I2C_REG(FIFO_CONTROL_RX_FIFO_TRIG, 2, 3); +DEFINE_I2C_REG(FIFO_CONTROL_TX_FIFO_TRIG, 5, 3); + +/* FIFO_STATUS */ +DEFINE_I2C_REG(FIFO_STATUS_RX_FIFO_FULL_CNT, 0, 4); +DEFINE_I2C_REG(FIFO_STATUS_TX_FIFO_EMPTY_CNT, 4, 4); + +/* INTERRUPT_MASK_REGISTER */ +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_DATA_REQ_INT_EN, 0, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_DATA_REQ_INT_EN, 1, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_ARB_LOST_INT_EN, 2, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_NOACK_INT_EN, 3, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_RFIFO_UNF_INT_EN, 4, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_TFIFO_OVF_INT_EN, 5, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_ALL_PACKETS_XFER_COMPLETE_INT_EN, 6, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_MASK_REGISTER_PACKET_XFER_COMPLETE_INT_EN, 7, DISABLE, ENABLE); + +/* INTERRUPT_STATUS_REGISTER */ +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_DATA_REQ, 0, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_DATA_REQ, 1, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_ARB_LOST, 2, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_NOACK, 3, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_RFIFO_UNF, 4, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_TFIFO_OVF, 5, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_ALL_PACKETS_XFER_COMPLETE, 6, UNSET, SET); +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_PACKET_XFER_COMPLETE, 7, UNSET, SET); + +DEFINE_I2C_REG_BIT_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, 11, UNSET, SET); + +/* CLK_DIVISOR_REGISTER */ +DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_HSMODE, 0, 16); +DEFINE_I2C_REG(CLK_DIVISOR_REGISTER_STD_FAST_MODE, 16, 16); + +/* BUS_CLEAR_CONFIG */ +DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_ENABLE, 0, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_TERMINATE, 1, THRESHOLD, IMMEDIATE); +DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_CONFIG_BC_STOP_COND, 2, NO_STOP, STOP); +DEFINE_I2C_REG(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 16, 8); + +/* BUS_CLEAR_STATUS */ +DEFINE_I2C_REG_BIT_ENUM(BUS_CLEAR_STATUS_BC_STATUS, 0, NOT_CLEARED, CLEARED); + +/* CONFIG_LOAD */ +DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, 0, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_SLV_CONFIG_LOAD, 1, DISABLE, ENABLE); +DEFINE_I2C_REG_BIT_ENUM(CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, 2, DISABLE, ENABLE); +DEFINE_I2C_REG(CONFIG_LOAD_RESERVED_BIT_5, 5, 1); + +/* INTERFACE_TIMING_0 */ +DEFINE_I2C_REG(INTERFACE_TIMING_0_TLOW, 0, 6); +DEFINE_I2C_REG(INTERFACE_TIMING_0_THIGH, 8, 6); + +/* HS_INTERFACE_TIMING_0 */ +DEFINE_I2C_REG(HS_INTERFACE_TIMING_0_HS_TLOW, 0, 6); +DEFINE_I2C_REG(HS_INTERFACE_TIMING_0_HS_THIGH, 8, 6); + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp b/libraries/libvapours/include/vapours/tegra/tegra_ictlr.hpp similarity index 84% rename from libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_ictlr.hpp index 6f11ee918..773fccb02 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_ictlr.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_ictlr.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define PRI_ICTLR(n) (0x60004000 + n) #define SEC_ICTLR(n) (0x60004100 + n) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp b/libraries/libvapours/include/vapours/tegra/tegra_mc.hpp similarity index 99% rename from libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_mc.hpp index 107bf548d..093842554 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_mc.hpp @@ -14,8 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include -#include +#include +#include +#include +#include +#include +#include #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) diff --git a/libraries/libvapours/include/vapours/tegra/tegra_mipi_cal.hpp b/libraries/libvapours/include/vapours/tegra/tegra_mipi_cal.hpp new file mode 100644 index 000000000..efff2ed97 --- /dev/null +++ b/libraries/libvapours/include/vapours/tegra/tegra_mipi_cal.hpp @@ -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 . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +#define MIPI_CAL_MIPI_CAL_CTRL (0x000) +#define MIPI_CAL_CIL_MIPI_CAL_STATUS (0x008) +#define MIPI_CAL_CILA_MIPI_CAL_CONFIG (0x014) +#define MIPI_CAL_CILB_MIPI_CAL_CONFIG (0x018) +#define MIPI_CAL_CILC_MIPI_CAL_CONFIG (0x01C) +#define MIPI_CAL_CILD_MIPI_CAL_CONFIG (0x020) +#define MIPI_CAL_CILE_MIPI_CAL_CONFIG (0x024) +#define MIPI_CAL_CILF_MIPI_CAL_CONFIG (0x028) +#define MIPI_CAL_DSIA_MIPI_CAL_CONFIG (0x038) +#define MIPI_CAL_DSIB_MIPI_CAL_CONFIG (0x03C) +#define MIPI_CAL_DSIC_MIPI_CAL_CONFIG (0x040) +#define MIPI_CAL_DSID_MIPI_CAL_CONFIG (0x044) +#define MIPI_CAL_MIPI_BIAS_PAD_CFG0 (0x058) +#define MIPI_CAL_MIPI_BIAS_PAD_CFG1 (0x05C) +#define MIPI_CAL_MIPI_BIAS_PAD_CFG2 (0x060) +#define MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2 (0x064) +#define MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2 (0x068) +#define MIPI_CAL_DSIC_MIPI_CAL_CONFIG_2 (0x070) +#define MIPI_CAL_DSID_MIPI_CAL_CONFIG_2 (0x074) + +#define MIPI_CAL_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (MIPI_CAL, NAME) +#define MIPI_CAL_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (MIPI_CAL, NAME, VALUE) +#define MIPI_CAL_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (MIPI_CAL, NAME, ENUM) +#define MIPI_CAL_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(MIPI_CAL, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_MIPI_CAL_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (MIPI_CAL, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_MIPI_CAL_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (MIPI_CAL, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_MIPI_CAL_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (MIPI_CAL, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_MIPI_CAL_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(MIPI_CAL, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_MIPI_CAL_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (MIPI_CAL, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp b/libraries/libvapours/include/vapours/tegra/tegra_mselect.hpp similarity index 94% rename from libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_mselect.hpp index 6f0c5137b..eda67c8da 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_mselect.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_mselect.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define MSELECT(x) (0x50060000 + x) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pg_up.hpp b/libraries/libvapours/include/vapours/tegra/tegra_pg_up.hpp similarity index 81% rename from libraries/libexosphere/include/exosphere/tegra/tegra_pg_up.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_pg_up.hpp index a461d28d2..aa8c192fb 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pg_up.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_pg_up.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define PG_UP(x) (0x60000000 + x) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp b/libraries/libvapours/include/vapours/tegra/tegra_pinmux.hpp similarity index 75% rename from libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_pinmux.hpp index 8e5424880..c79d2d17a 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_pinmux.hpp @@ -14,7 +14,21 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include + +#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); @@ -65,4 +94,6 @@ DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_UART3_PM, 0, UARTC, SPI4, RSVD2, RSVD3); DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_DVFS_PWM_PM, 0, RSVD0, CLDVFS, SPI3, RSVD3); +DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_LCD_BL_PWM_PM, 0, DISPLAYA, PWM0, SOR0, RSVD3); + DEFINE_PINMUX_REG_TWO_BIT_ENUM(AUX_GPIO_PA6_PM, 0, SATA, RSVD1, RSVD2, RSVD3); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp similarity index 95% rename from libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp index ecd8d1860..17a35201b 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp @@ -14,8 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include -#include +#include +#include +#include +#include +#include +#include #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,9 +204,14 @@ 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); +DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3); +DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD2_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3); DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_CRAIL, 0, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(CLAMP_STATUS_TE, 1, DISABLE, ENABLE); diff --git a/libraries/libvapours/include/vapours/tegra/tegra_pwm.hpp b/libraries/libvapours/include/vapours/tegra/tegra_pwm.hpp new file mode 100644 index 000000000..3beadefe7 --- /dev/null +++ b/libraries/libvapours/include/vapours/tegra/tegra_pwm.hpp @@ -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 . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +#define PWM_CONTROLLER_PWM_CHANNEL_OFFSET(channel) (0x10 * channel) + +#define PWM_CONTROLLER_PWM_CSR (0x000) + + +#define PWM_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (PWM_CONTROLLER, NAME) +#define PWM_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (PWM_CONTROLLER, NAME, VALUE) +#define PWM_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (PWM_CONTROLLER, NAME, ENUM) +#define PWM_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(PWM_CONTROLLER, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_PWM_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (PWM_CONTROLLER, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_PWM_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (PWM_CONTROLLER, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_PWM_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (PWM_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_PWM_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(PWM_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_PWM_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (PWM_CONTROLLER, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_PWM_REG(PWM_CSR_PFM, 0, 13); +DEFINE_PWM_REG(PWM_CSR_PWM, 16, 15); +DEFINE_PWM_REG_BIT_ENUM(PWM_CSR_ENB, 31, DISABLE, ENABLE); + diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp b/libraries/libvapours/include/vapours/tegra/tegra_sb.hpp similarity index 94% rename from libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_sb.hpp index 83ea3f677..7e484eea6 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_sb.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_sb.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define SB_CSR (0x200) #define SB_PFCFG (0x208) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp b/libraries/libvapours/include/vapours/tegra/tegra_sysctr0.hpp similarity index 94% rename from libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_sysctr0.hpp index 650067eb6..b0393e23b 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_sysctr0.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define SYSCTR0_CNTCR (0x000) #define SYSCTR0_CNTCV0 (0x008) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp b/libraries/libvapours/include/vapours/tegra/tegra_timer.hpp similarity index 94% rename from libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_timer.hpp index ee3fe3dd1..f68241014 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_timer.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_timer.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define TIMERUS_USEC_CFG (0x014) #define TIMER_SHARED_TIMER_SECURE_CFG (0x1A4) diff --git a/libraries/libvapours/include/vapours/timespan.hpp b/libraries/libvapours/include/vapours/timespan.hpp index 0cca06a84..3219bf238 100644 --- a/libraries/libvapours/include/vapours/timespan.hpp +++ b/libraries/libvapours/include/vapours/timespan.hpp @@ -61,7 +61,7 @@ namespace ams { private: TimeSpanType ts; public: - constexpr ALWAYS_INLINE TimeSpan(ZeroTag z = nullptr) : ts(TimeSpanType::FromNanoSeconds(0)) { /* ... */ } + constexpr ALWAYS_INLINE TimeSpan(ZeroTag z = nullptr) : ts(TimeSpanType::FromNanoSeconds(0)) { AMS_UNUSED(z); /* ... */ } constexpr ALWAYS_INLINE TimeSpan(const TimeSpanType &t) : ts(t) { /* ... */ } template @@ -95,6 +95,10 @@ namespace ams { constexpr ALWAYS_INLINE friend TimeSpan operator+(const TimeSpan &lhs, const TimeSpan &rhs) { TimeSpan r(lhs); return r += rhs; } constexpr ALWAYS_INLINE friend TimeSpan operator-(const TimeSpan &lhs, const TimeSpan &rhs) { TimeSpan r(lhs); return r -= rhs; } + + constexpr ALWAYS_INLINE operator TimeSpanType() const { + return this->ts; + } }; } \ No newline at end of file diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 0d60a3ea7..d55f5db64 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -34,8 +34,10 @@ #include #include #include +#include #include #include #include #include #include +#include diff --git a/libraries/libvapours/include/vapours/util/util_alignment.hpp b/libraries/libvapours/include/vapours/util/util_alignment.hpp index 849e141c0..3fa951504 100644 --- a/libraries/libvapours/include/vapours/util/util_alignment.hpp +++ b/libraries/libvapours/include/vapours/util/util_alignment.hpp @@ -73,4 +73,9 @@ namespace ams::util { return IsAligned(reinterpret_cast(value), alignment); } -} \ No newline at end of file + template requires std::integral && std::integral + constexpr ALWAYS_INLINE T DivideUp(T x, U y) { + return (x + (y - 1)) / y; + } + +} diff --git a/libraries/libvapours/include/vapours/util/util_format_string.hpp b/libraries/libvapours/include/vapours/util/util_format_string.hpp new file mode 100644 index 000000000..0dd2faf45 --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_format_string.hpp @@ -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 . + */ + +#pragma once +#include +#include + +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); + +} \ No newline at end of file diff --git a/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp b/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp index 511b289a7..486720f91 100644 --- a/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp +++ b/libraries/libvapours/include/vapours/util/util_intrusive_list.hpp @@ -267,12 +267,14 @@ namespace ams::util { } void splice(const_iterator pos, IntrusiveListImpl &o, const_iterator first) { + AMS_UNUSED(o); const_iterator last(first); std::advance(last, 1); splice_impl(pos, first, last); } void splice(const_iterator pos, IntrusiveListImpl &o, const_iterator first, const_iterator last) { + AMS_UNUSED(o); splice_impl(pos, first, last); } diff --git a/libraries/libvapours/include/vapours/util/util_intrusive_red_black_tree.hpp b/libraries/libvapours/include/vapours/util/util_intrusive_red_black_tree.hpp index d7910f5b8..84c05216d 100644 --- a/libraries/libvapours/include/vapours/util/util_intrusive_red_black_tree.hpp +++ b/libraries/libvapours/include/vapours/util/util_intrusive_red_black_tree.hpp @@ -22,11 +22,19 @@ namespace ams::util { + namespace impl { + + class IntrusiveRedBlackTreeImpl; + + } + struct IntrusiveRedBlackTreeNode { NON_COPYABLE(IntrusiveRedBlackTreeNode); private: RB_ENTRY(IntrusiveRedBlackTreeNode) entry; + friend class impl::IntrusiveRedBlackTreeImpl; + template friend class IntrusiveRedBlackTree; public: @@ -34,42 +42,247 @@ namespace ams::util { }; static_assert(std::is_literal_type::value); + template + class IntrusiveRedBlackTree; + + namespace impl { + + class IntrusiveRedBlackTreeImpl { + NON_COPYABLE(IntrusiveRedBlackTreeImpl); + private: + template + friend class ::ams::util::IntrusiveRedBlackTree; + private: + RB_HEAD(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode); + using RootType = IntrusiveRedBlackTreeRoot; + private: + IntrusiveRedBlackTreeRoot root; + public: + template + class Iterator; + + using value_type = IntrusiveRedBlackTreeNode; + using size_type = size_t; + using difference_type = ptrdiff_t; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using iterator = Iterator; + using const_iterator = Iterator; + + template + class Iterator { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = typename IntrusiveRedBlackTreeImpl::value_type; + using difference_type = typename IntrusiveRedBlackTreeImpl::difference_type; + using pointer = typename std::conditional::type; + using reference = typename std::conditional::type; + private: + pointer node; + public: + explicit Iterator(pointer n) : node(n) { /* ... */ } + + bool operator==(const Iterator &rhs) const { + return this->node == rhs.node; + } + + bool operator!=(const Iterator &rhs) const { + return !(*this == rhs); + } + + pointer operator->() const { + return this->node; + } + + reference operator*() const { + return *this->node; + } + + Iterator &operator++() { + this->node = GetNext(this->node); + return *this; + } + + Iterator &operator--() { + this->node = GetPrev(this->node); + return *this; + } + + Iterator operator++(int) { + const Iterator it{*this}; + ++(*this); + return it; + } + + Iterator operator--(int) { + const Iterator it{*this}; + --(*this); + return it; + } + + operator Iterator() const { + return Iterator(this->node); + } + }; + protected: + /* Generate static implementations for non-comparison operations for IntrusiveRedBlackTreeRoot. */ + RB_GENERATE_WITHOUT_COMPARE_STATIC(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode, entry); + private: + /* Define accessors using RB_* functions. */ + constexpr ALWAYS_INLINE void InitializeImpl() { + RB_INIT(&this->root); + } + + bool EmptyImpl() const { + return RB_EMPTY(&this->root); + } + + IntrusiveRedBlackTreeNode *GetMinImpl() const { + return RB_MIN(IntrusiveRedBlackTreeRoot, const_cast(&this->root)); + } + + IntrusiveRedBlackTreeNode *GetMaxImpl() const { + return RB_MAX(IntrusiveRedBlackTreeRoot, const_cast(&this->root)); + } + + IntrusiveRedBlackTreeNode *RemoveImpl(IntrusiveRedBlackTreeNode *node) { + return RB_REMOVE(IntrusiveRedBlackTreeRoot, &this->root, node); + } + public: + static IntrusiveRedBlackTreeNode *GetNext(IntrusiveRedBlackTreeNode *node) { + return RB_NEXT(IntrusiveRedBlackTreeRoot, nullptr, node); + } + + static IntrusiveRedBlackTreeNode *GetPrev(IntrusiveRedBlackTreeNode *node) { + return RB_PREV(IntrusiveRedBlackTreeRoot, nullptr, node); + } + + static ALWAYS_INLINE IntrusiveRedBlackTreeNode const *GetNext(IntrusiveRedBlackTreeNode const *node) { + return static_cast(GetNext(const_cast(node))); + } + + static ALWAYS_INLINE IntrusiveRedBlackTreeNode const *GetPrev(IntrusiveRedBlackTreeNode const *node) { + return static_cast(GetPrev(const_cast(node))); + } + public: + constexpr IntrusiveRedBlackTreeImpl() : root() { + this->InitializeImpl(); + } + + /* Iterator accessors. */ + iterator begin() { + return iterator(this->GetMinImpl()); + } + + const_iterator begin() const { + return const_iterator(this->GetMinImpl()); + } + + iterator end() { + return iterator(static_cast(nullptr)); + } + + const_iterator end() const { + return const_iterator(static_cast(nullptr)); + } + + const_iterator cbegin() const { + return this->begin(); + } + + const_iterator cend() const { + return this->end(); + } + + iterator iterator_to(reference ref) { + return iterator(&ref); + } + + const_iterator iterator_to(const_reference ref) const { + return const_iterator(&ref); + } + + /* Content management. */ + bool empty() const { + return this->EmptyImpl(); + } + + reference back() { + return *this->GetMaxImpl(); + } + + const_reference back() const { + return *this->GetMaxImpl(); + } + + reference front() { + return *this->GetMinImpl(); + } + + const_reference front() const { + return *this->GetMinImpl(); + } + + iterator erase(iterator it) { + auto cur = std::addressof(*it); + auto next = GetNext(cur); + this->RemoveImpl(cur); + return iterator(next); + } + }; + + } + template class IntrusiveRedBlackTree { NON_COPYABLE(IntrusiveRedBlackTree); - private: - RB_HEAD(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode); - - IntrusiveRedBlackTreeRoot root; public: + using ImplType = impl::IntrusiveRedBlackTreeImpl; + private: + ImplType impl; + public: + struct IntrusiveRedBlackTreeRootWithCompare : ImplType::IntrusiveRedBlackTreeRoot{}; + template class Iterator; - using value_type = T; - using size_type = size_t; - using difference_type = ptrdiff_t; - using pointer = T *; - using const_pointer = const T *; - using reference = T &; - using const_reference = const T &; - using iterator = Iterator; - using const_iterator = Iterator; + using value_type = T; + using size_type = size_t; + using difference_type = ptrdiff_t; + using pointer = T *; + using const_pointer = const T *; + using reference = T &; + using const_reference = const T &; + using iterator = Iterator; + using const_iterator = Iterator; template class Iterator { public: + friend class IntrusiveRedBlackTree; + + using ImplIterator = typename std::conditional::type; + using iterator_category = std::bidirectional_iterator_tag; using value_type = typename IntrusiveRedBlackTree::value_type; using difference_type = typename IntrusiveRedBlackTree::difference_type; - using pointer = typename std::conditional::type; + using pointer = typename std::conditional::type; using reference = typename std::conditional::type; private: - pointer node; - public: - explicit Iterator(pointer n) : node(n) { /* ... */ } + ImplIterator iterator; + private: + explicit Iterator(ImplIterator it) : iterator(it) { /* ... */ } + explicit Iterator(ImplIterator::pointer p) : iterator(p) { /* ... */ } + + ImplIterator GetImplIterator() const { + return this->iterator; + } + public: bool operator==(const Iterator &rhs) const { - return this->node == rhs.node; + return this->iterator == rhs.iterator; } bool operator!=(const Iterator &rhs) const { @@ -77,116 +290,77 @@ namespace ams::util { } pointer operator->() const { - return this->node; + return Traits::GetParent(std::addressof(*this->iterator)); } reference operator*() const { - return *this->node; + return *Traits::GetParent(std::addressof(*this->iterator)); } Iterator &operator++() { - this->node = Traits::GetParent(GetNext(Traits::GetNode(this->node))); + ++this->iterator; return *this; } Iterator &operator--() { - this->node = Traits::GetParent(GetPrev(Traits::GetNode(this->node))); + --this->iterator; return *this; } Iterator operator++(int) { const Iterator it{*this}; - ++(*this); + ++this->iterator; return it; } Iterator operator--(int) { const Iterator it{*this}; - --(*this); + --this->iterator; return it; } operator Iterator() const { - return Iterator(this->node); + return Iterator(this->iterator); } }; + private: + /* Generate static implementations for comparison operations for IntrusiveRedBlackTreeRoot. */ + RB_GENERATE_WITH_COMPARE_STATIC(IntrusiveRedBlackTreeRootWithCompare, IntrusiveRedBlackTreeNode, entry, CompareImpl); private: static int CompareImpl(const IntrusiveRedBlackTreeNode *lhs, const IntrusiveRedBlackTreeNode *rhs) { return Comparator::Compare(*Traits::GetParent(lhs), *Traits::GetParent(rhs)); } - /* Generate static implementations for IntrusiveRedBlackTreeRoot. */ - RB_GENERATE_STATIC(IntrusiveRedBlackTreeRoot, IntrusiveRedBlackTreeNode, entry, CompareImpl); - - static constexpr inline IntrusiveRedBlackTreeNode *GetNext(IntrusiveRedBlackTreeNode *node) { - return RB_NEXT(IntrusiveRedBlackTreeRoot, nullptr, node); - } - - static constexpr inline IntrusiveRedBlackTreeNode const *GetNext(IntrusiveRedBlackTreeNode const *node) { - return const_cast(GetNext(const_cast(node))); - } - - static constexpr inline IntrusiveRedBlackTreeNode *GetPrev(IntrusiveRedBlackTreeNode *node) { - return RB_PREV(IntrusiveRedBlackTreeRoot, nullptr, node); - } - - static constexpr inline IntrusiveRedBlackTreeNode const *GetPrev(IntrusiveRedBlackTreeNode const *node) { - return const_cast(GetPrev(const_cast(node))); - } - /* Define accessors using RB_* functions. */ - constexpr ALWAYS_INLINE void InitializeImpl() { - RB_INIT(&this->root); - } - - bool EmptyImpl() const { - return RB_EMPTY(&this->root); - } - - IntrusiveRedBlackTreeNode *GetMinImpl() const { - return RB_MIN(IntrusiveRedBlackTreeRoot, const_cast(&this->root)); - } - - IntrusiveRedBlackTreeNode *GetMaxImpl() const { - return RB_MAX(IntrusiveRedBlackTreeRoot, const_cast(&this->root)); - } - IntrusiveRedBlackTreeNode *InsertImpl(IntrusiveRedBlackTreeNode *node) { - return RB_INSERT(IntrusiveRedBlackTreeRoot, &this->root, node); - } - - IntrusiveRedBlackTreeNode *RemoveImpl(IntrusiveRedBlackTreeNode *node) { - return RB_REMOVE(IntrusiveRedBlackTreeRoot, &this->root, node); + return RB_INSERT(IntrusiveRedBlackTreeRootWithCompare, static_cast(&this->impl.root), node); } IntrusiveRedBlackTreeNode *FindImpl(IntrusiveRedBlackTreeNode const *node) const { - return RB_FIND(IntrusiveRedBlackTreeRoot, const_cast(&this->root), const_cast(node)); + return RB_FIND(IntrusiveRedBlackTreeRootWithCompare, const_cast(static_cast(&this->impl.root)), const_cast(node)); } IntrusiveRedBlackTreeNode *NFindImpl(IntrusiveRedBlackTreeNode const *node) const { - return RB_NFIND(IntrusiveRedBlackTreeRoot, const_cast(&this->root), const_cast(node)); + return RB_NFIND(IntrusiveRedBlackTreeRootWithCompare, const_cast(static_cast(&this->impl.root)), const_cast(node)); } - public: - constexpr ALWAYS_INLINE IntrusiveRedBlackTree() : root() { - this->InitializeImpl(); - } + constexpr ALWAYS_INLINE IntrusiveRedBlackTree() : impl() { /* ... */ } /* Iterator accessors. */ iterator begin() { - return iterator(Traits::GetParent(this->GetMinImpl())); + return iterator(this->impl.begin()); } const_iterator begin() const { - return const_iterator(Traits::GetParent(this->GetMinImpl())); + return const_iterator(this->impl.begin()); } iterator end() { - return iterator(Traits::GetParent(static_cast(nullptr))); + return iterator(this->impl.end()); } const_iterator end() const { - return const_iterator(Traits::GetParent(static_cast(nullptr))); + return const_iterator(this->impl.end()); } const_iterator cbegin() const { @@ -198,52 +372,50 @@ namespace ams::util { } iterator iterator_to(reference ref) { - return iterator(&ref); + return iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); } const_iterator iterator_to(const_reference ref) const { - return const_iterator(&ref); + return const_iterator(this->impl.iterator_to(*Traits::GetNode(std::addressof(ref)))); } /* Content management. */ bool empty() const { - return this->EmptyImpl(); + return this->impl.empty(); } reference back() { - return *Traits::GetParent(this->GetMaxImpl()); + return *Traits::GetParent(std::addressof(this->impl.back())); } const_reference back() const { - return *Traits::GetParent(this->GetMaxImpl()); + return *Traits::GetParent(std::addressof(this->impl.back())); } reference front() { - return *Traits::GetParent(this->GetMinImpl()); + return *Traits::GetParent(std::addressof(this->impl.front())); } const_reference front() const { - return *Traits::GetParent(this->GetMinImpl()); - } - - iterator insert(reference ref) { - this->InsertImpl(Traits::GetNode(&ref)); - return iterator(&ref); + return *Traits::GetParent(std::addressof(this->impl.front())); } iterator erase(iterator it) { - auto cur = Traits::GetNode(&*it); - auto next = Traits::GetParent(GetNext(cur)); - this->RemoveImpl(cur); - return iterator(next); + return iterator(this->impl.erase(it.GetImplIterator())); + } + + iterator insert(reference ref) { + ImplType::pointer node = Traits::GetNode(std::addressof(ref)); + this->InsertImpl(node); + return iterator(node); } iterator find(const_reference ref) const { - return iterator(Traits::GetParent(this->FindImpl(Traits::GetNode(&ref)))); + return iterator(this->FindImpl(Traits::GetNode(std::addressof(ref)))); } iterator nfind(const_reference ref) const { - return iterator(Traits::GetParent(this->NFindImpl(Traits::GetNode(&ref)))); + return iterator(this->NFindImpl(Traits::GetNode(std::addressof(ref)))); } }; @@ -254,11 +426,14 @@ namespace ams::util { class IntrusiveRedBlackTreeMemberTraits { public: template - using TreeType = IntrusiveRedBlackTree; + using TreeType = IntrusiveRedBlackTree; + using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; private: template friend class IntrusiveRedBlackTree; + friend class impl::IntrusiveRedBlackTreeImpl; + static constexpr IntrusiveRedBlackTreeNode *GetNode(Derived *parent) { return std::addressof(parent->*Member); } @@ -286,7 +461,8 @@ namespace ams::util { class IntrusiveRedBlackTreeMemberTraitsDeferredAssert { public: template - using TreeType = IntrusiveRedBlackTree; + using TreeType = IntrusiveRedBlackTree; + using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; static constexpr bool IsValid() { TYPED_STORAGE(Derived) DerivedStorage = {}; @@ -296,6 +472,8 @@ namespace ams::util { template friend class IntrusiveRedBlackTree; + friend class impl::IntrusiveRedBlackTreeImpl; + static constexpr IntrusiveRedBlackTreeNode *GetNode(Derived *parent) { return std::addressof(parent->*Member); } @@ -314,17 +492,27 @@ namespace ams::util { }; template - class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode{}; + class IntrusiveRedBlackTreeBaseNode : public IntrusiveRedBlackTreeNode { + public: + constexpr ALWAYS_INLINE Derived *GetPrev() { return static_cast< Derived *>(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); } + constexpr ALWAYS_INLINE const Derived *GetPrev() const { return static_cast(impl::IntrusiveRedBlackTreeImpl::GetPrev(this)); } + + constexpr ALWAYS_INLINE Derived *GetNext() { return static_cast< Derived *>(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); } + constexpr ALWAYS_INLINE const Derived *GetNext() const { return static_cast(impl::IntrusiveRedBlackTreeImpl::GetNext(this)); } + }; template class IntrusiveRedBlackTreeBaseTraits { public: template - using TreeType = IntrusiveRedBlackTree; + using TreeType = IntrusiveRedBlackTree; + using TreeTypeImpl = impl::IntrusiveRedBlackTreeImpl; private: template friend class IntrusiveRedBlackTree; + friend class impl::IntrusiveRedBlackTreeImpl; + static constexpr IntrusiveRedBlackTreeNode *GetNode(Derived *parent) { return static_cast(parent); } @@ -342,4 +530,4 @@ namespace ams::util { } }; -} \ No newline at end of file +} diff --git a/libraries/libvapours/include/vapours/util/util_timer.hpp b/libraries/libvapours/include/vapours/util/util_timer.hpp new file mode 100644 index 000000000..2d2236163 --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_timer.hpp @@ -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 . + */ + +#pragma once +#include +#include + +namespace ams::util { + + u32 GetMicroSeconds(); + void WaitMicroSeconds(int us); + +} diff --git a/libraries/libvapours/source/crypto/impl/crypto_xts_mode_impl.cpp b/libraries/libvapours/source/crypto/impl/crypto_xts_mode_impl.cpp index 86d022d71..90caa87ee 100644 --- a/libraries/libvapours/source/crypto/impl/crypto_xts_mode_impl.cpp +++ b/libraries/libvapours/source/crypto/impl/crypto_xts_mode_impl.cpp @@ -55,6 +55,7 @@ namespace ams::crypto::impl { size_t XtsModeImpl::FinalizeEncryption(void *dst, size_t dst_size) { AMS_ASSERT(this->state == State_Processing); + AMS_UNUSED(dst_size); u8 *dst_u8 = static_cast(dst); size_t processed = 0; @@ -80,6 +81,7 @@ namespace ams::crypto::impl { size_t XtsModeImpl::FinalizeDecryption(void *dst, size_t dst_size) { AMS_ASSERT(this->state == State_Processing); + AMS_UNUSED(dst_size); u8 *dst_u8 = static_cast(dst); size_t processed = 0; @@ -131,6 +133,8 @@ namespace ams::crypto::impl { } size_t XtsModeImpl::ProcessRemainingData(u8 *dst, const u8 *src, size_t size) { + AMS_UNUSED(dst); + std::memcpy(this->buffer, src, size); this->num_buffered = size; diff --git a/libraries/libvapours/source/dd/dd_cache.cpp b/libraries/libvapours/source/dd/dd_cache.cpp new file mode 100644 index 000000000..1cab96001 --- /dev/null +++ b/libraries/libvapours/source/dd/dd_cache.cpp @@ -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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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); + } + +} diff --git a/libraries/libvapours/source/dd/dd_io_mapping.cpp b/libraries/libvapours/source/dd/dd_io_mapping.cpp new file mode 100644 index 000000000..1bbdeb8c0 --- /dev/null +++ b/libraries/libvapours/source/dd/dd_io_mapping.cpp @@ -0,0 +1,162 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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(phys_addr); + #elif defined(ATMOSPHERE_ARCH_ARM) + /* TODO: BPMP translation? */ + AMS_UNUSED(size); + return static_cast(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(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(virt_addr) + offset; + + #else + #error "Unknown execution context for ams::dd::QueryIoMapping!" + #endif + } + + namespace { + + #if defined(ATMOSPHERE_IS_STRATOSPHERE) + + #if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + constexpr dd::PhysicalAddress PmcPhysStart = 0x7000E400; + constexpr dd::PhysicalAddress PmcPhysLast = 0x7000EFFF; + + constexpr bool IsValidPmcPhysicalAddress(dd::PhysicalAddress phys_addr) { + return util::IsAligned(phys_addr, alignof(u32)) && PmcPhysStart <= phys_addr && phys_addr <= PmcPhysLast; + } + + u32 ReadWritePmcRegisterImpl(dd::PhysicalAddress phys_addr, u32 value, u32 mask) { + u32 out_value; + R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::AtmosphereReadWriteRegister(phys_addr, mask, value, &out_value))); + return out_value; + } + + bool TryReadModifyWritePmcRegister(u32 *out, dd::PhysicalAddress phys_addr, u32 value, u32 mask) { + if (IsValidPmcPhysicalAddress(phys_addr)) { + *out = ReadWritePmcRegisterImpl(phys_addr, value, mask); + return true; + } else { + return false; + } + } + + #else + + bool TryReadModifyWritePmcRegister(u32 *out, dd::PhysicalAddress phys_addr, u32 value, u32 mask) { + AMS_UNUSED(out, phys_addr, value, mask); + return false; + } + + #endif + + #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; + if (!TryReadModifyWritePmcRegister(std::addressof(val), phys_addr, 0, 0)) { + 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; + if (!TryReadModifyWritePmcRegister(std::addressof(out_val), phys_addr, value, 0xFFFFFFFF)) { + 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 + } + + u32 ReadModifyWriteIoRegister(PhysicalAddress phys_addr, u32 value, u32 mask) { + #if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) + AMS_UNUSED(phys_addr, value, mask); + AMS_ABORT("ReadModifyWriteIoRegister TODO under non-stratosphere"); + #elif defined(ATMOSPHERE_IS_STRATOSPHERE) + + u32 out_val; + if (!TryReadModifyWritePmcRegister(std::addressof(out_val), phys_addr, value, mask)) { + R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(out_val), phys_addr, mask, value)); + } + return out_val; + + #else + #error "Unknown execution context for ams::dd::ReadModifyWriteIoRegister!" + #endif + } + +} diff --git a/libraries/libvapours/source/dd/impl/dd_cache_impl.os.horizon.hpp b/libraries/libvapours/source/dd/impl/dd_cache_impl.os.horizon.hpp new file mode 100644 index 000000000..53ccf6473 --- /dev/null +++ b/libraries/libvapours/source/dd/impl/dd_cache_impl.os.horizon.hpp @@ -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 . + */ +#pragma once +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#else +#include +#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(addr) & ~(cache_line_size - 1); + const uintptr_t end_addr = reinterpret_cast(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(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(addr) & ~(cache_line_size - 1); + const uintptr_t end_addr = reinterpret_cast(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(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); + } + +} diff --git a/libraries/libvapours/source/dd/impl/dd_select_cache_impl.hpp b/libraries/libvapours/source/dd/impl/dd_select_cache_impl.hpp new file mode 100644 index 000000000..18e021496 --- /dev/null +++ b/libraries/libvapours/source/dd/impl/dd_select_cache_impl.hpp @@ -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 . + */ +#pragma once + +#if defined(ATMOSPHERE_OS_HORIZON) + #include "dd_cache_impl.os.horizon.hpp" +#else + #error "Unknown OS for ams::dd::CacheImpl" +#endif diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp new file mode 100644 index 000000000..c9234e994 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.cpp @@ -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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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((this->csd[2] >> 7) & 0x7); + *out_read_bl_len = static_cast((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((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(dst), alignof(u32))); + AMS_ABORT_UNLESS(dst_size >= DeviceCidSize); + this->host_controller->GetLastResponse(static_cast(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(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(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(dst), alignof(u32))); + AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize); + this->host_controller->GetLastResponse(static_cast(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(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(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 + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp new file mode 100644 index 000000000..8cb0490e8 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_base_device_accessor.hpp @@ -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 . + */ +#pragma once +#include +#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(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(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(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(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; + }; + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp new file mode 100644 index 000000000..17903800b --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.board.nintendo_nx.cpp @@ -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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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(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(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); + } + } + + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.hpp new file mode 100644 index 000000000..29ac27601 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.hpp @@ -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 . + */ +#pragma once +#include + +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); + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp new file mode 100644 index 000000000..8d8c39deb --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.cpp @@ -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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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 + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp new file mode 100644 index 000000000..85144bff3 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.pcv.board.nintendo_nx.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 . + */ +#pragma once +#include +#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); + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp new file mode 100644 index 000000000..3a494281e --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.cpp @@ -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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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(n)); break; + case Module_Sdmmc2: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2, clk_m | static_cast(n)); break; + case Module_Sdmmc3: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3, clk_m | static_cast(n)); break; + case Module_Sdmmc4: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4, clk_m | static_cast(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); + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp new file mode 100644 index 000000000..4043037e4 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_clock_reset_controller.reg.board.nintendo_nx.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 . + */ +#pragma once +#include +#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); + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp new file mode 100644 index 000000000..3529b85a0 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.cpp @@ -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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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 diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.hpp new file mode 100644 index 000000000..a61f05095 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_device_detector.hpp @@ -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 . + */ +#pragma once +#include +#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) +#include + +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(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 \ No newline at end of file diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.cpp new file mode 100644 index 000000000..a1ebe02f6 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.cpp @@ -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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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(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(); + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.hpp new file mode 100644 index 000000000..c6c24ec1c --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_gc_asic_device_accessor.hpp @@ -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 . + */ +#pragma once +#include +#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 + } + }; + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp new file mode 100644 index 000000000..94e85bd59 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_device_accessor.hpp @@ -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 . + */ +#pragma once +#include +#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; + }; + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp new file mode 100644 index 000000000..3ac285761 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_i_host_controller.hpp @@ -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 . + */ +#pragma once +#include + +#if defined(AMS_SDMMC_USE_OS_EVENTS) +#include +#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; + }; + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp new file mode 100644 index 000000000..bf36180e7 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp @@ -0,0 +1,998 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_timer.hpp" +#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" +#include "sdmmc_io_impl.board.nintendo_nx.hpp" + +namespace ams::sdmmc::impl { + + /* Lovingly taken from libexosphere. */ + namespace i2c_impl { + + enum Port { + Port_1 = 0, + /* TODO: Support other ports? */ + Port_5 = 4, + + Port_Count = 5, + }; + + constexpr inline const dd::PhysicalAddress I2c5RegistersAddress = UINT64_C(0x7000D000); + constexpr inline const size_t I2c5RegistersSize = 4_KB; + + namespace { + + constexpr inline size_t MaxTransferSize = sizeof(u32); + + constinit std::array g_register_addresses = [] { + std::array arr = {}; + return arr; + }(); + + void LoadConfig(uintptr_t address) { + /* Configure for TIMEOUT and MSTR config load. */ + /* NOTE: Nintendo writes value 1 to reserved bit 5 here. This bit is documented as having no meaning. */ + /* We will reproduce the write just in case it is undocumented. */ + reg::Write(address + I2C_CONFIG_LOAD, I2C_REG_BITS_VALUE(CONFIG_LOAD_RESERVED_BIT_5, 1), + I2C_REG_BITS_ENUM (CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, ENABLE), + I2C_REG_BITS_ENUM (CONFIG_LOAD_SLV_CONFIG_LOAD, DISABLE), + I2C_REG_BITS_ENUM (CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE)); + + /* Wait up to 20 microseconds for the master config to be loaded. */ + for (int i = 0; i < 20; ++i) { + if (reg::HasValue(address + I2C_CONFIG_LOAD, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, DISABLE))) { + return; + } + util::WaitMicroSeconds(1); + } + } + + void ClearBus(uintptr_t address) { + /* Configure the bus clear register. */ + reg::Write(address + I2C_BUS_CLEAR_CONFIG, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_STOP_COND, NO_STOP), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE), + I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE)); + + /* Load the config. */ + LoadConfig(address); + + /* Wait up to 250us (in 25 us increments) until the bus clear is done. */ + for (int i = 0; i < 10; ++i) { + if (reg::HasValue(address + I2C_INTERRUPT_STATUS_REGISTER, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, SET))) { + break; + } + + util::WaitMicroSeconds(25); + } + + /* Read the bus clear status. */ + reg::Read(address + I2C_BUS_CLEAR_STATUS); + } + + void InitializePort(uintptr_t address) { + /* Calculate the divisor. */ + constexpr int Divisor = util::DivideUp(19200, 8 * 400); + + /* Set the divisor. */ + reg::Write(address + I2C_CLK_DIVISOR_REGISTER, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, Divisor - 1), + I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, 1)); + + /* Clear the bus. */ + ClearBus(address); + + /* Clear the status. */ + reg::Write(address + I2C_INTERRUPT_STATUS_REGISTER, reg::Read(address + I2C_INTERRUPT_STATUS_REGISTER)); + } + + bool Write(uintptr_t base_address, Port port, int address, const void *src, size_t src_size, bool unused) { + AMS_UNUSED(port, unused); + + /* Ensure we don't write too much. */ + u32 data = 0; + if (src_size > MaxTransferSize) { + return false; + } + + /* Copy the data to a transfer word. */ + std::memcpy(std::addressof(data), src, src_size); + + + /* Configure the to write the 7-bit address. */ + reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), + I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, WRITE)); + + /* Configure to write the data. */ + reg::Write(base_address + I2C_I2C_CMD_DATA1, data); + + /* Configure to write the correct amount of data. */ + reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), + I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), + I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, WRITE), + I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, src_size - 1)); + + /* Load the configuration. */ + LoadConfig(base_address); + + /* Start the command. */ + reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); + + /* Wait for the command to be done. */ + while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } + + /* Check if the transfer was successful. */ + return reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL)); + } + + bool Read(uintptr_t base_address, Port port, void *dst, size_t dst_size, int address, bool unused) { + AMS_UNUSED(port, unused); + + /* Ensure we don't read too much. */ + if (dst_size > MaxTransferSize) { + return false; + } + + /* Configure the to read the 7-bit address. */ + reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address), + I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, READ)); + + /* Configure to read the correct amount of data. */ + reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T), + I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE), + I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, READ), + I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, dst_size - 1)); + + /* Load the configuration. */ + LoadConfig(base_address); + + /* Start the command. */ + reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO)); + + /* Wait for the command to be done. */ + while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ } + + /* Check that the transfer was successful. */ + if (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL))) { + return false; + } + + /* Read and copy out the data. */ + u32 data = reg::Read(base_address + I2C_I2C_CMD_DATA1); + std::memcpy(dst, std::addressof(data), dst_size); + return true; + } + + } + + void SetRegisterAddress(Port port, uintptr_t address) { + g_register_addresses[port] = address; + } + + void Initialize(Port port) { + InitializePort(g_register_addresses[port]); + } + + bool Query(void *dst, size_t dst_size, Port port, int address, int r) { + const uintptr_t base_address = g_register_addresses[port]; + + /* Select the register we want to read. */ + bool success = Write(base_address, port, address, std::addressof(r), 1, false); + if (success) { + /* If we successfully selected, read data from the register. */ + success = Read(base_address, port, dst, dst_size, address, true); + } + + return success; + } + + bool Send(Port port, int address, int r, const void *src, size_t src_size) { + const uintptr_t base_address = g_register_addresses[port]; + + /* Create a transfer buffer, make sure we can use it. */ + u8 buffer[MaxTransferSize]; + if (src_size > sizeof(buffer) - 1) { + return false; + } + + /* Copy data into the buffer. */ + buffer[0] = static_cast(r); + std::memcpy(buffer + 1, src, src_size); + + return Write(base_address, port, address, buffer, src_size + 1, false); + } + + } + + namespace max7762x { + + /* The only regulator we care about for SD card power is ldo2. */ + constexpr inline const u8 Max77620PwrI2cAddr = 0x3C; + + constexpr inline const u32 Max77620Ldo2MvStep = 50'000; /* 50 mV (50K uV) steps. */ + constexpr inline const u32 Max77620Ldo2MvMin = 800'000; /* 0.8V min voltage. */ + constexpr inline const u32 Max77620Ldo2MvDefault = 1'800'000; /* 1.8V default voltage. */ + constexpr inline const u32 Max77620Ldo2MvMax = 3'300'000; /* 3.3V max voltage. */ + constexpr inline const u8 Max77620Ldo2VoltAddr = 0x27; + constexpr inline const u8 Max77620Ldo2CfgAddr = 0x28; + constexpr inline const u8 Max77620Ldo2VoltMask = 0x3F; + constexpr inline const u8 Max77620Ldo2EnableMask = 0xC0; + constexpr inline const u8 Max77620Ldo2EnableShift = 0x6; + constexpr inline const u8 Max77620Ldo2StatusMask = 0x00; + + namespace { + + #if defined(AMS_SDMMC_THREAD_SAFE) + constinit os::SdkMutex g_i2c_init_mutex; + + #define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() std::scoped_lock lk(g_i2c_init_mutex) + + #else + + #define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() + + #endif + + constinit bool g_initialized_i2c = false; + + void EnsureI2cInitialized() { + if (AMS_UNLIKELY(!g_initialized_i2c)) { + /* Ensure we have exclusive access to the i2c init status. */ + AMS_SDMMC_LOCK_I2C_INIT_MUTEX(); + + if (AMS_LIKELY(!g_initialized_i2c)) { + i2c_impl::SetRegisterAddress(i2c_impl::Port_5, dd::QueryIoMapping(i2c_impl::I2c5RegistersAddress, i2c_impl::I2c5RegistersSize)); + i2c_impl::Initialize(i2c_impl::Port_5); + g_initialized_i2c = true; + } + } + } + + } + + bool SetVoltageEnabled(bool en) { + /* Ensure that we can use i2c to communicate with the max7762x regulator. */ + EnsureI2cInitialized(); + + /* Read the current value. */ + u8 val; + if (!i2c_impl::Query(std::addressof(val), sizeof(val), i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr)) { + return false; + } + + /* Set or clear the enable mask. */ + val &= ~Max77620Ldo2EnableMask; + if (en) { + val |= ((3 << Max77620Ldo2EnableShift) & Max77620Ldo2EnableMask); + } + + /* Write the updated value. */ + if (!i2c_impl::Send(i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr, std::addressof(val), sizeof(val))) { + return false; + } + + /* Wait 1ms for change to take. */ + WaitMicroSeconds(1); + + /* Voltage is now enabled/disabled. */ + return true; + } + + bool SetVoltageValue(u32 micro_volts) { + /* Ensure that we can use i2c to communicate with the max7762x regulator. */ + EnsureI2cInitialized(); + + /* Check that the value is within range. */ + if (micro_volts < Max77620Ldo2MvMin || Max77620Ldo2MvMax < micro_volts) { + return false; + } + + /* Determine the mult. */ + const u32 mult = util::DivideUp(micro_volts - Max77620Ldo2MvMin, Max77620Ldo2MvStep); + + /* Read the current value. */ + u8 val; + if (!i2c_impl::Query(std::addressof(val), sizeof(val), i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr)) { + return false; + } + + /* Set the new voltage. */ + val &= ~Max77620Ldo2VoltMask; + val |= (mult & Max77620Ldo2VoltMask); + + /* Write the updated value. */ + if (!i2c_impl::Send(i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr, std::addressof(val), sizeof(val))) { + return false; + } + + /* Wait 1ms for change to take. */ + WaitMicroSeconds(1); + + /* Voltage is now set. */ + return true; + } + + } + + Result SetSdCardVoltageEnabled(bool en) { + /* TODO: A way for this to be non-fatal? */ + AMS_ABORT_UNLESS(max7762x::SetVoltageEnabled(en)); + + return ResultSuccess(); + } + + Result SetSdCardVoltageValue(u32 micro_volts) { + /* TODO: A way for this to be non-fatal? */ + AMS_ABORT_UNLESS(max7762x::SetVoltageValue(micro_volts)); + + return ResultSuccess(); + } + + namespace gpio_impl { + + namespace { + + constexpr inline dd::PhysicalAddress GpioRegistersPhysicalAddress = 0x6000d000; + constexpr inline size_t GpioRegistersSize = 4_KB; + + enum GpioPadPort { + GpioPadPort_A = 0, + GpioPadPort_B = 1, + GpioPadPort_C = 2, + GpioPadPort_D = 3, + GpioPadPort_E = 4, + GpioPadPort_F = 5, + GpioPadPort_G = 6, + GpioPadPort_H = 7, + GpioPadPort_I = 8, + GpioPadPort_J = 9, + GpioPadPort_K = 10, + GpioPadPort_L = 11, + GpioPadPort_M = 12, + GpioPadPort_N = 13, + GpioPadPort_O = 14, + GpioPadPort_P = 15, + GpioPadPort_Q = 16, + GpioPadPort_R = 17, + GpioPadPort_S = 18, + GpioPadPort_T = 19, + GpioPadPort_U = 20, + GpioPadPort_V = 21, + GpioPadPort_W = 22, + GpioPadPort_X = 23, + GpioPadPort_Y = 24, + GpioPadPort_Z = 25, + GpioPadPort_AA = 26, + GpioPadPort_BB = 27, + GpioPadPort_CC = 28, + GpioPadPort_DD = 29, + GpioPadPort_EE = 30, + GpioPadPort_FF = 31, + }; + + consteval unsigned int GetInternalGpioPadNumber(GpioPadPort port, unsigned int which) { + AMS_ASSUME(which < 8); + + return (static_cast(port) * 8) + which; + } + + enum InternalGpioPadNumber { + InternalGpioPadNumber_E4 = GetInternalGpioPadNumber(GpioPadPort_E, 4), + InternalGpioPadNumber_M0 = GetInternalGpioPadNumber(GpioPadPort_M, 0), + }; + + constexpr int ConvertInternalGpioPadNumberToController(InternalGpioPadNumber number) { + return (number >> 5); + } + + constexpr int ConvertInternalGpioPadNumberToPort(InternalGpioPadNumber number) { + return (number >> 3); + } + + constexpr int ConvertInternalGpioPadNumberToBitIndex(InternalGpioPadNumber number) { + return (number & 7); + } + + constexpr int ConvertPortNumberToOffset(int port_number) { + return (port_number & 3); + } + + struct PadNameToInternalPadNumberEntry { + GpioPadName pad_name; + InternalGpioPadNumber internal_number; + }; + + constexpr inline const PadNameToInternalPadNumberEntry PadNameToInternalPadNumberTable[] = { + { GpioPadName_PowSdEn, InternalGpioPadNumber_E4 }, + }; + + constexpr InternalGpioPadNumber ConvertPadNameToInternalPadNumber(GpioPadName pad) { + const PadNameToInternalPadNumberEntry *target = nullptr; + for (const auto &entry : PadNameToInternalPadNumberTable) { + if (entry.pad_name == pad) { + target = std::addressof(entry); + break; + } + } + + AMS_ABORT_UNLESS(target != nullptr); + return target->internal_number; + } + + enum GpioRegisterType { + GpioRegisterType_GPIO_CNF = 0, + GpioRegisterType_GPIO_OE = 1, + GpioRegisterType_GPIO_OUT = 2, + GpioRegisterType_GPIO_IN = 3, + GpioRegisterType_GPIO_INT_STA = 4, + GpioRegisterType_GPIO_INT_ENB = 5, + GpioRegisterType_GPIO_INT_LVL = 6, + GpioRegisterType_GPIO_INT_CLR = 7, + GpioRegisterType_GPIO_DB_CTRL = 8, + GpioRegisterType_GPIO_DB_CNT = 9, + }; + + constexpr inline uintptr_t MaskedWriteAddressOffset = 0x80; + constexpr inline int MaskedWriteBitOffset = 8; + + constexpr uintptr_t GetGpioRegisterAddress(uintptr_t gpio_address, GpioRegisterType reg_type, InternalGpioPadNumber pad_number) { + const auto controller = ConvertInternalGpioPadNumberToController(pad_number); + const auto port = ConvertInternalGpioPadNumberToPort(pad_number); + const auto offset = ConvertPortNumberToOffset(port); + + switch (reg_type) { + default: + return gpio_address + (0x100 * controller) + (0x10 * reg_type) + (0x4 * offset); + case GpioRegisterType_GPIO_DB_CTRL: + return gpio_address + (0x100 * controller) + (0x10 * GpioRegisterType_GPIO_IN) + (0x4 * offset); + case GpioRegisterType_GPIO_DB_CNT: + return gpio_address + (0x100 * controller) + MaskedWriteAddressOffset + (0x10 * GpioRegisterType_GPIO_INT_CLR) + (0x4 * offset); + } + } + + void SetMaskedBit(uintptr_t pad_address, int index, int value) { + const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; + + reg::Write(mask_address, (1u << (MaskedWriteBitOffset + index)) | (static_cast(value) << index)); + } + + void SetMaskedBits(uintptr_t pad_address, unsigned int mask, unsigned int value) { + const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset; + + reg::Write(mask_address, (mask << MaskedWriteBitOffset) | (value)); + } + + } + + void OpenSession(GpioPadName pad) { + /* Convert the pad to an internal number. */ + const auto pad_number = ConvertPadNameToInternalPadNumber(pad); + + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); + + /* Configure the pad as GPIO by setting the appropriate bit in CNF. */ + const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_CNF, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, 1); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + } + + void CloseSession(GpioPadName pad) { + /* Nothing needs to be done here, as the only thing official code does is unbind the interrupt event. */ + AMS_UNUSED(pad); + } + + void SetDirection(GpioPadName pad, Direction direction) { + /* Convert the pad to an internal number. */ + const auto pad_number = ConvertPadNameToInternalPadNumber(pad); + + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); + + /* Configure the pad direction modifying the appropriate bit in OE. */ + const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_OE, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, direction); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + } + + void SetValue(GpioPadName pad, GpioValue value) { + /* Convert the pad to an internal number. */ + const auto pad_number = ConvertPadNameToInternalPadNumber(pad); + + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize); + + /* Configure the pad value modifying the appropriate bit in OUT. */ + const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_OUT, pad_number); + const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number); + SetMaskedBit(pad_address, pad_index, value); + + /* Read the pad address to make sure our configuration takes. */ + reg::Read(pad_address); + } + + } + + namespace pinmux_impl { + + namespace { + + constexpr auto Sdmmc1ClkCmdDat03PadNumber = gpio_impl::InternalGpioPadNumber_M0; + + constexpr unsigned int Sdmmc1ClkCmdDat03PadMask = 0x3F; + + constexpr unsigned int Sdmmc1ClkCmdDat03PadCnfGpio = 0x3F; + constexpr unsigned int Sdmmc1ClkCmdDat03PadCnfSfio = 0x00; + + constexpr unsigned int Sdmmc1ClkCmdDat03PadOutHigh = 0x3F; + constexpr unsigned int Sdmmc1ClkCmdDat03PadOutLow = 0x00; + + constexpr unsigned int Sdmmc1ClkCmdDat03PadOeOutput = 0x3F; + constexpr unsigned int Sdmmc1ClkCmdDat03PadOeInput = 0x00; + + struct PinmuxDefinition { + u32 reg_offset; + u32 mask_val; + u32 pm_val; + }; + + /* NOTE: We only use the SDMMC1 pins, which are conveniently the first few... */ + constexpr const PinmuxDefinition PinmuxDefinitionMap[] = { + {0x00003000, 0x72FF, 0x01}, /* Sdmmc1Clk */ + {0x00003004, 0x72FF, 0x02}, /* Sdmmc1Cmd */ + {0x00003008, 0x72FF, 0x02}, /* Sdmmc1Dat3 */ + {0x0000300C, 0x72FF, 0x02}, /* Sdmmc1Dat2 */ + {0x00003010, 0x72FF, 0x02}, /* Sdmmc1Dat1 */ + {0x00003014, 0x72FF, 0x01}, /* Sdmmc1Dat0 */ + }; + + enum PinmuxPadIndex { + PinmuxPadIndex_Sdmmc1Clk = 0, + PinmuxPadIndex_Sdmmc1Cmd = 1, + PinmuxPadIndex_Sdmmc1Dat3 = 2, + PinmuxPadIndex_Sdmmc1Dat2 = 3, + PinmuxPadIndex_Sdmmc1Dat1 = 4, + PinmuxPadIndex_Sdmmc1Dat0 = 5, + + PinmuxPadIndex_Count, + }; + + static_assert(util::size(PinmuxDefinitionMap) == PinmuxPadIndex_Count); + + consteval const PinmuxDefinition GetDefinition(PinmuxPadIndex pad_index) { + AMS_ABORT_UNLESS(pad_index < PinmuxPadIndex_Count); + + return PinmuxDefinitionMap[pad_index]; + } + + template + ALWAYS_INLINE u32 UpdatePinmuxPad(uintptr_t pinmux_base_vaddr) { + constexpr const PinmuxDefinition Definition = GetDefinition(PadIndex); + + /* Fetch this PINMUX's register offset */ + constexpr u32 PinmuxRegOffset = Definition.reg_offset; + + /* Fetch this PINMUX's mask value */ + constexpr u32 PinmuxMaskVal = Definition.mask_val; + + /* Get current register ptr. */ + const uintptr_t pinmux_reg = pinmux_base_vaddr + PinmuxRegOffset; + + /* Read from the PINMUX register */ + u32 pinmux_val = reg::Read(pinmux_reg); + + /* This PINMUX register is locked */ + AMS_ABORT_UNLESS((pinmux_val & 0x80) == 0); + + constexpr u32 PmVal = (PinmuxConfigVal & 0x07); + + /* Adjust PM */ + if constexpr (PinmuxConfigMaskVal & 0x07) { + /* Apply additional changes first */ + if constexpr (PmVal == 0x05) { + /* This pin supports PUPD change */ + if constexpr (PinmuxMaskVal & 0x0C) { + /* Change PUPD */ + if ((pinmux_val & 0x0C) != 0x04) { + pinmux_val &= 0xFFFFFFF3; + pinmux_val |= 0x04; + } + } + + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (!(pinmux_val & 0x10)) { + pinmux_val |= 0x10; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (pinmux_val & 0x40) { + pinmux_val &= 0xFFFFFFBF; + } + } + } + + /* Translate PM value if necessary */ + constexpr u32 TranslatedPmVal = (PmVal == 0x04 || PmVal == 0x05 || PmVal >= 0x06) ? Definition.pm_val : PmVal; + + /* This pin supports PM change */ + if constexpr (PinmuxMaskVal & 0x03) { + /* Change PM */ + if ((pinmux_val & 0x03) != (TranslatedPmVal & 0x03)) { + pinmux_val &= 0xFFFFFFFC; + pinmux_val |= (TranslatedPmVal & 0x03); + } + } + } + + constexpr u32 PupdConfigVal = (PinmuxConfigVal & 0x18); + + /* Adjust PUPD */ + if constexpr (PinmuxConfigMaskVal & 0x18) { + if constexpr (PupdConfigVal < 0x11) { + /* This pin supports PUPD change */ + if constexpr (PinmuxMaskVal & 0x0C) { + /* Change PUPD */ + if (((pinmux_val >> 0x02) & 0x03) != (PupdConfigVal >> 0x03)) { + pinmux_val &= 0xFFFFFFF3; + pinmux_val |= (PupdConfigVal >> 0x01); + } + } + } + } + + constexpr u32 EodConfigVal = (PinmuxConfigVal & 0x60); + + /* Adjust EOd field */ + if constexpr (PinmuxConfigMaskVal & 0x60) { + if constexpr (EodConfigVal == 0x20) { + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (!(pinmux_val & 0x10)) { + pinmux_val |= 0x10; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (!(pinmux_val & 0x40)) { + pinmux_val |= 0x40; + } + } + + /* This pin supports EOd change */ + if constexpr (PinmuxMaskVal & 0x800) { + /* Change EOd */ + if (pinmux_val & 0x800) { + pinmux_val &= 0xFFFFF7FF; + } + } + } else if constexpr (EodConfigVal == 0x40) { + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (pinmux_val & 0x10) { + pinmux_val &= 0xFFFFFFEF; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (!(pinmux_val & 0x40)) { + pinmux_val |= 0x40; + } + } + + /* This pin supports EOd change */ + if constexpr (PinmuxMaskVal & 0x800) { + /* Change EOd */ + if (pinmux_val & 0x800) { + pinmux_val &= 0xFFFFF7FF; + } + } + } else if constexpr (EodConfigVal == 0x60) { + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (pinmux_val & 0x10) { + pinmux_val &= 0xFFFFFFEF; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (!(pinmux_val & 0x40)) { + pinmux_val |= 0x40; + } + } + + /* This pin supports EOd change */ + if constexpr (PinmuxMaskVal & 0x800) { + /* Change EOd */ + if (!(pinmux_val & 0x800)) { + pinmux_val |= 0x800; + } + } + } else { + /* This pin supports Tristate change */ + if constexpr (PinmuxMaskVal & 0x10) { + /* Change Tristate */ + if (pinmux_val & 0x10) { + pinmux_val &= 0xFFFFFFEF; + } + } + + /* This pin supports EInput change */ + if constexpr (PinmuxMaskVal & 0x40) { + /* Change EInput */ + if (pinmux_val & 0x40) { + pinmux_val &= 0xFFFFFFBF; + } + } + + /* This pin supports EOd change */ + if constexpr (PinmuxMaskVal & 0x800) { + /* Change EOd */ + if (pinmux_val & 0x800) { + pinmux_val &= 0xFFFFF7FF; + } + } + } + } + + constexpr u32 LockConfigVal = (PinmuxConfigVal & 0x80); + + /* Adjust Lock */ + if constexpr (PinmuxConfigMaskVal & 0x80) { + /* This pin supports Lock change */ + if constexpr (PinmuxMaskVal & 0x80) { + /* Change Lock */ + if ((pinmux_val ^ PinmuxConfigVal) & 0x80) { + pinmux_val &= 0xFFFFFF7F; + pinmux_val |= LockConfigVal; + } + } + } + + constexpr u32 IoResetConfigVal = (((PinmuxConfigVal >> 0x08) & 0x1) << 0x10); + + /* Adjust IoReset */ + if constexpr (PinmuxConfigMaskVal & 0x100) { + /* This pin supports IoReset change */ + if constexpr (PinmuxMaskVal & 0x10000) { + /* Change IoReset */ + if (((pinmux_val >> 0x10) ^ (PinmuxConfigVal >> 0x08)) & 0x01) { + pinmux_val &= 0xFFFEFFFF; + pinmux_val |= IoResetConfigVal; + } + } + } + + constexpr u32 ParkConfigVal = (((PinmuxConfigVal >> 0x0A) & 0x1) << 0x5); + + /* Adjust Park */ + if constexpr (PinmuxConfigMaskVal & 0x400) { + /* This pin supports Park change */ + if constexpr (PinmuxMaskVal & 0x20) { + /* Change Park */ + if (((pinmux_val >> 0x05) ^ (PinmuxConfigVal >> 0x0A)) & 0x01) { + pinmux_val &= 0xFFFFFFDF; + pinmux_val |= ParkConfigVal; + } + } + } + + constexpr u32 ElpdrConfigVal = (((PinmuxConfigVal >> 0x0B) & 0x1) << 0x08); + + /* Adjust ELpdr */ + if constexpr (PinmuxConfigMaskVal & 0x800) { + /* This pin supports ELpdr change */ + if constexpr (PinmuxMaskVal & 0x100) { + /* Change ELpdr */ + if (((pinmux_val >> 0x08) ^ (PinmuxConfigVal >> 0x0B)) & 0x01) { + pinmux_val &= 0xFFFFFEFF; + pinmux_val |= ElpdrConfigVal; + } + } + } + + constexpr u32 EhsmConfigVal = (((PinmuxConfigVal >> 0x0C) & 0x1) << 0x09); + + /* Adjust EHsm */ + if constexpr (PinmuxConfigMaskVal & 0x1000) { + /* This pin supports EHsm change */ + if constexpr (PinmuxMaskVal & 0x200) { + /* Change EHsm */ + if (((pinmux_val >> 0x09) ^ (PinmuxConfigVal >> 0x0C)) & 0x01) { + pinmux_val &= 0xFFFFFDFF; + pinmux_val |= EhsmConfigVal; + } + } + } + + constexpr u32 EIoHvConfigVal = (((PinmuxConfigVal >> 0x09) & 0x1) << 0x0A); + + /* Adjust EIoHv */ + if constexpr (PinmuxConfigMaskVal & 0x200) { + /* This pin supports EIoHv change */ + if constexpr (PinmuxMaskVal & 0x400) { + /* Change EIoHv */ + if (((pinmux_val >> 0x0A) ^ (PinmuxConfigVal >> 0x09)) & 0x01) { + pinmux_val &= 0xFFFFFBFF; + pinmux_val |= EIoHvConfigVal; + } + } + } + + constexpr u32 EschmtConfigVal = (((PinmuxConfigVal >> 0x0D) & 0x1) << 0x0C); + + /* Adjust ESchmt */ + if constexpr (PinmuxConfigMaskVal & 0x2000) { + /* This pin supports ESchmt change */ + if constexpr (PinmuxMaskVal & 0x1000) { + /* Change ESchmt */ + if (((pinmux_val >> 0x0C) ^ (PinmuxConfigVal >> 0x0D)) & 0x01) { + pinmux_val &= 0xFFFFEFFF; + pinmux_val |= EschmtConfigVal; + } + } + } + + constexpr u32 PreempConfigVal = (((PinmuxConfigVal >> 0x10) & 0x1) << 0xF); + + /* Adjust Preemp */ + if constexpr (PinmuxConfigMaskVal & 0x10000) { + /* This pin supports Preemp change */ + if constexpr (PinmuxMaskVal & 0x8000) { + /* Change Preemp */ + if (((pinmux_val >> 0x0F) ^ (PinmuxConfigVal >> 0x10)) & 0x01) { + pinmux_val &= 0xFFFF7FFF; + pinmux_val |= PreempConfigVal; + } + } + } + + constexpr u32 DrvTypeConfigVal = (((PinmuxConfigVal >> 0x0E) & 0x3) << 0xD); + + /* Adjust DrvType */ + if constexpr (PinmuxConfigMaskVal & 0xC000) { + /* This pin supports DrvType change */ + if constexpr (PinmuxMaskVal & 0x6000) { + /* Change DrvType */ + if (((pinmux_val >> 0x0D) ^ (PinmuxConfigVal >> 0x0E)) & 0x03) { + pinmux_val &= 0xFFFF9FFF; + pinmux_val |= DrvTypeConfigVal; + } + } + } + + /* Write to the appropriate PINMUX register */ + reg::Write(pinmux_reg, pinmux_val); + + /* Do a dummy read from the PINMUX register */ + pinmux_val = reg::Read(pinmux_reg); + + return pinmux_val; + } + + } + + void SetPinAssignment(PinAssignment assignment) { + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + AMS_UNUSED(apb_address); + + /* Set the pin assignment. */ + switch (assignment) { + case PinAssignment_Sdmmc1OutputHigh: + { + /* Clear Sdmmc1Clk pulldown. */ + UpdatePinmuxPad(apb_address); + + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(gpio_impl::GpioRegistersPhysicalAddress, gpio_impl::GpioRegistersSize); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as gpio. */ + const uintptr_t cnf_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_CNF, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(cnf_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadCnfGpio); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as high. */ + const uintptr_t out_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OUT, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(out_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOutHigh); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as output. */ + const uintptr_t oe_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OE, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(oe_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOeOutput); + + /* Read to be sure that our configuration takes. */ + reg::Read(oe_address); + } + break; + case PinAssignment_Sdmmc1ResetState: + { + /* Get the gpio registers address. */ + const uintptr_t gpio_address = dd::QueryIoMapping(gpio_impl::GpioRegistersPhysicalAddress, gpio_impl::GpioRegistersSize); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as sfio. */ + const uintptr_t cnf_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_CNF, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(cnf_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadCnfSfio); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as low. */ + const uintptr_t out_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OUT, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(out_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOutLow); + + /* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as input. */ + const uintptr_t oe_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OE, Sdmmc1ClkCmdDat03PadNumber); + gpio_impl::SetMaskedBits(oe_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOeInput); + + /* Read to be sure that our configuration takes. */ + reg::Read(oe_address); + + /* Set Sdmmc1Clk pulldown. */ + UpdatePinmuxPad(apb_address); + } + break; + case PinAssignment_Sdmmc1SchmtEnable: + { + /* Set Schmitt enable for all pins in the group. */ + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + } + break; + case PinAssignment_Sdmmc1SchmtDisable: + { + /* Set Schmitt disable for all pins in the group. */ + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + UpdatePinmuxPad(apb_address); + } + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp new file mode 100644 index 000000000..89b623073 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp @@ -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 . + */ +#pragma once +#include + +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); + + + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.cpp new file mode 100644 index 000000000..7468aaba2 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.cpp @@ -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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#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(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(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(wb), alignof(u32))); + this->mmc_device.SetMemoryCapacity(GetMemoryCapacityFromExtCsd(static_cast(wb))); + + /* If the mmc is manufactured by toshiba, try to enable bkops auto. */ + if (is_toshiba && !IsBkopAutoEnable(static_cast(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(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(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(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(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(); + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.hpp new file mode 100644 index 000000000..97ac86499 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_mmc_device_accessor.hpp @@ -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 . + */ +#pragma once +#include +#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; + }; + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.cpp new file mode 100644 index 000000000..763869d54 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.cpp @@ -0,0 +1,50 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_port_gc_asic0.hpp" +#include "sdmmc_select_sdmmc_controller.hpp" + + +namespace ams::sdmmc::impl { + + namespace { + + SdmmcControllerForPortGcAsic0 g_gc_asic0_host_controller; + GcAsicDeviceAccessor g_gc_asic0_device_accessor(std::addressof(g_gc_asic0_host_controller)); + + } + + IHostController *GetHostControllerOfPortGcAsic0() { + return std::addressof(g_gc_asic0_host_controller); + } + + IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0() { + return std::addressof(g_gc_asic0_device_accessor); + } + + GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0() { + return std::addressof(g_gc_asic0_device_accessor); + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.hpp new file mode 100644 index 000000000..8dc7880a0 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.hpp @@ -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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_i_device_accessor.hpp" +#include "sdmmc_gc_asic_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + IHostController *GetHostControllerOfPortGcAsic0(); + IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0(); + GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0(); + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp new file mode 100644 index 000000000..30cfc869e --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp @@ -0,0 +1,51 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_port_mmc0.hpp" +#include "sdmmc_select_sdmmc_controller.hpp" +#include "sdmmc_base_device_accessor.hpp" + + +namespace ams::sdmmc::impl { + + namespace { + + SdmmcControllerForPortMmc0 g_mmc0_host_controller; + MmcDeviceAccessor g_mmc0_device_accessor(std::addressof(g_mmc0_host_controller)); + + } + + IHostController *GetHostControllerOfPortMmc0() { + return std::addressof(g_mmc0_host_controller); + } + + IDeviceAccessor *GetDeviceAccessorOfPortMmc0() { + return std::addressof(g_mmc0_device_accessor); + } + + MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0() { + return std::addressof(g_mmc0_device_accessor); + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.hpp new file mode 100644 index 000000000..b675bd48c --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.hpp @@ -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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_i_device_accessor.hpp" +#include "sdmmc_mmc_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + IHostController *GetHostControllerOfPortMmc0(); + IDeviceAccessor *GetDeviceAccessorOfPortMmc0(); + MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0(); + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.cpp new file mode 100644 index 000000000..5288a5e88 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.cpp @@ -0,0 +1,63 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_port_sd_card0.hpp" +#include "sdmmc_select_sdmmc_controller.hpp" + + +namespace ams::sdmmc::impl { + + namespace { + + SdmmcControllerForPortSdCard0 g_sd_card0_host_controller; + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + + constexpr inline u32 SdCard0DebounceMilliSeconds = 128; + DeviceDetector g_sd_card0_detector(gpio::DeviceCode_SdCd, gpio::GpioValue_Low, SdCard0DebounceMilliSeconds); + + SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller), std::addressof(g_sd_card0_detector)); + + #else + + SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller)); + + #endif + + + } + + IHostController *GetHostControllerOfPortSdCard0() { + return std::addressof(g_sd_card0_host_controller); + } + + IDeviceAccessor *GetDeviceAccessorOfPortSdCard0() { + return std::addressof(g_sd_card0_device_accessor); + } + + SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0() { + return std::addressof(g_sd_card0_device_accessor); + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.hpp new file mode 100644 index 000000000..690dbb67c --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.hpp @@ -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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_i_device_accessor.hpp" +#include "sdmmc_sd_card_device_accessor.hpp" + +namespace ams::sdmmc::impl { + + IHostController *GetHostControllerOfPortSdCard0(); + IDeviceAccessor *GetDeviceAccessorOfPortSdCard0(); + SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0(); + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.cpp new file mode 100644 index 000000000..9bfff1ad3 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.cpp @@ -0,0 +1,1054 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_sd_card_device_accessor.hpp" +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl { + + #if defined(AMS_SDMMC_THREAD_SAFE) + + #define AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX() std::scoped_lock lk(this->sd_card_device.device_mutex) + + #else + + #define AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX() + + #endif + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + + #define AMS_SDMMC_CHECK_SD_CARD_REMOVED() R_UNLESS(!this->sd_card_device.IsRemoved(), sdmmc::ResultDeviceRemoved()) + + #else + + #define AMS_SDMMC_CHECK_SD_CARD_REMOVED() + + #endif + + namespace { + + constexpr inline u32 OcrCardPowerUpStatus = (1 << 31); + constexpr inline u32 OcrCardCapacityStatus = (1 << 30); + constexpr inline u32 OcrSwitchingTo1_8VAccepted = (1 << 24); + + constexpr bool IsLessThanSpecification1_1(const u8 *scr) { + AMS_ABORT_UNLESS(scr != nullptr); + + const u8 sd_spec = scr[0] & 0xF; + return sd_spec < 1; + } + + constexpr u32 GetSendOpCmdArgument(bool spec_under_2, bool uhs_i_supported) { + const u32 hcs = !spec_under_2 ? (1u << 30) : (0u << 30); + const u32 xpc = !spec_under_2 ? (1u << 28) : (0u << 28); + const u32 s18r = (!spec_under_2 && uhs_i_supported) ? (1u << 24) : (0u << 24); + return hcs | xpc | s18r | 0x00100000u; + } + + constexpr bool IsLessThanCsdVersion2(const u8 *csd) { + AMS_ABORT_UNLESS(csd != nullptr); + + /* Check whether CSD_STRUCTURE is 0. */ + return ((csd[14] & 0xC0) >> 6) == 0; + } + + constexpr u32 GetMemoryCapacityFromCsd(const u16 *csd) { + AMS_ABORT_UNLESS(csd != nullptr); + + /* Get CSIZE, convert appropriately. */ + const u32 csize = (static_cast(csd[3] & 0x3FFF) << 8) | (static_cast(csd[2] & 0xFF00) >> 8); + return (1 + csize) << 10; + } + + constexpr u8 GetSdBusWidths(const u8 *scr) { + AMS_ABORT_UNLESS(scr != nullptr); + return scr[1] & 0xF; + } + + constexpr bool IsSupportedBusWidth4Bit(u8 sd_bw) { + return (sd_bw & 0x4) != 0; + } + + constexpr bool IsSupportedAccessMode(const u8 *status, SwitchFunctionAccessMode access_mode) { + AMS_ABORT_UNLESS(status != nullptr); + + return (status[13] & (1u << access_mode)) != 0; + } + + constexpr u8 GetAccessModeFromFunctionSelection(const u8 *status) { + AMS_ABORT_UNLESS(status != nullptr); + return (status[16] & 0xF); + } + + constexpr bool IsAccessModeInFunctionSelection(const u8 *status, SwitchFunctionAccessMode mode) { + return GetAccessModeFromFunctionSelection(status) == static_cast(mode); + } + + constexpr u16 GetMaximumCurrentConsumption(const u8 *status) { + AMS_ABORT_UNLESS(status != nullptr); + + return (static_cast(status[0]) << 8) | + (static_cast(status[1]) << 0); + } + + constexpr u32 GetSizeOfProtectedArea(const u8 *sd_status) { + return (static_cast(sd_status[4]) << 24) | + (static_cast(sd_status[5]) << 16) | + (static_cast(sd_status[6]) << 8) | + (static_cast(sd_status[7]) << 0); + } + + Result GetCurrentSpeedMode(SpeedMode *out_sm, const u8 *status, bool is_uhs_i) { + AMS_ABORT_UNLESS(out_sm != nullptr); + + /* Get the access mode. */ + switch (static_cast(GetAccessModeFromFunctionSelection(status))) { + case SwitchFunctionAccessMode_Default: + if (is_uhs_i) { + *out_sm = SpeedMode_SdCardSdr12; + } else { + *out_sm = SpeedMode_SdCardDefaultSpeed; + } + break; + case SwitchFunctionAccessMode_HighSpeed: + if (is_uhs_i) { + *out_sm = SpeedMode_SdCardSdr25; + } else { + *out_sm = SpeedMode_SdCardHighSpeed; + } + break; + case SwitchFunctionAccessMode_Sdr50: + *out_sm = SpeedMode_SdCardSdr50; + break; + case SwitchFunctionAccessMode_Sdr104: + *out_sm = SpeedMode_SdCardSdr104; + break; + case SwitchFunctionAccessMode_Ddr50: + *out_sm = SpeedMode_SdCardDdr50; + break; + default: + return sdmmc::ResultUnexpectedSdCardSwitchFunctionStatus(); + } + + return ResultSuccess(); + } + + } + + void SdCardDevice::SetOcrAndHighCapacity(u32 ocr) { + /* Set ocr. */ + BaseDevice::SetOcr(ocr); + + /* Set high capacity. */ + BaseDevice::SetHighCapacity((ocr & OcrCardCapacityStatus) != 0); + } + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + void SdCardDeviceAccessor::RemovedCallback() { + /* Signal that the device was removed. */ + this->sd_card_device.SignalRemovedEvent(); + + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Shut down. */ + BaseDeviceAccessor::GetHostController()->Shutdown(); + } + #endif + + Result SdCardDeviceAccessor::IssueCommandSendRelativeAddr(u16 *out_rca) const { + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R6; + Command command(CommandIndex_SendRelativeAddr, 0, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + + /* Set the output rca. */ + AMS_ABORT_UNLESS(out_rca != nullptr); + *out_rca = static_cast(resp >> 16); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSendIfCond() const { + /* Get the argument. */ + constexpr u32 SendIfCommandArgument = 0x01AAu; + constexpr u32 SendIfCommandArgumentMask = 0x0FFFu; + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R7; + Command command(CommandIndex_SendIfCond, SendIfCommandArgument, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + + /* Verify that our argument was returned to us. */ + R_UNLESS((resp & SendIfCommandArgumentMask) == (SendIfCommandArgument & SendIfCommandArgumentMask), sdmmc::ResultSdCardValidationError()); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandCheckSupportedFunction(void *dst, size_t dst_size) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= SdCardSwitchFunctionStatusSize); + + /* Get the argument. */ + constexpr u32 CheckSupportedFunctionArgument = 0x00FFFFFF; + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(CommandIndex_Switch, CheckSupportedFunctionArgument, CommandResponseType, false); + TransferData xfer_data(dst, SdCardSwitchFunctionStatusSize, 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->sd_card_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSwitchAccessMode(void *dst, size_t dst_size, bool set_function, SwitchFunctionAccessMode access_mode) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= SdCardSwitchFunctionStatusSize); + + /* Get the argument. */ + const u32 arg = (set_function ? (1u << 31) : (0u << 31)) | 0x00FFFFF0 | static_cast(access_mode); + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(CommandIndex_Switch, arg, CommandResponseType, false); + TransferData xfer_data(dst, SdCardSwitchFunctionStatusSize, 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->sd_card_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandVoltageSwitch() const { + /* Issue the command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_VoltageSwitch, 0, false, DeviceState_Ready)); + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandAppCmd(DeviceState expected_state, u32 ignore_mask) const { + /* Get arg. */ + const u32 arg = static_cast(this->sd_card_device.GetRca()) << 16; + + /* Issue the command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(CommandIndex_AppCmd, arg, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + u32 resp; + hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType); + + /* Mask out the ignored status bits. */ + if (ignore_mask != 0) { + resp &= ~ignore_mask; + } + + /* Check the device status. */ + R_TRY(this->sd_card_device.CheckDeviceStatus(resp)); + + /* Check the app command bit. */ + R_UNLESS((resp & DeviceStatus_AppCmd) != 0, sdmmc::ResultUnexpectedSdCardAcmdDisabled()); + + /* Check the device state. */ + if (expected_state != DeviceState_Unknown) { + R_UNLESS(this->sd_card_device.GetDeviceState(resp) == expected_state, sdmmc::ResultUnexpectedDeviceState()); + } + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSetBusWidth4Bit() const { + /* Issue the application command. */ + constexpr u32 Arg = 0x2; + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(SdApplicationCommandIndex_SetBusWidth, Arg, false, DeviceState_Tran)); + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSdStatus(void *dst, size_t dst_size) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= SdCardSdStatusSize); + + /* Issue the application command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(SdApplicationCommandIndex_SdStatus, 0, CommandResponseType, false); + TransferData xfer_data(dst, SdCardSdStatusSize, 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->sd_card_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSendOpCond(u32 *out_ocr, bool spec_under_2, bool uhs_i_supported) const { + /* Get the argument. */ + const u32 arg = GetSendOpCmdArgument(spec_under_2, uhs_i_supported); + + /* Issue the application command. */ + constexpr ResponseType CommandResponseType = ResponseType_R3; + Command command(SdApplicationCommandIndex_SdSendOpCond, arg, CommandResponseType, false); + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->IssueCommand(std::addressof(command))); + + /* Get the response. */ + hc->GetLastResponse(out_ocr, sizeof(u32), CommandResponseType); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandClearCardDetect() const { + /* Issue the application command. */ + R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(SdApplicationCommandIndex_SetClearCardDetect, 0, false, DeviceState_Tran)); + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::IssueCommandSendScr(void *dst, size_t dst_size) const { + /* Validate the output buffer. */ + AMS_ABORT_UNLESS(dst != nullptr); + AMS_ABORT_UNLESS(dst_size >= SdCardScrSize); + + /* Issue the application command. */ + constexpr ResponseType CommandResponseType = ResponseType_R1; + Command command(SdApplicationCommandIndex_SendScr, 0, CommandResponseType, false); + TransferData xfer_data(dst, SdCardScrSize, 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->sd_card_device.CheckDeviceStatus(resp)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::EnterUhsIMode() { + /* Send voltage switch command. */ + R_TRY(this->IssueCommandVoltageSwitch()); + + /* Switch to sdr12. */ + R_TRY(BaseDeviceAccessor::GetHostController()->SwitchToSdr12()); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::ChangeToReadyState(bool spec_under_2, bool uhs_i_supported) { + /* Decide on an ignore mask. */ + u32 ignore_mask = spec_under_2 ? static_cast(DeviceStatus_IllegalCommand) : 0u; + + /* Be prepared to wait up to 3.0 seconds to change state. */ + ManualTimer timer(3000); + while (true) { + /* We want to get ocr, which requires our sending an application command. */ + R_TRY(this->IssueCommandAppCmd(DeviceState_Unknown, ignore_mask)); + ignore_mask = 0; + + /* Get the ocr, and check if we're done. */ + u32 ocr; + R_TRY(this->IssueCommandSendOpCond(std::addressof(ocr), spec_under_2, uhs_i_supported)); + + if ((ocr & OcrCardPowerUpStatus) != 0) { + this->sd_card_device.SetOcrAndHighCapacity(ocr); + + /* Handle uhs i mode. */ + this->sd_card_device.SetUhsIMode(false); + if (uhs_i_supported && ((ocr & OcrSwitchingTo1_8VAccepted) != 0)) { + R_TRY(this->EnterUhsIMode()); + this->sd_card_device.SetUhsIMode(true); + } + + return ResultSuccess(); + } + + /* Check if we've timed out. */ + R_UNLESS(timer.Update(), sdmmc::ResultSdCardInitializationSoftwareTimeout()); + + /* Try again in 1ms. */ + WaitMicroSeconds(1000); + } + } + + Result SdCardDeviceAccessor::ChangeToStbyStateAndGetRca() { + /* Be prepared to wait up to 1.0 seconds to change state. */ + ManualTimer timer(1000); + while (true) { + /* Get rca. */ + u16 rca; + R_TRY(this->IssueCommandSendRelativeAddr(std::addressof(rca))); + if (rca != 0) { + this->sd_card_device.SetRca(rca); + return ResultSuccess(); + } + + /* Check if we've timed out. */ + R_UNLESS(timer.Update(), sdmmc::ResultSdCardGetValidRcaSoftwareTimeout()); + } + } + + Result SdCardDeviceAccessor::SetMemoryCapacity(const void *csd) { + if (IsLessThanCsdVersion2(static_cast(csd))) { + R_TRY(this->sd_card_device.SetLegacyMemoryCapacity()); + } else { + AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast(csd), alignof(u16))); + this->sd_card_device.SetMemoryCapacity(GetMemoryCapacityFromCsd(static_cast(csd))); + } + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetScr(void *dst, size_t dst_size) const { + /* Issue the application command. */ + R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); + R_TRY(this->IssueCommandSendScr(dst, dst_size)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::ExtendBusWidth(BusWidth max_bw, u8 sd_bw) { + /* If the maximum bus width is 1bit, we can't extend. */ + R_SUCCEED_IF(max_bw == BusWidth_1Bit); + + /* If 4bit mode isn't supported, we can't extend. */ + R_SUCCEED_IF(!IsSupportedBusWidth4Bit(sd_bw)); + + /* If the host controller doesn't support 4bit mode, we can't extend. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_SUCCEED_IF(!hc->IsSupportedBusWidth(BusWidth_4Bit)); + + /* Issue the application command to change to 4bit mode. */ + R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); + R_TRY(this->IssueCommandSetBusWidth4Bit()); + + /* Set the host controller's bus width. */ + hc->SetBusWidth(BusWidth_4Bit); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::SwitchAccessMode(SwitchFunctionAccessMode access_mode, void *wb, size_t wb_size) { + /* Issue command to check if we can switch access mode. */ + R_TRY(this->IssueCommandSwitchAccessMode(wb, wb_size, false, access_mode)); + R_UNLESS(IsAccessModeInFunctionSelection(static_cast(wb), access_mode), sdmmc::ResultSdCardCannotSwitchAccessMode()); + + /* Check if we can accept the resulting current consumption. */ + constexpr u16 AcceptableCurrentLimit = 800; /* mA */ + R_UNLESS(GetMaximumCurrentConsumption(static_cast(wb)) < AcceptableCurrentLimit, sdmmc::ResultSdCardUnacceptableCurrentConsumption()); + + /* Switch the access mode. */ + R_TRY(this->IssueCommandSwitchAccessMode(wb, wb_size, true, access_mode)); + R_UNLESS(IsAccessModeInFunctionSelection(static_cast(wb), access_mode), sdmmc::ResultSdCardFailedSwitchAccessMode()); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::ExtendBusSpeedAtUhsIMode(SpeedMode max_sm, void *wb, size_t wb_size) { + /* Check that we're in 4bit bus mode. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_UNLESS(hc->GetBusWidth() == BusWidth_4Bit, sdmmc::ResultSdCardNot4BitBusWidthAtUhsIMode()); + + /* Determine what speed mode/access mode we should switch to. */ + R_TRY(this->IssueCommandCheckSupportedFunction(wb, wb_size)); + SwitchFunctionAccessMode target_am; + SpeedMode target_sm; + if (max_sm == SpeedMode_SdCardSdr104 && IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_Sdr104)) { + target_am = SwitchFunctionAccessMode_Sdr104; + target_sm = SpeedMode_SdCardSdr104; + } else if ((max_sm == SpeedMode_SdCardSdr104 || max_sm == SpeedMode_SdCardSdr50) && IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_Sdr50)) { + target_am = SwitchFunctionAccessMode_Sdr50; + target_sm = SpeedMode_SdCardSdr50; + } else { + return sdmmc::ResultSdCardNotSupportSdr104AndSdr50(); + } + + /* Switch the access mode. */ + R_TRY(this->SwitchAccessMode(target_am, wb, wb_size)); + + /* Set the host controller speed mode and perform tuning using command index 19. */ + R_TRY(hc->SetSpeedMode(target_sm)); + R_TRY(hc->Tuning(target_sm, 19)); + + /* Check status. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::ExtendBusSpeedAtNonUhsIMode(SpeedMode max_sm, bool spec_under_1_1, void *wb, size_t wb_size) { + /* If the maximum speed is default speed, we have nothing to do. */ + R_SUCCEED_IF(max_sm == SpeedMode_SdCardDefaultSpeed); + + /* Otherwise, if the spec is under 1.1, we have nothing to do. */ + R_SUCCEED_IF(spec_under_1_1); + + /* Otherwise, Check if high speed is supported. */ + R_TRY(this->IssueCommandCheckSupportedFunction(wb, wb_size)); + R_SUCCEED_IF(!IsSupportedAccessMode(static_cast(wb), SwitchFunctionAccessMode_HighSpeed)); + + /* Switch the access mode. */ + R_TRY(this->SwitchAccessMode(SwitchFunctionAccessMode_HighSpeed, wb, wb_size)); + + /* Check status. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendStatus()); + + /* Set the host controller speed mode. */ + R_TRY(BaseDeviceAccessor::GetHostController()->SetSpeedMode(SpeedMode_SdCardHighSpeed)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdStatus(void *dst, size_t dst_size) const { + /* Issue the application command. */ + R_TRY(this->IssueCommandAppCmd(DeviceState_Tran)); + R_TRY(this->IssueCommandSdStatus(dst, dst_size)); + + return ResultSuccess(); + } + + void SdCardDeviceAccessor::TryDisconnectDat3PullUpResistor() const { + /* Issue the application command to clear card detect. */ + /* NOTE: Nintendo accepts a failure. */ + if (R_SUCCEEDED(this->IssueCommandAppCmd(DeviceState_Tran))) { + /* NOTE: Nintendo does not check the result of this. */ + this->IssueCommandClearCardDetect(); + } + + /* NOTE: Nintendo does not check the result of this. */ + BaseDeviceAccessor::IssueCommandSendStatus(); + } + + Result SdCardDeviceAccessor::StartupSdCardDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size) { + /* Start up the host controller. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + R_TRY(hc->Startup(BusPower_3_3V, BusWidth_1Bit, SpeedMode_SdCardIdentification, 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()); + + /* Check whether the spec is under 2.0. */ + bool spec_under_2 = false; + R_TRY_CATCH(this->IssueCommandSendIfCond()) { + R_CATCH(sdmmc::ResultResponseTimeoutError) { spec_under_2 = true; } + } R_END_TRY_CATCH; + + /* Set the rca to 0. */ + this->sd_card_device.SetRca(0); + + /* Go to ready state. */ + const bool can_use_uhs_i_mode = (max_bw != BusWidth_1Bit) && (max_sm == SpeedMode_SdCardSdr104 || max_sm == SpeedMode_SdCardSdr50); + const bool uhs_i_supported = hc->IsSupportedTuning() && hc->IsSupportedBusPower(BusPower_1_8V); + R_TRY(this->ChangeToReadyState(spec_under_2, can_use_uhs_i_mode && uhs_i_supported)); + + /* Get the CID. */ + R_TRY(BaseDeviceAccessor::IssueCommandAllSendCid(wb, wb_size)); + this->sd_card_device.SetCid(wb, wb_size); + + /* Go to stby state and get the RCA. */ + R_TRY(this->ChangeToStbyStateAndGetRca()); + + /* Get the CSD. */ + R_TRY(BaseDeviceAccessor::IssueCommandSendCsd(wb, wb_size)); + this->sd_card_device.SetCsd(wb, wb_size); + R_TRY(this->SetMemoryCapacity(wb)); + + /* Set the host controller speed mode to default if we're not in uhs i mode. */ + if (!this->sd_card_device.IsUhsIMode()) { + R_TRY(hc->SetSpeedMode(SpeedMode_SdCardDefaultSpeed)); + } + + /* Issue select card command. */ + R_TRY(BaseDeviceAccessor::IssueCommandSelectCard()); + + /* Set block length to sector size. */ + R_TRY(BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize()); + + /* Try to disconnect dat3 pullup resistor. */ + TryDisconnectDat3PullUpResistor(); + + /* Get the SCR. */ + R_TRY(this->GetScr(wb, wb_size)); + const u8 sd_bw = GetSdBusWidths(static_cast(wb)); + const bool spec_under_1_1 = IsLessThanSpecification1_1(static_cast(wb)); + + /* Extend the bus width to the largest that we can. */ + R_TRY(this->ExtendBusWidth(max_bw, sd_bw)); + + /* Extend the bus speed to as fast as we can. */ + if (this->sd_card_device.IsUhsIMode()) { + R_TRY(this->ExtendBusSpeedAtUhsIMode(max_sm, wb, wb_size)); + } else { + R_TRY(this->ExtendBusSpeedAtNonUhsIMode(max_sm, spec_under_1_1, wb, wb_size)); + } + + /* Enable power saving. */ + hc->SetPowerSaving(true); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::OnActivate() { + /* Define the possible startup parameters. */ + constexpr const struct { + BusWidth bus_width; + SpeedMode speed_mode; + } StartupParameters[] = { + #if defined(AMS_SDMMC_ENABLE_SD_UHS_I) + { BusWidth_4Bit, SpeedMode_SdCardSdr104 }, + { BusWidth_4Bit, SpeedMode_SdCardSdr104 }, + { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, + { BusWidth_4Bit, SpeedMode_SdCardDefaultSpeed }, + { BusWidth_1Bit, SpeedMode_SdCardHighSpeed }, + #else + { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, + { BusWidth_4Bit, SpeedMode_SdCardHighSpeed }, + { BusWidth_4Bit, SpeedMode_SdCardDefaultSpeed }, + { BusWidth_1Bit, SpeedMode_SdCardHighSpeed }, + #endif + }; + + /* Try to start up with each set of parameters. */ + Result result; + for (int i = 0; i < static_cast(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->StartupSdCardDevice(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(); + } + + /* Check if we were removed. */ + AMS_SDMMC_CHECK_SD_CARD_REMOVED(); + + /* 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. */ + /* Check the csd for errors. */ + if (sdmmc::ResultUnexpectedDeviceCsdValue::Includes(result)) { + u32 csd[DeviceCsdSize / sizeof(u32)]; + this->sd_card_device.GetCsd(csd, sizeof(csd)); + BaseDeviceAccessor::PushErrorLog(false, "%06X%08X%08X%08X", csd[3] & 0x00FFFFFF, csd[2], csd[1], csd[0]); + } + BaseDeviceAccessor::PushErrorTimeStamp(); + + /* Check if we failed because the sd card is removed. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + if (sdmmc::ResultCommunicationNotAttained::Includes(result)) { + WaitMicroSeconds(this->sd_card_detector->GetDebounceMilliSeconds() * 1000); + AMS_SDMMC_CHECK_SD_CARD_REMOVED(); + } + #endif + + return result; + } + + Result SdCardDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) { + /* Do the read/write. */ + const Result result = BaseDeviceAccessor::ReadWriteMultiple(sector_index, num_sectors, 0, buf, buf_size, is_read); + + /* Check if we failed because the sd card is removed. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + if (sdmmc::ResultCommunicationNotAttained::Includes(result)) { + WaitMicroSeconds(this->sd_card_detector->GetDebounceMilliSeconds() * 1000); + AMS_SDMMC_CHECK_SD_CARD_REMOVED(); + } + #endif + + return result; + } + + Result SdCardDeviceAccessor::ReStartup() { + /* Shut down the host controller. */ + BaseDeviceAccessor::GetHostController()->Shutdown(); + + /* Perform start up. */ + Result result = this->StartupSdCardDevice(this->max_bus_width, this->max_speed_mode, this->work_buffer, this->work_buffer_size); + if (R_FAILED(result)) { + AMS_SDMMC_CHECK_SD_CARD_REMOVED(); + + BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", this->max_bus_width, this->max_speed_mode, result.GetValue()); + return result; + } + + return ResultSuccess(); + } + + void SdCardDeviceAccessor::Initialize() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_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 sd card device. */ + BaseDeviceAccessor::SetDevice(std::addressof(this->sd_card_device)); + + /* Initialize. */ + IHostController *hc = BaseDeviceAccessor::GetHostController(); + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + { + /* TODO: We probably want this (and other sd card detection stuff) to be conditional pcv control active. */ + /* This will be a requirement to support sd card access with detector in stratosphere before PCV is alive. */ + this->sd_card_device.InitializeRemovedEvent(); + hc->PreSetRemovedEvent(this->sd_card_device.GetRemovedEvent()); + CallbackInfo ci = { + .inserted_callback = nullptr, + .inserted_callback_arg = this, + .removed_callback = RemovedCallbackEntry, + .removed_callback_arg = this, + }; + this->sd_card_detector->Initialize(std::addressof(ci)); + } + #endif + hc->Initialize(); + + /* Mark ourselves as initialized. */ + this->is_initialized = true; + } + + void SdCardDeviceAccessor::Finalize() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_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 detector. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + { + this->sd_card_detector->Finalize(); + this->sd_card_device.FinalizeRemovedEvent(); + } + #endif + } + + Result SdCardDeviceAccessor::Activate() { + /* Activate the detector. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're awake. */ + R_UNLESS(this->sd_card_device.IsAwake(), sdmmc::ResultNotAwakened()); + + /* Check that we're not already active. */ + R_SUCCEED_IF(this->sd_card_device.IsActive()); + + /* Clear the removed event. */ + this->sd_card_device.ClearRemovedEvent(); + + /* Check that the SD card is inserted. */ + R_UNLESS(this->sd_card_detector->IsInserted(), sdmmc::ResultNoDevice()); + } + #endif + + /* Activate the base device. */ + return BaseDeviceAccessor::Activate(); + } + + Result SdCardDeviceAccessor::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_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Check whether we're specification 1 (and thus default speed). */ + R_TRY(this->GetScr(this->work_buffer, this->work_buffer_size)); + if (IsLessThanSpecification1_1(static_cast(this->work_buffer))) { + *out_speed_mode = SpeedMode_SdCardDefaultSpeed; + return ResultSuccess(); + } + + /* Get the current speed mode. */ + R_TRY(this->IssueCommandCheckSupportedFunction(this->work_buffer, this->work_buffer_size)); + R_TRY(GetCurrentSpeedMode(out_speed_mode, static_cast(this->work_buffer), this->sd_card_device.IsUhsIMode())); + + return ResultSuccess(); + } + + void SdCardDeviceAccessor::PutSdCardToSleep() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* If the device isn't awake, we don't need to do anything. */ + if (!this->sd_card_device.IsAwake()) { + return; + } + + /* Put the device to sleep. */ + this->sd_card_device.PutToSleep(); + + /* Put the detector to sleep. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + this->sd_card_detector->PutToSleep(); + #endif + + /* If necessary, put the host controller to sleep. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + if (this->sd_card_device.IsActive() && !this->sd_card_device.IsRemoved()) + #else + if (this->sd_card_device.IsActive()) + #endif + { + BaseDeviceAccessor::GetHostController()->PutToSleep(); + } + } + + void SdCardDeviceAccessor::AwakenSdCard() { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* If the device is awake, we don't need to do anything. */ + if (this->sd_card_device.IsAwake()) { + return; + } + + /* Wake the host controller, if we need to.*/ + bool force_det = false; + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + if (this->sd_card_device.IsActive() && !this->sd_card_device.IsRemoved()) + #else + if (this->sd_card_device.IsActive()) + #endif + { + const Result result = BaseDeviceAccessor::GetHostController()->Awaken(); + if (R_SUCCEEDED(result)) { + force_det = R_FAILED(BaseDeviceAccessor::IssueCommandSendStatus()); + } else { + BaseDeviceAccessor::PushErrorLog(true, "A:%X", result.GetValue()); + force_det = true; + } + } + + /* Wake the detector. */ + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + this->sd_card_detector->Awaken(force_det); + #else + AMS_UNUSED(force_det); + #endif + + /* Wake the device. */ + this->sd_card_device.Awaken(); + } + + Result SdCardDeviceAccessor::GetSdCardScr(void *dst, size_t dst_size) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Get the SCR. */ + R_TRY(this->GetScr(dst, dst_size)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, SdCardSwitchFunction switch_function) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Check whether we're specification 1 (and thus can't switch). */ + R_TRY(this->GetScr(dst, dst_size)); + R_UNLESS(!IsLessThanSpecification1_1(static_cast(dst)), sdmmc::ResultSdCardNotSupportSwitchFunctionStatus()); + + /* Get the status. */ + if (switch_function == SdCardSwitchFunction_CheckSupportedFunction) { + R_TRY(this->IssueCommandCheckSupportedFunction(dst, dst_size)); + } else { + SwitchFunctionAccessMode am; + switch (switch_function) { + case SdCardSwitchFunction_CheckDefault: am = SwitchFunctionAccessMode_Default; break; + case SdCardSwitchFunction_CheckHighSpeed: am = SwitchFunctionAccessMode_HighSpeed; break; + case SdCardSwitchFunction_CheckSdr50: am = SwitchFunctionAccessMode_Sdr50; break; + case SdCardSwitchFunction_CheckSdr104: am = SwitchFunctionAccessMode_Sdr104; break; + case SdCardSwitchFunction_CheckDdr50: am = SwitchFunctionAccessMode_Ddr50; break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + R_TRY(this->IssueCommandSwitchAccessMode(dst, dst_size, false, am)); + } + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdCardCurrentConsumption(u16 *out_current_consumption, SpeedMode speed_mode) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Check whether we're specification 1 (and thus can't switch). */ + R_TRY(this->GetScr(this->work_buffer, this->work_buffer_size)); + R_UNLESS(!IsLessThanSpecification1_1(static_cast(this->work_buffer)), sdmmc::ResultSdCardNotSupportSwitchFunctionStatus()); + + /* Determine the access mode. */ + SwitchFunctionAccessMode am; + switch (speed_mode) { + case SpeedMode_SdCardSdr12: + case SpeedMode_SdCardDefaultSpeed: + am = SwitchFunctionAccessMode_Default; + break; + case SpeedMode_SdCardSdr25: + case SpeedMode_SdCardHighSpeed: + am = SwitchFunctionAccessMode_HighSpeed; + break; + case SpeedMode_SdCardSdr50: + am = SwitchFunctionAccessMode_Sdr50; + break; + case SpeedMode_SdCardSdr104: + am = SwitchFunctionAccessMode_Sdr104; + break; + case SpeedMode_SdCardDdr50: + am = SwitchFunctionAccessMode_Ddr50; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Check that the mode is supported. */ + R_TRY(this->IssueCommandSwitchAccessMode(this->work_buffer, this->work_buffer_size, false, am)); + R_UNLESS(IsSupportedAccessMode(static_cast(this->work_buffer), am), sdmmc::ResultSdCardNotSupportAccessMode()); + + /* Get the current consumption. */ + AMS_ABORT_UNLESS(out_current_consumption != nullptr); + *out_current_consumption = GetMaximumCurrentConsumption(static_cast(this->work_buffer)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdCardSdStatus(void *dst, size_t dst_size) const { + /* Acquire exclusive access to the device. */ + AMS_SDMMC_LOCK_SD_CARD_DEVICE_MUTEX(); + + /* Check that we're accessible. */ + R_TRY(this->sd_card_device.CheckAccessible()); + + /* Get the status. */ + R_TRY(this->GetSdStatus(dst, dst_size)); + + return ResultSuccess(); + } + + Result SdCardDeviceAccessor::GetSdCardProtectedAreaCapacity(u32 *out_num_sectors) const { + AMS_ABORT_UNLESS(out_num_sectors != nullptr); + + /* Get the sd status. */ + R_TRY(this->GetSdCardSdStatus(this->work_buffer, this->work_buffer_size)); + const u32 size_of_protected_area = GetSizeOfProtectedArea(static_cast(this->work_buffer)); + + /* Get the csd. */ + u8 csd[DeviceCsdSize]; + this->sd_card_device.GetCsd(csd, sizeof(csd)); + + /* Handle based on csd version. */ + if (IsLessThanCsdVersion2(csd)) { + /* Get c_size_mult and read_bl_len. */ + u8 c_size_mult, read_bl_len; + this->sd_card_device.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()); + + /* Calculate capacity. */ + *out_num_sectors = size_of_protected_area << ((read_bl_len + c_size_mult + 2) - 9); + } else { + /* SIZE_OF_PROTECTED_AREA is in bytes. */ + *out_num_sectors = size_of_protected_area / SectorSize; + } + + return ResultSuccess(); + } + + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.hpp new file mode 100644 index 000000000..4f37b0c29 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_card_device_accessor.hpp @@ -0,0 +1,222 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_base_device_accessor.hpp" + +#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) +#include "sdmmc_device_detector.hpp" +#endif + +namespace ams::sdmmc::impl { + + class SdCardDevice : public BaseDevice { + private: + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + mutable os::EventType removed_event; + #endif + u16 rca; + bool is_valid_rca; + bool is_uhs_i_mode; + public: + SdCardDevice() : rca(0) { + this->OnDeactivate(); + } + + virtual void Deactivate() override { + this->OnDeactivate(); + BaseDevice::Deactivate(); + } + + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + virtual os::EventType *GetRemovedEvent() const override { + return std::addressof(this->removed_event); + } + #elif 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_SdCard; + } + + virtual u16 GetRca() const override { + AMS_ABORT_UNLESS(this->is_valid_rca); + return this->rca; + } + + void OnDeactivate() { + this->is_valid_rca = false; + this->is_uhs_i_mode = false; + } + + void SetRca(u16 v) { + this->rca = v; + this->is_valid_rca = true; + } + + void SetOcrAndHighCapacity(u32 ocr); + + void SetUhsIMode(bool en) { + this->is_uhs_i_mode = en; + } + + bool IsUhsIMode() const { + return this->is_uhs_i_mode; + } + }; + + enum SdCardApplicationCommandIndex : std::underlying_type::type { + SdApplicationCommandIndex_SetBusWidth = 6, + + SdApplicationCommandIndex_SdStatus = 13, + + SdApplicationCommandIndex_SendNumWriteBlocks = 22, + SdApplicationCommandIndex_SetWriteBlockEraseCount = 23, + + SdApplicationCommandIndex_SdSendOpCond = 41, + SdApplicationCommandIndex_SetClearCardDetect = 42, + + SdApplicationCommandIndex_SendScr = 51, + }; + + enum SwitchFunctionAccessMode { + SwitchFunctionAccessMode_Default = 0, + SwitchFunctionAccessMode_HighSpeed = 1, + SwitchFunctionAccessMode_Sdr50 = 2, + SwitchFunctionAccessMode_Sdr104 = 3, + SwitchFunctionAccessMode_Ddr50 = 4, + }; + + class SdCardDeviceAccessor : public BaseDeviceAccessor { + private: + SdCardDevice sd_card_device; + void *work_buffer; + size_t work_buffer_size; + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + DeviceDetector *sd_card_detector; + #endif + BusWidth max_bus_width; + SpeedMode max_speed_mode; + bool is_initialized; + private: + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + void RemovedCallback(); + + static void RemovedCallbackEntry(void *arg) { + static_cast(arg)->RemovedCallback(); + } + #endif + + Result IssueCommandSendRelativeAddr(u16 *out_rca) const; + Result IssueCommandSendIfCond() const; + Result IssueCommandCheckSupportedFunction(void *dst, size_t dst_size) const; + Result IssueCommandSwitchAccessMode(void *dst, size_t dst_size, bool set_function, SwitchFunctionAccessMode access_mode) const; + Result IssueCommandVoltageSwitch() const; + Result IssueCommandAppCmd(DeviceState expected_state, u32 ignore_mask = 0) const; + Result IssueCommandSetBusWidth4Bit() const; + Result IssueCommandSdStatus(void *dst, size_t dst_size) const; + Result IssueCommandSendOpCond(u32 *out_ocr, bool spec_under_2, bool uhs_i_supported) const; + Result IssueCommandClearCardDetect() const; + Result IssueCommandSendScr(void *dst, size_t dst_size) const; + + Result EnterUhsIMode(); + Result ChangeToReadyState(bool spec_under_2, bool uhs_i_supported); + Result ChangeToStbyStateAndGetRca(); + Result SetMemoryCapacity(const void *csd); + Result GetScr(void *dst, size_t dst_size) const; + Result ExtendBusWidth(BusWidth max_bw, u8 sd_bw); + Result SwitchAccessMode(SwitchFunctionAccessMode access_mode, void *wb, size_t wb_size); + Result ExtendBusSpeedAtUhsIMode(SpeedMode max_sm, void *wb, size_t wb_size); + Result ExtendBusSpeedAtNonUhsIMode(SpeedMode max_sm, bool spec_under_1_1, void *wb, size_t wb_size); + Result GetSdStatus(void *dst, size_t dst_size) const; + Result StartupSdCardDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size); + + void TryDisconnectDat3PullUpResistor() const; + 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 Activate() override; + virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override; + public: + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + explicit SdCardDeviceAccessor(IHostController *hc, DeviceDetector *dd) : BaseDeviceAccessor(hc), sd_card_detector(dd) + #else + explicit SdCardDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc) + #endif + { + this->work_buffer = nullptr; + this->work_buffer_size = 0; + this->max_bus_width = BusWidth_4Bit; + this->max_speed_mode = SpeedMode_SdCardSdr104; + this->is_initialized = false; + } + + void SetSdCardWorkBuffer(void *wb, size_t wb_size) { + this->work_buffer = wb; + this->work_buffer_size = wb_size; + } + + void PutSdCardToSleep(); + void AwakenSdCard(); + Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors) const; + Result GetSdCardScr(void *dst, size_t dst_size) const; + Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, SdCardSwitchFunction switch_function) const; + Result GetSdCardCurrentConsumption(u16 *out_current_consumption, SpeedMode speed_mode) const; + Result GetSdCardSdStatus(void *dst, size_t dst_size) const; + + bool IsSdCardInserted() { + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + return this->sd_card_detector->IsInserted(); + #else + AMS_ABORT("IsSdCardInserted without SdCardDetector"); + #endif + } + + bool IsSdCardRemoved() { + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + return this->sd_card_device.IsRemoved(); + #else + AMS_ABORT("IsSdCardRemoved without SdCardDetector"); + #endif + } + + void RegisterSdCardDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) { + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + return this->sd_card_detector->RegisterDetectionEventCallback(cb, arg); + #else + AMS_UNUSED(cb, arg); + AMS_ABORT("RegisterSdCardDetectionEventCallback without SdCardDetector"); + #endif + } + + void UnregisterSdCardDetectionEventCallback() { + #if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR) + return this->sd_card_detector->UnregisterDetectionEventCallback(); + #else + AMS_ABORT("UnregisterSdCardDetectionEventCallback without SdCardDetector"); + #endif + } + }; + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp new file mode 100644 index 000000000..5cb9af01a --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp @@ -0,0 +1,1045 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_sd_host_standard_controller.hpp" +#include "sdmmc_timer.hpp" + +#if defined(ATMOSPHERE_IS_STRATOSPHERE) + #include +#endif + + +namespace ams::sdmmc::impl { + + namespace { + + constexpr inline u32 ControllerReactionTimeoutMilliSeconds = 2000; + constexpr inline u32 CommandTimeoutMilliSeconds = 2000; + constexpr inline u32 DefaultCheckTransferIntervalMilliSeconds = 1500; + constexpr inline u32 BusyTimeoutMilliSeconds = 2000; + + } + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + void SdHostStandardController::ResetBufferInfos() { + for (auto &info : this->buffer_infos) { + info.buffer_address = 0; + info.buffer_size = 0; + } + } + + dd::DeviceVirtualAddress SdHostStandardController::GetDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size) { + /* Try to find the buffer in our registered regions. */ + dd::DeviceVirtualAddress device_addr = 0; + for (const auto &info : this->buffer_infos) { + if (info.buffer_address <= buffer && (buffer + buffer_size) <= (info.buffer_address + info.buffer_size)) { + device_addr = info.buffer_device_virtual_address + (buffer - info.buffer_address); + break; + } + } + + /* Ensure that we found the buffer. */ + AMS_ABORT_UNLESS(device_addr != 0); + return device_addr; + } + #endif + + void SdHostStandardController::EnsureControl() { + /* Perform a read of clock control to be sure previous configuration takes. */ + reg::Read(this->registers->clock_control); + } + + Result SdHostStandardController::EnableInternalClock() { + /* Enable internal clock. */ + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE, OSCILLATE)); + this->EnsureControl(); + + /* Wait for the internal clock to become stable. */ + { + ManualTimer timer(ControllerReactionTimeoutMilliSeconds); + while (true) { + /* Check if the clock is steady. */ + if (reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_STABLE, READY))) { + break; + } + + /* If not, check for timeout. */ + R_UNLESS(timer.Update(), sdmmc::ResultInternalClockStableSoftwareTimeout()); + } + } + + /* Configure to use host controlled divided clock. */ + reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_PRESET_VALUE_ENABLE, HOST_DRIVER)); + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_CLOCK_GENERATOR_SELECT, DIVIDED_CLOCK)); + + /* Set host version 4.0.0 enable. */ + reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_HOST_VERSION_4_ENABLE, VERSION_4)); + + /* Set host 64 bit addressing enable. */ + AMS_ABORT_UNLESS(reg::HasValue(this->registers->capabilities, SD_REG_BITS_ENUM(CAPABILITIES_64_BIT_SYSTEM_ADDRESS_SUPPORT_FOR_V3, SUPPORTED))); + reg::ReadWrite(this->registers->host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_64_BIT_ADDRESSING, 64_BIT_ADDRESSING)); + + /* Select SDMA mode. */ + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DMA_SELECT, SDMA)); + + /* Configure timeout control to use the maximum timeout value (TMCLK * 2^27) */ + reg::ReadWrite(this->registers->timeout_control, SD_REG_BITS_VALUE(TIMEOUT_CONTROL_DATA_TIMEOUT_COUNTER, 0b1110)); + + return ResultSuccess(); + } + + void SdHostStandardController::SetBusPower(BusPower bus_power) { + /* Check that we support the bus power. */ + AMS_ABORT_UNLESS(this->IsSupportedBusPower(bus_power)); + + /* Set the appropriate power. */ + switch (bus_power) { + case BusPower_Off: + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, OFF)); + break; + case BusPower_1_8V: + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 1_8V)); + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON)); + break; + case BusPower_3_3V: + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 3_3V)); + reg::ReadWrite(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void SdHostStandardController::EnableInterruptStatus() { + /* Set the status register interrupt enables. */ + reg::ReadWrite(this->registers->normal_int_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(ENABLED)); + reg::ReadWrite(this->registers->error_int_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (ENABLED)); + + /* Read/write the interrupt enables to be sure they take. */ + reg::Write(this->registers->normal_int_enable, reg::Read(this->registers->normal_int_enable)); + reg::Write(this->registers->error_int_enable, reg::Read(this->registers->error_int_enable)); + + /* If we're using interrupt events, configure appropriately. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + /* Clear interrupts. */ + this->ClearInterrupt(); + + /* Enable the interrupt signals. */ + reg::ReadWrite(this->registers->normal_signal_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(ENABLED)); + reg::ReadWrite(this->registers->error_signal_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (ENABLED)); + } + #endif + } + + void SdHostStandardController::DisableInterruptStatus() { + /* If we're using interrupt events, configure appropriately. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + /* Disable the interrupt signals. */ + reg::ReadWrite(this->registers->normal_signal_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(MASKED)); + reg::ReadWrite(this->registers->error_signal_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (MASKED)); + } + #endif + + /* Mask the status register interrupt enables. */ + reg::ReadWrite(this->registers->normal_int_enable, SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(MASKED)); + reg::ReadWrite(this->registers->error_int_enable, SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND (MASKED)); + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + Result SdHostStandardController::WaitInterrupt(u32 timeout_ms) { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait for the interrupt to be signaled. */ + os::WaitableHolderType *signaled_holder = os::TimedWaitAny(std::addressof(this->waitable_manager), TimeSpan::FromMilliSeconds(timeout_ms)); + if (signaled_holder == std::addressof(this->interrupt_event_holder)) { + /* We received the interrupt. */ + return ResultSuccess(); + } else if (signaled_holder == std::addressof(this->removed_event_holder)) { + /* The device was removed. */ + return sdmmc::ResultDeviceRemoved(); + } else { + /* Timeout occurred. */ + return sdmmc::ResultWaitInterruptSoftwareTimeout(); + } + } + + void SdHostStandardController::ClearInterrupt() { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Clear the interrupt event. */ + os::ClearInterruptEvent(this->interrupt_event); + } + #endif + + void SdHostStandardController::SetTransfer(u32 *out_num_transferred_blocks, const TransferData *xfer_data) { + /* Ensure the transfer data is valid. */ + AMS_ABORT_UNLESS(xfer_data->block_size != 0); + AMS_ABORT_UNLESS(xfer_data->num_blocks != 0); + + /* Determine the number of blocks. */ + const u16 num_blocks = std::min(xfer_data->num_blocks, SdHostStandardRegisters::BlockCountMax); + + /* Determine the address/how many blocks to transfer. */ + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + const u64 address = this->GetDeviceVirtualAddress(reinterpret_cast(xfer_data->buffer), xfer_data->block_size * num_blocks); + const u16 num_xfer_blocks = num_blocks; + #else + const u64 address = reinterpret_cast(xfer_data->buffer); + const u16 num_xfer_blocks = num_blocks; + #endif + + /* Verify the address is usable. */ + AMS_ABORT_UNLESS(util::IsAligned(address, BufferDeviceVirtualAddressAlignment)); + + /* Configure for sdma. */ + reg::Write(this->registers->adma_address, static_cast(address >> 0)); + reg::Write(this->registers->upper_adma_address, static_cast(address >> BITSIZEOF(u32))); + + /* Set our next sdma address. */ + this->next_sdma_address = util::AlignDown(address + SdmaBufferBoundary, SdmaBufferBoundary); + + /* Configure block size. */ + AMS_ABORT_UNLESS(xfer_data->block_size <= SdHostStandardBlockSizeTransferBlockSizeMax); + reg::Write(this->registers->block_size, SD_REG_BITS_ENUM (BLOCK_SIZE_SDMA_BUFFER_BOUNDARY, 512_KB), + SD_REG_BITS_VALUE(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, static_cast(xfer_data->block_size))); + + /* Configure transfer blocks. */ + reg::Write(this->registers->block_count, num_xfer_blocks); + if (out_num_transferred_blocks != nullptr) { + *out_num_transferred_blocks = num_xfer_blocks; + } + + /* Configure transfer mode. */ + reg::Write(this->registers->transfer_mode, SD_REG_BITS_ENUM (TRANSFER_MODE_DMA_ENABLE, ENABLE), + SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_BLOCK_COUNT_ENABLE, (xfer_data->is_multi_block_transfer), ENABLE, DISABLE), + SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_MULTI_BLOCK_SELECT, (xfer_data->is_multi_block_transfer), MULTI_BLOCK, SINGLE_BLOCK), + SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, (xfer_data->transfer_direction == TransferDirection_ReadFromDevice), READ, WRITE), + SD_REG_BITS_ENUM_SEL(TRANSFER_MODE_AUTO_CMD_ENABLE, (xfer_data->is_stop_transmission_command_enabled), CMD12_ENABLE, DISABLE)); + } + + void SdHostStandardController::SetTransferForTuning() { + /* Get the tuning block size. */ + u16 tuning_block_size; + switch (this->GetBusWidth()) { + case BusWidth_4Bit: + tuning_block_size = 64; + break; + case BusWidth_8Bit: + tuning_block_size = 128; + break; + case BusWidth_1Bit: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Configure block size. */ + AMS_ABORT_UNLESS(tuning_block_size <= SdHostStandardBlockSizeTransferBlockSizeMax); + reg::Write(this->registers->block_size, SD_REG_BITS_VALUE(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, tuning_block_size)); + + /* Configure transfer blocks. */ + reg::Write(this->registers->block_count, 1); + + /* Configure transfer mode. */ + reg::Write(this->registers->transfer_mode, SD_REG_BITS_ENUM(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, READ)); + } + + void SdHostStandardController::SetCommand(const Command *command, bool has_xfer_data) { + /* Encode the command value. */ + u16 command_val = 0; + + /* Encode the response type. */ + switch (command->response_type) { + case ResponseType_R0: + command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, NO_RESPONSE), + SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, DISABLE), + SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); + break; + case ResponseType_R1: + case ResponseType_R6: + case ResponseType_R7: + command_val |= reg::Encode(SD_REG_BITS_ENUM_SEL(COMMAND_RESPONSE_TYPE, command->is_busy, RESPONSE_LENGTH_48_CHECK_BUSY_AFTER_RESPONSE, RESPONSE_LENGTH_48), + SD_REG_BITS_ENUM (COMMAND_CRC_CHECK, ENABLE), + SD_REG_BITS_ENUM (COMMAND_INDEX_CHECK, ENABLE)); + break; + case ResponseType_R2: + command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, RESPONSE_LENGTH_136), + SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, ENABLE), + SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); + break; + case ResponseType_R3: + command_val |= reg::Encode(SD_REG_BITS_ENUM(COMMAND_RESPONSE_TYPE, RESPONSE_LENGTH_48), + SD_REG_BITS_ENUM(COMMAND_CRC_CHECK, DISABLE), + SD_REG_BITS_ENUM(COMMAND_INDEX_CHECK, DISABLE)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Encode the data present select. */ + command_val |= reg::Encode(SD_REG_BITS_ENUM_SEL(COMMAND_DATA_PRESENT, has_xfer_data, DATA_PRESENT, NO_DATA_PRESENT)); + + /* Encode the command index. */ + AMS_ABORT_UNLESS(command->command_index <= SdHostStandardCommandIndexMax); + command_val |= reg::Encode(SD_REG_BITS_VALUE(COMMAND_COMMAND_INDEX, command->command_index)); + + /* Write the command and argument. */ + reg::Write(this->registers->argument, command->command_argument); + reg::Write(this->registers->command, command_val); + } + + void SdHostStandardController::SetCommandForTuning(u32 command_index) { + Command command(command_index, 0, ResponseType_R1, false); + return this->SetCommand(std::addressof(command), true); + } + + Result SdHostStandardController::ResetCmdDatLine() { + /* Set the software reset cmd/dat bits. */ + reg::ReadWrite(this->registers->software_reset, SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_CMD, RESET), + SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_DAT, RESET)); + + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait until reset is done. */ + { + ManualTimer timer(ControllerReactionTimeoutMilliSeconds); + while (true) { + /* Check if the target has been removed. */ + R_TRY(this->CheckRemoved()); + + /* Check if command inhibit is no longer present. */ + if (reg::HasValue(this->registers->software_reset, SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_CMD, WORK), + SD_REG_BITS_ENUM(SOFTWARE_RESET_FOR_DAT, WORK))) + { + break; + } + + /* Otherwise, check if we've timed out. */ + if (!timer.Update()) { + return sdmmc::ResultAbortTransactionSoftwareTimeout(); + } + } + } + + return ResultSuccess(); + } + + Result SdHostStandardController::AbortTransaction() { + R_TRY(this->ResetCmdDatLine()); + return ResultSuccess(); + } + + Result SdHostStandardController::WaitWhileCommandInhibit(bool has_dat) { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait while command inhibit cmd is set. */ + { + ManualTimer timer(ControllerReactionTimeoutMilliSeconds); + while (true) { + /* Check if the target has been removed. */ + R_TRY(this->CheckRemoved()); + + /* Check if command inhibit is no longer present. */ + if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_COMMAND_INHIBIT_CMD, READY))) { + break; + } + + /* Otherwise, check if we've timed out. */ + if (!timer.Update()) { + this->AbortTransaction(); + return sdmmc::ResultCommandInhibitCmdSoftwareTimeout(); + } + } + } + + /* Wait while command inhibit dat is set. */ + if (has_dat) { + ManualTimer timer(ControllerReactionTimeoutMilliSeconds); + while (true) { + /* Check if the target has been removed. */ + R_TRY(this->CheckRemoved()); + + /* Check if command inhibit is no longer present. */ + if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_COMMAND_INHIBIT_DAT, READY))) { + break; + } + + /* Otherwise, check if we've timed out. */ + if (!timer.Update()) { + this->AbortTransaction(); + return sdmmc::ResultCommandInhibitDatSoftwareTimeout(); + } + } + } + + return ResultSuccess(); + } + + Result SdHostStandardController::CheckAndClearInterruptStatus(volatile u16 *out_normal_int_status, u16 wait_mask) { + /* Read the statuses. */ + volatile u16 normal_int_status = reg::Read(this->registers->normal_int_status); + volatile u16 error_int_status = reg::Read(this->registers->error_int_status); + volatile u16 auto_cmd_err_status = reg::Read(this->registers->acmd12_err); + + /* Set the output status, if necessary. */ + if (out_normal_int_status != nullptr) { + *out_normal_int_status = normal_int_status; + } + + /* If we don't have an error interrupt, just use the normal status. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_ERROR_INTERRUPT, NO_ERROR))) { + /* If the wait mask has any bits set, we're done waiting for the interrupt. */ + const u16 masked_status = (normal_int_status & wait_mask); + R_UNLESS(masked_status != 0, sdmmc::ResultNoWaitedInterrupt()); + + /* Write the masked value to the status register to ensure consistent state. */ + reg::Write(this->registers->normal_int_status, masked_status); + return ResultSuccess(); + } + + /* We have an error interrupt. Write the status to the register to ensure consistent state. */ + reg::Write(this->registers->error_int_status, error_int_status); + + /* Check the error interrupt status bits, and return appropriate errors. */ + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_INDEX, NO_ERROR)), sdmmc::ResultResponseIndexError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_END_BIT, NO_ERROR)), sdmmc::ResultResponseEndBitError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_CRC, NO_ERROR)), sdmmc::ResultResponseCrcError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_TIMEOUT, NO_ERROR)), sdmmc::ResultResponseTimeoutError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_END_BIT, NO_ERROR)), sdmmc::ResultDataEndBitError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_CRC, NO_ERROR)), sdmmc::ResultDataCrcError()); + R_UNLESS(reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_DATA_TIMEOUT, NO_ERROR)), sdmmc::ResultDataTimeoutError()); + + /* Check for auto cmd errors. */ + if (reg::HasValue(error_int_status, SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_AUTO_CMD, ERROR))) { + R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_INDEX, NO_ERROR)), sdmmc::ResultAutoCommandResponseIndexError()); + R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_END_BIT, NO_ERROR)), sdmmc::ResultAutoCommandResponseEndBitError()); + R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_CRC, NO_ERROR)), sdmmc::ResultAutoCommandResponseCrcError()); + R_UNLESS(reg::HasValue(auto_cmd_err_status, SD_REG_BITS_ENUM(AUTO_CMD_ERROR_AUTO_CMD_TIMEOUT, NO_ERROR)), sdmmc::ResultAutoCommandResponseTimeoutError()); + + /* An known auto cmd error occurred. */ + return sdmmc::ResultSdHostStandardUnknownAutoCmdError(); + } else { + /* Unknown error occurred. */ + return sdmmc::ResultSdHostStandardUnknownError(); + } + } + + Result SdHostStandardController::WaitCommandComplete() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + /* Wait for interrupt. */ + Result result = this->WaitInterrupt(CommandTimeoutMilliSeconds); + if (R_SUCCEEDED(result)) { + /* If we succeeded, check/clear our interrupt status. */ + result = this->CheckAndClearInterruptStatus(nullptr, reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, COMPLETE))); + this->ClearInterrupt(); + + if (R_FAILED(result)) { + this->AbortTransaction(); + } + + return result; + } else if (sdmmc::ResultDeviceRemoved::Includes(result)) { + /* Otherwise, check if the device was removed. */ + return result; + } else { + /* If the device wasn't removed, cancel our transaction. */ + this->AbortTransaction(); + return sdmmc::ResultCommandCompleteSoftwareTimeout(); + } + } + #else + { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait while command is not complete. */ + { + ManualTimer timer(CommandTimeoutMilliSeconds); + while (true) { + /* Check and clear the interrupt status. */ + const auto result = this->CheckAndClearInterruptStatus(nullptr, reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, COMPLETE))); + + /* If we succeeded, we're done. */ + if (R_SUCCEEDED(result)) { + return ResultSuccess(); + } else if (sdmmc::ResultNoWaitedInterrupt::Includes(result)) { + /* Otherwise, if the wait for the interrupt isn't done, update the timer and check for timeout. */ + if (!timer.Update()) { + this->AbortTransaction(); + return sdmmc::ResultCommandCompleteSoftwareTimeout(); + } + } else { + /* Otherwise, we have a generic failure. */ + this->AbortTransaction(); + return result; + } + } + } + } + #endif + } + + Result SdHostStandardController::WaitTransferComplete() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + /* Wait while transfer is not complete. */ + while (true) { + /* Get the last block count. */ + const u16 last_block_count = reg::Read(this->registers->block_count); + + /* Wait for interrupt. */ + Result result = this->WaitInterrupt(this->check_transfer_interval_ms); + if (R_SUCCEEDED(result)) { + /* If we succeeded, check/clear our interrupt status. */ + volatile u16 normal_int_status; + result = this->CheckAndClearInterruptStatus(std::addressof(normal_int_status), reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE), + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))); + + this->ClearInterrupt(); + + /* If the interrupt succeeded, check status. */ + if (R_SUCCEEDED(result)) { + /* If the transfer is complete, we're done. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE))) { + return ResultSuccess(); + } + + /* Otherwise, if a DMA interrupt was generated, advance to the next address. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))) { + reg::Write(this->registers->adma_address, static_cast(this->next_sdma_address >> 0)); + reg::Write(this->registers->upper_adma_address, static_cast(this->next_sdma_address >> BITSIZEOF(u32))); + + this->next_sdma_address += SdmaBufferBoundary; + } + } else { + /* Abort the transaction. */ + this->AbortTransaction(); + return result; + } + + return result; + } else if (sdmmc::ResultDeviceRemoved::Includes(result)) { + /* Otherwise, check if the device was removed. */ + return result; + } else { + /* Otherwise, timeout if the transfer hasn't advanced. */ + if (last_block_count != reg::Read(this->registers->block_count)) { + this->AbortTransaction(); + return sdmmc::ResultTransferCompleteSoftwareTimeout(); + } + } + } + } + #else + { + /* Wait while transfer is not complete. */ + while (true) { + /* Get the last block count. */ + const u16 last_block_count = reg::Read(this->registers->block_count); + + /* Wait until transfer times out. */ + { + ManualTimer timer(this->check_transfer_interval_ms); + while (true) { + /* Check/clear our interrupt status. */ + volatile u16 normal_int_status; + const auto result = this->CheckAndClearInterruptStatus(std::addressof(normal_int_status), reg::Encode(SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE), + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))); + + /* If the check succeeded, check status. */ + if (R_SUCCEEDED(result)) { + /* If the transfer is complete, we're done. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, COMPLETE))) { + return ResultSuccess(); + } + + /* Otherwise, if a DMA interrupt was generated, advance to the next address. */ + if (reg::HasValue(normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, GENERATED))) { + reg::Write(this->registers->adma_address, static_cast(this->next_sdma_address >> 0)); + reg::Write(this->registers->upper_adma_address, static_cast(this->next_sdma_address >> BITSIZEOF(u32))); + + this->next_sdma_address += SdmaBufferBoundary; + } + } else if (sdmmc::ResultNoWaitedInterrupt::Includes(result)) { + /* Otherwise, if the wait for the interrupt isn't done, update the timer and check for timeout. */ + if (!timer.Update()) { + /* Only timeout if the transfer hasn't advanced. */ + if (last_block_count != reg::Read(this->registers->block_count)) { + this->AbortTransaction(); + return sdmmc::ResultTransferCompleteSoftwareTimeout(); + } + break; + } + } else { + /* Otherwise, we have a generic failure. */ + this->AbortTransaction(); + return result; + } + } + } + } + } + #endif + } + + Result SdHostStandardController::WaitWhileBusy() { + /* Ensure that we control the registers. */ + this->EnsureControl(); + + /* Wait while busy. */ + { + ManualTimer timer(BusyTimeoutMilliSeconds); + while (true) { + /* Check if the target has been removed. */ + R_TRY(this->CheckRemoved()); + + /* If the DAT0 line signal is level high, we're done. */ + if (reg::HasValue(this->registers->present_state, SD_REG_BITS_ENUM(PRESENT_STATE_DAT0_LINE_SIGNAL_LEVEL, HIGH))) { + return ResultSuccess(); + } + + /* Otherwise, check if we're timed out. */ + if (!timer.Update()) { + this->AbortTransaction(); + return sdmmc::ResultBusySoftwareTimeout(); + } + } + } + } + + void SdHostStandardController::GetResponse(u32 *out_response, size_t response_size, ResponseType response_type) const { + /* Check that we can write the response. */ + AMS_ABORT_UNLESS(out_response != nullptr); + + /* Get the response appropriately. */ + switch (response_type) { + case ResponseType_R1: + case ResponseType_R3: + case ResponseType_R6: + case ResponseType_R7: + /* 32-bit response. */ + AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 1); + out_response[0] = reg::Read(this->registers->response[0]); + break; + case ResponseType_R2: + /* 128-bit response. */ + AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 4); + out_response[0] = reg::Read(this->registers->response[0]); + out_response[1] = reg::Read(this->registers->response[1]); + out_response[2] = reg::Read(this->registers->response[2]); + out_response[3] = reg::Read(this->registers->response[3]); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + Result SdHostStandardController::IssueCommandWithDeviceClock(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { + /* Wait until we can issue the command. */ + R_TRY(this->WaitWhileCommandInhibit((xfer_data != nullptr) || command->is_busy)); + + /* Configure for the transfer. */ + u32 num_transferred_blocks = 0; + if (xfer_data != nullptr) { + /* Setup the transfer, and get the number of blocks. */ + this->SetTransfer(std::addressof(num_transferred_blocks), xfer_data); + + /* Ensure the device sees consistent data with the cpu. */ + dd::FlushDataCache(xfer_data->buffer, xfer_data->block_size * num_transferred_blocks); + } + + /* Issue the command with interrupt status enabled. */ + { + this->EnableInterruptStatus(); + ON_SCOPE_EXIT { this->DisableInterruptStatus(); }; + + /* Set the command. */ + this->SetCommand(command, xfer_data != nullptr); + + /* Wait for the command to complete. */ + R_TRY(this->WaitCommandComplete()); + + /* Process any response. */ + if (command->response_type != ResponseType_R0) { + this->last_response_type = command->response_type; + this->GetResponse(this->last_response, sizeof(this->last_response), this->last_response_type); + } + + /* Wait for data to be transferred. */ + if (xfer_data != nullptr) { + R_TRY(this->WaitTransferComplete()); + } + } + + /* If data was transferred, ensure we're in a consistent state. */ + if (xfer_data != nullptr) { + /* Ensure the cpu sees consistent data with the device. */ + if (xfer_data->transfer_direction == TransferDirection_ReadFromDevice) { + dd::InvalidateDataCache(xfer_data->buffer, xfer_data->block_size * num_transferred_blocks); + } + + /* Set the number of transferred blocks. */ + if (out_num_transferred_blocks != nullptr) { + *out_num_transferred_blocks = num_transferred_blocks; + } + + /* Process stop transition command. */ + this->last_stop_transmission_response = this->registers->response[3]; + } + + /* Wait until we're no longer busy. */ + if (command->is_busy || (xfer_data != nullptr)) { + R_TRY(this->WaitWhileBusy()); + } + + return ResultSuccess(); + } + + Result SdHostStandardController::IssueStopTransmissionCommandWithDeviceClock(u32 *out_response) { + /* Wait until we can issue the command. */ + R_TRY(this->WaitWhileCommandInhibit(false)); + + /* Issue the command with interrupt status enabled. */ + constexpr ResponseType StopTransmissionCommandResponseType = ResponseType_R1; + { + this->EnableInterruptStatus(); + ON_SCOPE_EXIT { this->DisableInterruptStatus(); }; + + /* Set the command. */ + Command command(CommandIndex_StopTransmission, 0, StopTransmissionCommandResponseType, true); + this->SetCommand(std::addressof(command), false); + + /* Wait for the command to complete. */ + R_TRY(this->WaitCommandComplete()); + } + + /* Process response. */ + this->GetResponse(out_response, sizeof(u32), StopTransmissionCommandResponseType); + + /* Wait until we're done. */ + R_TRY(this->WaitWhileBusy()); + + return ResultSuccess(); + } + + SdHostStandardController::SdHostStandardController(dd::PhysicalAddress registers_phys_addr, size_t registers_size) { + /* Translate the physical address to a address. */ + #if defined(ATMOSPHERE_IS_STRATOSPHERE) + const uintptr_t registers_addr = dd::QueryIoMapping(registers_phys_addr, registers_size); + #else + /* TODO: Discriminate between bpmp, exosphere address? */ + AMS_UNUSED(registers_size); + const uintptr_t registers_addr = static_cast(registers_phys_addr); + #endif + + /* Set registers. */ + AMS_ABORT_UNLESS(registers_addr != 0); + this->registers = reinterpret_cast(registers_addr); + + /* Reset DMA buffers, if we have any. */ + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + this->ResetBufferInfos(); + #endif + + /* Clear removed event, if we have one. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + this->removed_event = nullptr; + #endif + + /* Clear dma address. */ + this->next_sdma_address = 0; + this->check_transfer_interval_ms = DefaultCheckTransferIntervalMilliSeconds; + + /* Clear clock/power trackers. */ + this->device_clock_frequency_khz = 0; + this->is_power_saving_enable = false; + this->is_device_clock_enable = false; + + /* Clear last response. */ + this->last_response_type = ResponseType_R0; + std::memset(this->last_response, 0, sizeof(this->last_response)); + this->last_stop_transmission_response = 0; + } + + void SdHostStandardController::Initialize() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + os::InitializeWaitableManager(std::addressof(this->waitable_manager)); + + AMS_ABORT_UNLESS(this->interrupt_event != nullptr); + os::InitializeWaitableHolder(std::addressof(this->interrupt_event_holder), this->interrupt_event); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->interrupt_event_holder)); + + if (this->removed_event != nullptr) { + os::InitializeWaitableHolder(std::addressof(this->removed_event_holder), this->removed_event); + os::LinkWaitableHolder(std::addressof(this->waitable_manager), std::addressof(this->removed_event_holder)); + } + } + #endif + } + + void SdHostStandardController::Finalize() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + if (this->removed_event != nullptr) { + os::UnlinkWaitableHolder(std::addressof(this->removed_event_holder)); + os::FinalizeWaitableHolder(std::addressof(this->removed_event_holder)); + } + + os::UnlinkWaitableHolder(std::addressof(this->interrupt_event_holder)); + os::FinalizeWaitableHolder(std::addressof(this->interrupt_event_holder)); + + os::FinalizeWaitableManager(std::addressof(this->waitable_manager)); + } + #endif + } + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + void SdHostStandardController::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + /* Find and set a free info. */ + for (auto &info : this->buffer_infos) { + if (info.buffer_address == 0) { + info = { + .buffer_address = buffer, + .buffer_size = buffer_size, + .buffer_device_virtual_address = buffer_device_virtual_address, + }; + return; + } + } + + AMS_ABORT("Out of BufferInfos\n"); + } + + void SdHostStandardController::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + /* Find and clear the buffer info. */ + for (auto &info : this->buffer_infos) { + if (info.buffer_address == 0) { + AMS_ABORT_UNLESS(info.buffer_size == buffer_size); + AMS_ABORT_UNLESS(info.buffer_device_virtual_address == buffer_device_virtual_address); + + info.buffer_address = 0; + info.buffer_size = 0; + return; + } + } + + AMS_ABORT("BufferInfo not found\n"); + } + #endif + + void SdHostStandardController::SetWorkBuffer(void *wb, size_t wb_size) { + AMS_UNUSED(wb, wb_size); + AMS_ABORT("WorkBuffer is not needed\n"); + } + + BusPower SdHostStandardController::GetBusPower() const { + /* Check if the bus has power. */ + if (reg::HasValue(this->registers->power_control, SD_REG_BITS_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, ON))) { + /* If it does, return the corresponding power. */ + switch (reg::GetValue(this->registers->power_control, SD_REG_BITS_MASK(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1))) { + case SD_HOST_STANDARD_POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1_1_8V: + return BusPower_1_8V; + case SD_HOST_STANDARD_POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1_3_3V: + return BusPower_3_3V; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } else { + /* It doesn't, so it's off. */ + return BusPower_Off; + } + } + + void SdHostStandardController::SetBusWidth(BusWidth bus_width) { + /* Check that we support the bus width. */ + AMS_ABORT_UNLESS(this->IsSupportedBusWidth(bus_width)); + + /* Set the appropriate data transfer width. */ + switch (bus_width) { + case BusWidth_1Bit: + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, ONE_BIT)); + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, USE_DATA_TRANSFER_WIDTH)); + break; + case BusWidth_4Bit: + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, FOUR_BIT)); + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, USE_DATA_TRANSFER_WIDTH)); + break; + case BusWidth_8Bit: + reg::ReadWrite(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, EIGHT_BIT)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + BusWidth SdHostStandardController::GetBusWidth() const { + /* Check if the bus is using eight-bit extended data transfer. */ + if (reg::HasValue(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, EIGHT_BIT))) { + return BusWidth_8Bit; + } else { + /* Bus is configured as USE_DATA_TRANSFER_WIDTH, so check if it's four bit. */ + if (reg::HasValue(this->registers->host_control, SD_REG_BITS_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, FOUR_BIT))) { + return BusWidth_4Bit; + } else { + return BusWidth_1Bit; + } + } + } + + void SdHostStandardController::SetPowerSaving(bool en) { + /* Set whether we're power saving enable. */ + this->is_power_saving_enable = en; + + /* Configure accordingly. */ + if (this->is_power_saving_enable) { + /* We want to disable SD clock if it's enabled. */ + if (reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE))) { + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + } + } else { + /* We want to enable SD clock if it's disabled and we're supposed to enable device clock. */ + if (this->is_device_clock_enable && reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE))) { + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + } + } + + void SdHostStandardController::EnableDeviceClock() { + /* If we're not in power-saving mode and the device clock is disabled, enable it. */ + if (!this->is_power_saving_enable && reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE))) { + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + this->is_device_clock_enable = true; + } + + void SdHostStandardController::DisableDeviceClock() { + /* Unconditionally disable the device clock. */ + this->is_device_clock_enable = false; + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + } + + void SdHostStandardController::ChangeCheckTransferInterval(u32 ms) { + this->check_transfer_interval_ms = ms; + } + + void SdHostStandardController::SetDefaultCheckTransferInterval() { + this->check_transfer_interval_ms = DefaultCheckTransferIntervalMilliSeconds; + } + + Result SdHostStandardController::IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { + /* We need to have device clock enabled to issue commands. */ + AMS_ABORT_UNLESS(this->is_device_clock_enable); + + /* Check if we need to temporarily re-enable the device clock. */ + const bool clock_disabled = reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + + /* Ensure that the clock is enabled and the device is usable for the period we're using it. */ + if (clock_disabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* Ensure that our configuration takes. */ + this->EnsureControl(); + + /* Wait 8 device clocks to be sure that it's usable. */ + WaitClocks(8, this->device_clock_frequency_khz); + } + ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; + + /* Issue the command. */ + { + /* After we issue the command, we need to wait 8 device clocks. */ + ON_SCOPE_EXIT { WaitClocks(8, this->device_clock_frequency_khz); }; + + return this->IssueCommandWithDeviceClock(command, xfer_data, out_num_transferred_blocks); + } + } + + Result SdHostStandardController::IssueStopTransmissionCommand(u32 *out_response) { + /* We need to have device clock enabled to issue commands. */ + AMS_ABORT_UNLESS(this->is_device_clock_enable); + + /* Check if we need to temporarily re-enable the device clock. */ + const bool clock_disabled = reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + + /* Ensure that the clock is enabled and the device is usable for the period we're using it. */ + if (clock_disabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* Ensure that our configuration takes. */ + this->EnsureControl(); + + /* Wait 8 device clocks to be sure that it's usable. */ + WaitClocks(8, this->device_clock_frequency_khz); + } + ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; + + /* Issue the command. */ + { + /* After we issue the command, we need to wait 8 device clocks. */ + ON_SCOPE_EXIT { WaitClocks(8, this->device_clock_frequency_khz); }; + + return this->IssueStopTransmissionCommandWithDeviceClock(out_response); + } + } + + void SdHostStandardController::GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const { + /* Check that we can get the response. */ + AMS_ABORT_UNLESS(out_response != nullptr); + AMS_ABORT_UNLESS(response_type == this->last_response_type); + + /* Get the response appropriately. */ + switch (response_type) { + case ResponseType_R1: + case ResponseType_R3: + case ResponseType_R6: + case ResponseType_R7: + /* 32-bit response. */ + AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 1); + out_response[0] = this->last_response[0]; + break; + case ResponseType_R2: + /* 128-bit response. */ + AMS_ABORT_UNLESS(response_size >= sizeof(u32) * 4); + out_response[0] = this->last_response[0]; + out_response[1] = this->last_response[1]; + out_response[2] = this->last_response[2]; + out_response[3] = this->last_response[3]; + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + void SdHostStandardController::GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const { + /* Check that we can get the response. */ + AMS_ABORT_UNLESS(out_response != nullptr); + AMS_ABORT_UNLESS(response_size >= sizeof(u32)); + + /* Get the response. */ + out_response[0] = this->last_stop_transmission_response; + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.hpp new file mode 100644 index 000000000..7bdf0a3cd --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.hpp @@ -0,0 +1,188 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_i_host_controller.hpp" +#include "sdmmc_sd_host_standard_registers.hpp" + +namespace ams::sdmmc::impl { + + class SdHostStandardController : public IHostController { + protected: + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + struct BufferInfo { + uintptr_t buffer_address; + size_t buffer_size; + dd::DeviceVirtualAddress buffer_device_virtual_address; + }; + + static constexpr inline auto NumBufferInfos = 3; + #endif + protected: + SdHostStandardRegisters *registers; + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + BufferInfo buffer_infos[NumBufferInfos]; + #endif + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + os::WaitableManagerType waitable_manager; + os::InterruptEventType *interrupt_event; + os::WaitableHolderType interrupt_event_holder; + os::EventType *removed_event; + os::WaitableHolderType removed_event_holder; + #endif + + u64 next_sdma_address; + u32 check_transfer_interval_ms; + + u32 device_clock_frequency_khz; + bool is_power_saving_enable; + bool is_device_clock_enable; + + ResponseType last_response_type; + u32 last_response[4]; + u32 last_stop_transmission_response; + protected: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + void PreSetInterruptEvent(os::InterruptEventType *ie) { + this->interrupt_event = ie; + } + + bool IsRemoved() const { + return this->removed_event != nullptr && os::TryWaitEvent(this->removed_event); + } + #endif + + void SetDeviceClockFrequencyKHz(u32 khz) { + this->device_clock_frequency_khz = khz; + } + + #if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS) + void ResetBufferInfos(); + dd::DeviceVirtualAddress GetDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size); + #endif + + void EnsureControl(); + Result EnableInternalClock(); + void SetBusPower(BusPower bus_power); + + void EnableInterruptStatus(); + void DisableInterruptStatus(); + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + Result WaitInterrupt(u32 timeout_ms); + void ClearInterrupt(); + #endif + + void SetTransfer(u32 *out_num_transferred_blocks, const TransferData *xfer_data); + void SetTransferForTuning(); + + void SetCommand(const Command *command, bool has_xfer_data); + void SetCommandForTuning(u32 command_index); + + Result ResetCmdDatLine(); + Result AbortTransaction(); + Result WaitWhileCommandInhibit(bool has_dat); + Result CheckAndClearInterruptStatus(volatile u16 *out_normal_int_status, u16 wait_mask); + Result WaitCommandComplete(); + Result WaitTransferComplete(); + Result WaitWhileBusy(); + + void GetResponse(u32 *out_response, size_t response_size, ResponseType response_type) const; + + Result IssueCommandWithDeviceClock(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks); + Result IssueStopTransmissionCommandWithDeviceClock(u32 *out_response); + + ALWAYS_INLINE Result CheckRemoved() { + #if defined(AMS_SDMMC_USE_OS_EVENTS) + R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved()); + #endif + + return ResultSuccess(); + } + public: + SdHostStandardController(dd::PhysicalAddress registers_phys_addr, size_t registers_size); + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual void PreSetRemovedEvent(os::EventType *e) override { + this->removed_event = e; + } + #endif + + virtual void Initialize() override; + virtual void Finalize() override; + + #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 void SetWorkBuffer(void *wb, size_t wb_size) override; + + virtual Result SwitchToSdr12() override { + AMS_ABORT("SwitchToSdr12 not supported\n"); + } + + virtual BusPower GetBusPower() const override; + + virtual void SetBusWidth(BusWidth bus_width) override; + virtual BusWidth GetBusWidth() const override; + + virtual u32 GetDeviceClockFrequencyKHz() const override { + return this->device_clock_frequency_khz; + } + + virtual void SetPowerSaving(bool en) override; + virtual bool IsPowerSavingEnable() const override { + return this->is_power_saving_enable; + } + + virtual void EnableDeviceClock() override; + virtual void DisableDeviceClock() override; + virtual bool IsDeviceClockEnable() const override { + return this->is_device_clock_enable; + } + + virtual u32 GetMaxTransferNumBlocks() const override { + return SdHostStandardRegisters::BlockCountMax; + } + + virtual void ChangeCheckTransferInterval(u32 ms) override; + virtual void SetDefaultCheckTransferInterval() override; + + virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override; + virtual Result IssueStopTransmissionCommand(u32 *out_response) override; + + virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const override; + virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const override; + + virtual bool IsSupportedTuning() const override { + return false; + } + + virtual Result Tuning(SpeedMode speed_mode, u32 command_index) { + AMS_UNUSED(speed_mode, command_index); + AMS_ABORT("Tuning not supported\n"); + } + + virtual void SaveTuningStatusForHs400() { + AMS_ABORT("SaveTuningStatusForHs400 not supported\n"); + } + + }; + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_registers.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_registers.hpp new file mode 100644 index 000000000..e104374ed --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_registers.hpp @@ -0,0 +1,193 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::sdmmc::impl { + + struct SdHostStandardRegisters { + volatile uint32_t dma_address; + volatile uint16_t block_size; + volatile uint16_t block_count; + volatile uint32_t argument; + volatile uint16_t transfer_mode; + volatile uint16_t command; + volatile uint32_t response[0x4]; + volatile uint32_t buffer; + volatile uint32_t present_state; + volatile uint8_t host_control; + volatile uint8_t power_control; + volatile uint8_t block_gap_control; + volatile uint8_t wake_up_control; + volatile uint16_t clock_control; + volatile uint8_t timeout_control; + volatile uint8_t software_reset; + volatile uint16_t normal_int_status; + volatile uint16_t error_int_status; + volatile uint16_t normal_int_enable; + volatile uint16_t error_int_enable; + volatile uint16_t normal_signal_enable; + volatile uint16_t error_signal_enable; + volatile uint16_t acmd12_err; + volatile uint16_t host_control2; + volatile uint32_t capabilities; + volatile uint32_t capabilities_1; + volatile uint32_t max_current; + volatile uint32_t _0x4c; + volatile uint16_t set_acmd12_error; + volatile uint16_t set_int_error; + volatile uint8_t adma_error; + volatile uint8_t _0x56[0x3]; + volatile uint32_t adma_address; + volatile uint32_t upper_adma_address; + volatile uint16_t preset_for_init; + volatile uint16_t preset_for_default; + volatile uint16_t preset_for_high; + volatile uint16_t preset_for_sdr12; + volatile uint16_t preset_for_sdr25; + volatile uint16_t preset_for_sdr50; + volatile uint16_t preset_for_sdr104; + volatile uint16_t preset_for_ddr50; + volatile uint32_t _0x70[0x23]; + volatile uint16_t slot_int_status; + volatile uint16_t host_version; + + static constexpr inline u16 BlockCountMax = 0xFFFF; + }; + static_assert(sizeof(SdHostStandardRegisters) == 0x100); + + constexpr inline size_t SdmaBufferBoundary = 512_KB; + + #define SD_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SD_HOST_STANDARD, NAME) + #define SD_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SD_HOST_STANDARD, NAME, VALUE) + #define SD_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SD_HOST_STANDARD, NAME, ENUM) + #define SD_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SD_HOST_STANDARD, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + + #define DEFINE_SD_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SD_HOST_STANDARD, NAME, __OFFSET__, __WIDTH__) + #define DEFINE_SD_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE) + #define DEFINE_SD_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) + #define DEFINE_SD_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) + #define DEFINE_SD_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + + DEFINE_SD_REG(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, 0, 12); + DEFINE_SD_REG_THREE_BIT_ENUM(BLOCK_SIZE_SDMA_BUFFER_BOUNDARY, 12, 4_KB, 8_KB, 16_KB, 32_KB, 64_KB, 128_KB, 256_KB, 512_KB); + constexpr inline size_t SdHostStandardBlockSizeTransferBlockSizeMax = 0xFFF; + + DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DMA_ENABLE, 0, DISABLE, ENABLE); + DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_BLOCK_COUNT_ENABLE, 1, DISABLE, ENABLE); + DEFINE_SD_REG_TWO_BIT_ENUM(TRANSFER_MODE_AUTO_CMD_ENABLE, 2, DISABLE, CMD12_ENABLE, CMD23_ENABLE, AUTO_SELECT); + DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, 4, WRITE, READ); + DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_MULTI_BLOCK_SELECT, 5, SINGLE_BLOCK, MULTI_BLOCK); + + DEFINE_SD_REG_TWO_BIT_ENUM(COMMAND_RESPONSE_TYPE, 0, NO_RESPONSE, RESPONSE_LENGTH_136, RESPONSE_LENGTH_48, RESPONSE_LENGTH_48_CHECK_BUSY_AFTER_RESPONSE); + DEFINE_SD_REG_BIT_ENUM(COMMAND_CRC_CHECK, 3, DISABLE, ENABLE); + DEFINE_SD_REG_BIT_ENUM(COMMAND_INDEX_CHECK, 4, DISABLE, ENABLE); + DEFINE_SD_REG_BIT_ENUM(COMMAND_DATA_PRESENT, 5, NO_DATA_PRESENT, DATA_PRESENT); + DEFINE_SD_REG(COMMAND_COMMAND_INDEX, 8, 6); + constexpr inline size_t SdHostStandardCommandIndexMax = 0x3F; + + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_CMD, 0, READY, NOT_READY); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_DAT, 1, READY, NOT_READY); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT0_LINE_SIGNAL_LEVEL, 20, LOW, HIGH); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT1_LINE_SIGNAL_LEVEL, 21, LOW, HIGH); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT2_LINE_SIGNAL_LEVEL, 22, LOW, HIGH); + DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT3_LINE_SIGNAL_LEVEL, 23, LOW, HIGH); + DEFINE_SD_REG(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 20, 4); + + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, 1, ONE_BIT, FOUR_BIT); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, 2, NORMAL_SPEED, HIGH_SPEED); + DEFINE_SD_REG_TWO_BIT_ENUM(HOST_CONTROL_DMA_SELECT, 3, SDMA, RESERVED1, ADMA2, ADMA2_OR_ADMA3); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, 5, USE_DATA_TRANSFER_WIDTH, EIGHT_BIT); + + DEFINE_SD_REG_BIT_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, 0, OFF, ON); + DEFINE_SD_REG_THREE_BIT_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 1, RESERVED0, RESERVED1, RESERVED2, RESERVED3, RESERVED4, 1_8V, 3_0V, 3_3V); + + DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE, 0, STOP, OSCILLATE); + DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_STABLE, 1, NOT_READY, READY); + DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, 2, DISABLE, ENABLE); + DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_CLOCK_GENERATOR_SELECT, 5, DIVIDED_CLOCK, PROGRAMMABLE_CLOCK); + DEFINE_SD_REG(CLOCK_CONTROL_UPPER_BITS_OF_SDCLK_FREQUENCY_SELECT, 6, 2); + DEFINE_SD_REG(CLOCK_CONTROL_SDCLK_FREQUENCY_SELECT, 8, 8); + + DEFINE_SD_REG(TIMEOUT_CONTROL_DATA_TIMEOUT_COUNTER, 0, 4); + + DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_ALL, 0, WORK, RESET); + DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_CMD, 1, WORK, RESET); + DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_DAT, 2, WORK, RESET); + + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, 0, NOT_COMPLETE, COMPLETE); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, 1, NOT_COMPLETE, COMPLETE); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, 3, NOT_GENERATED, GENERATED); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_ERROR_INTERRUPT, 15, NO_ERROR, ERROR); + + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_TIMEOUT, 0, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_CRC, 1, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_END_BIT, 2, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_INDEX, 3, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_TIMEOUT, 4, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_CRC, 5, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_END_BIT, 6, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_AUTO_CMD, 8, NO_ERROR, ERROR); + + DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_TIMEOUT, 1, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_CRC, 2, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_END_BIT, 3, NO_ERROR, ERROR); + DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_INDEX, 4, NO_ERROR, ERROR); + + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, 0, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, 1, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, 3, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, 5, MASKED, ENABLED); + + #define SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \ + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, __ENUM__), \ + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, __ENUM__), \ + SD_REG_BITS_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, __ENUM__) + + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, 0, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, 1, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, 2, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, 3, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, 4, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, 5, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, 6, MASKED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, 8, MASKED, ENABLED); + + #define SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, __ENUM__), \ + SD_REG_BITS_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, __ENUM__) + + + DEFINE_SD_REG_THREE_BIT_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, 0, SDR12, SDR25, SDR50, SDR104, DDR50, HS400, RSVD6, UHS_II); + + constexpr inline auto SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_HS200 = SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_SDR104; + + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3, 3_3V_SIGNALING, 1_8V_SIGNALING); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_EXECUTE_TUNING, 6, TUNING_COMPLETED, EXECUTE_TUNING); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_SAMPLING_CLOCK, 7, USING_FIXED_CLOCK, USING_TUNED_CLOCK); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_HOST_VERSION_4_ENABLE, 12, VERSION_300_COMPATIBLE, VERSION_4); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_64_BIT_ADDRESSING, 13, 32_BIT_ADDRESSING, 64_BIT_ADDRESSING); + DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_PRESET_VALUE_ENABLE, 15, HOST_DRIVER, AUTOMATIC_SELECTION); + + DEFINE_SD_REG_BIT_ENUM(CAPABILITIES_64_BIT_SYSTEM_ADDRESS_SUPPORT_FOR_V3, 28, NOT_SUPPORTED, SUPPORTED); + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp new file mode 100644 index 000000000..d69d498e1 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp @@ -0,0 +1,1304 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" +#include "sdmmc_io_impl.board.nintendo_nx.hpp" +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl { + + /* FOR REFERENCE: board-specific sdmmc registers. */ + //struct SdmmcRegisters { + // /* Standard registers. */ + // volatile SdHostStandardRegisters sd_host_standard_registers; + // + // /* Vendor specific registers */ + // volatile uint32_t vendor_clock_cntrl; + // volatile uint32_t vendor_sys_sw_cntrl; + // volatile uint32_t vendor_err_intr_status; + // volatile uint32_t vendor_cap_overrides; + // volatile uint32_t vendor_boot_cntrl; + // volatile uint32_t vendor_boot_ack_timeout; + // volatile uint32_t vendor_boot_dat_timeout; + // volatile uint32_t vendor_debounce_count; + // volatile uint32_t vendor_misc_cntrl; + // volatile uint32_t max_current_override; + // volatile uint32_t max_current_override_hi; + // volatile uint32_t _0x12c[0x20]; + // volatile uint32_t vendor_io_trim_cntrl; + // + // /* Start of sdmmc2/sdmmc4 only */ + // volatile uint32_t vendor_dllcal_cfg; + // volatile uint32_t vendor_dll_ctrl0; + // volatile uint32_t vendor_dll_ctrl1; + // volatile uint32_t vendor_dllcal_cfg_sta; + // /* End of sdmmc2/sdmmc4 only */ + // + // volatile uint32_t vendor_tuning_cntrl0; + // volatile uint32_t vendor_tuning_cntrl1; + // volatile uint32_t vendor_tuning_status0; + // volatile uint32_t vendor_tuning_status1; + // volatile uint32_t vendor_clk_gate_hysteresis_count; + // volatile uint32_t vendor_preset_val0; + // volatile uint32_t vendor_preset_val1; + // volatile uint32_t vendor_preset_val2; + // volatile uint32_t sdmemcomppadctrl; + // volatile uint32_t auto_cal_config; + // volatile uint32_t auto_cal_interval; + // volatile uint32_t auto_cal_status; + // volatile uint32_t io_spare; + // volatile uint32_t sdmmca_mccif_fifoctrl; + // volatile uint32_t timeout_wcoal_sdmmca; + // volatile uint32_t _0x1fc; + //}; + + DEFINE_SD_REG_BIT_ENUM(VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE, 2, NORMAL, OVERRIDE); + DEFINE_SD_REG(VENDOR_CLOCK_CNTRL_TAP_VAL, 16, 8); + DEFINE_SD_REG(VENDOR_CLOCK_CNTRL_TRIM_VAL, 24, 5); + + DEFINE_SD_REG(VENDOR_CAP_OVERRIDES_DQS_TRIM_VAL, 8, 6); + + DEFINE_SD_REG(VENDOR_IO_TRIM_CNTRL_SEL_VREG, 2, 1); + + DEFINE_SD_REG_BIT_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, 31, DISABLE, ENABLE); + + DEFINE_SD_REG_BIT_ENUM(VENDOR_DLLCAL_CFG_STA_DLL_CAL_ACTIVE, 31, DONE, RUNNING); + + DEFINE_SD_REG(VENDOR_TUNING_CNTRL0_MUL_M, 6, 7); + DEFINE_SD_REG_THREE_BIT_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, 13, TRIES_40, TRIES_64, TRIES_128, TRIES_192, TRIES_256, RESERVED5, RESERVED6, RESERVED7); + DEFINE_SD_REG_BIT_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, 17, NOT_UPDATED_BY_HW, UPDATED_BY_HW); + + DEFINE_SD_REG(SDMEMCOMPPADCTRL_SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL, 0, 4); + DEFINE_SD_REG(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 31, 1); + + DEFINE_SD_REG(AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET, 0, 7); + DEFINE_SD_REG(AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET, 8, 7); + DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, 29, DISABLED, ENABLED); + DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, 31, DISABLED, ENABLED); + + DEFINE_SD_REG(AUTO_CAL_STATUS_AUTO_CAL_PULLUP, 0, 7); + DEFINE_SD_REG_BIT_ENUM(AUTO_CAL_STATUS_AUTO_CAL_ACTIVE, 31, INACTIVE, ACTIVE); + + DEFINE_SD_REG_BIT_ENUM(IO_SPARE_SPARE_OUT_3, 19, TWO_CYCLE_DELAY, ONE_CYCLE_DELAY); + + namespace { + + constexpr inline u32 TuningCommandTimeoutMilliSeconds = 5; + + constexpr void GetDividerSetting(u32 *out_target_clock_frequency_khz, u16 *out_x, SpeedMode speed_mode) { + switch (speed_mode) { + case SpeedMode_MmcIdentification: + *out_target_clock_frequency_khz = 26000; + *out_x = 66; + break; + case SpeedMode_MmcLegacySpeed: + *out_target_clock_frequency_khz = 26000; + *out_x = 1; + break; + case SpeedMode_MmcHighSpeed: + *out_target_clock_frequency_khz = 52000; + *out_x = 1; + break; + case SpeedMode_MmcHs200: + *out_target_clock_frequency_khz = 200000; + *out_x = 1; + break; + case SpeedMode_MmcHs400: + *out_target_clock_frequency_khz = 200000; + *out_x = 1; + break; + case SpeedMode_SdCardIdentification: + *out_target_clock_frequency_khz = 25000; + *out_x = 64; + break; + case SpeedMode_SdCardDefaultSpeed: + *out_target_clock_frequency_khz = 25000; + *out_x = 1; + break; + case SpeedMode_SdCardHighSpeed: + *out_target_clock_frequency_khz = 50000; + *out_x = 1; + break; + case SpeedMode_SdCardSdr12: + *out_target_clock_frequency_khz = 25000; + *out_x = 1; + break; + case SpeedMode_SdCardSdr50: + *out_target_clock_frequency_khz = 100000; + *out_x = 1; + break; + case SpeedMode_SdCardSdr104: + *out_target_clock_frequency_khz = 200000; + *out_x = 1; + break; + case SpeedMode_GcAsicFpgaSpeed: + *out_target_clock_frequency_khz = 40800; + *out_x = 1; + break; + case SpeedMode_GcAsicSpeed: + *out_target_clock_frequency_khz = 200000; + *out_x = 2; + break; + case SpeedMode_SdCardSdr25: + case SpeedMode_SdCardDdr50: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + } + + namespace { + + #if defined(AMS_SDMMC_THREAD_SAFE) + constinit os::SdkMutex g_soc_mutex; + + #define AMS_SDMMC_LOCK_SOC_MUTEX() std::scoped_lock lk(g_soc_mutex) + + #else + + #define AMS_SDMMC_LOCK_SOC_MUTEX() + + #endif + + constinit bool g_determined_soc = false; + constinit bool g_is_soc_mariko = false; + + } + + bool IsSocMariko() { + if (!g_determined_soc) { + /* Ensure we have exclusive access to the soc variables. */ + AMS_SDMMC_LOCK_SOC_MUTEX(); + + /* Check the SocType. */ + #if defined(ATMOSPHERE_IS_EXOSPHERE) + { + g_is_soc_mariko = fuse::GetSocType() == fuse::SocType_Mariko; + } + #elif defined(ATMOSPHERE_IS_MESOSPHERE) + { + MESOSPHERE_TODO("Detect mariko via KSystemControl call?"); + } + #elif defined(ATMOSPHERE_IS_STRATOSPHERE) + { + /* Connect to spl for the duration of our check. */ + spl::Initialize(); + ON_SCOPE_EXIT { spl::Finalize(); }; + + g_is_soc_mariko = spl::GetSocType() == spl::SocType_Mariko; + } + #else + #error "Unknown execution context for ams::sdmmc::impl::IsSocMariko" + #endif + + /* Note that we determined the soc. */ + g_determined_soc = true; + } + + return g_is_soc_mariko; + } + + void SdmmcController::ReleaseReset(SpeedMode speed_mode) { + /* Get the clock reset module. */ + const auto module = this->GetClockResetModule(); + + /* If the module is available, disable clock. */ + if (ClockResetController::IsAvailable(module)) { + SdHostStandardController::DisableDeviceClock(); + SdHostStandardController::EnsureControl(); + } + + /* Get the correct divider setting for the speed mode. */ + u32 target_clock_frequency_khz; + u16 x; + GetDividerSetting(std::addressof(target_clock_frequency_khz), std::addressof(x), speed_mode); + + /* Release reset. */ + ClockResetController::ReleaseReset(module, target_clock_frequency_khz); + } + + void SdmmcController::AssertReset() { + return ClockResetController::AssertReset(this->GetClockResetModule()); + } + + Result SdmmcController::StartupCore(BusPower bus_power) { + /* Set schmitt trigger. */ + this->SetSchmittTrigger(bus_power); + + /* Select one-cycle delay version of cmd_oen. */ + reg::ReadWrite(this->sdmmc_registers->io_spare, SD_REG_BITS_ENUM(IO_SPARE_SPARE_OUT_3, ONE_CYCLE_DELAY)); + + /* Select regulated reference voltage for trimmer and DLL supply. */ + reg::ReadWrite(this->sdmmc_registers->vendor_io_trim_cntrl, SD_REG_BITS_VALUE(VENDOR_IO_TRIM_CNTRL_SEL_VREG, 0)); + + /* Configure outbound tap value. */ + reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_VALUE(VENDOR_CLOCK_CNTRL_TRIM_VAL, this->GetOutboundTapValue())); + + /* Configure SPI_MODE_CLKEN_OVERRIDE. */ + reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_ENUM(VENDOR_CLOCK_CNTRL_SPI_MODE_CLKEN_OVERRIDE, NORMAL)); + + /* Set slew codes. */ + this->SetSlewCodes(); + + /* Set vref sel. */ + reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_SDMMC2TMC_CFG_SDMEMCOMP_VREF_SEL, this->GetVrefSelValue())); + + /* Perform drive strength calibration at the new power. */ + this->SetDriveCodeOffsets(bus_power); + this->CalibrateDriveStrength(bus_power); + + /* Enable internal clock. */ + R_TRY(SdHostStandardController::EnableInternalClock()); + + return ResultSuccess(); + } + + Result SdmmcController::SetClockTrimmer(SpeedMode speed_mode, u8 tap_value) { + /* If speed mode is Hs400, set the dqs trim value. */ + if (speed_mode == SpeedMode_MmcHs400) { + reg::ReadWrite(this->sdmmc_registers->vendor_cap_overrides, SD_REG_BITS_VALUE(VENDOR_CAP_OVERRIDES_DQS_TRIM_VAL, 40)); + } + + /* Configure tap value as updated by software. */ + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, NOT_UPDATED_BY_HW)); + + /* Set the inbound tap value. */ + reg::ReadWrite(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_VALUE(VENDOR_CLOCK_CNTRL_TAP_VAL, tap_value)); + + /* Reset the cmd/dat line. */ + R_TRY(SdHostStandardController::ResetCmdDatLine()); + + return ResultSuccess(); + } + + u8 SdmmcController::GetCurrentTapValue() { + return static_cast(reg::GetValue(this->sdmmc_registers->vendor_clock_cntrl, SD_REG_BITS_MASK(VENDOR_CLOCK_CNTRL_TAP_VAL))); + } + + Result SdmmcController::CalibrateDll() { + /* Check if we need to temporarily re-enable the device clock. */ + const bool clock_disabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + + /* Ensure that the clock is enabled for the period we're using it. */ + if (clock_disabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + ON_SCOPE_EXIT { if (clock_disabled) { reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); } }; + + /* Begin calibration. */ + reg::ReadWrite(this->sdmmc_registers->vendor_dllcal_cfg, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, ENABLE)); + + /* Wait up to 5ms for calibration to begin. */ + { + ManualTimer timer(5); + while (true) { + /* If calibration is done, we're done. */ + if (!reg::HasValue(this->sdmmc_registers->vendor_dllcal_cfg, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_CALIBRATE, ENABLE))) { + break; + } + + /* Otherwise, check if we've timed out. */ + R_UNLESS((timer.Update()), sdmmc::ResultSdmmcDllCalibrationSoftwareTimeout()); + } + } + + /* Wait up to 10ms for calibration to complete. */ + { + ManualTimer timer(10); + while (true) { + /* If calibration is done, we're done. */ + if (reg::HasValue(this->sdmmc_registers->vendor_dllcal_cfg_sta, SD_REG_BITS_ENUM(VENDOR_DLLCAL_CFG_STA_DLL_CAL_ACTIVE, DONE))) { + break; + } + + /* Otherwise, check if we've timed out. */ + R_UNLESS((timer.Update()), sdmmc::ResultSdmmcDllApplicationSoftwareTimeout()); + } + } + + return ResultSuccess(); + } + + Result SdmmcController::SetSpeedModeWithTapValue(SpeedMode speed_mode, u8 tap_value) { + /* Check if we need to temporarily disable the device clock. */ + const bool clock_enabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* Ensure that the clock is disabled for the period we're using it. */ + if (clock_enabled) { + /* Turn off the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + } + + /* Set clock trimmer. */ + /* NOTE: Nintendo does not re-enable the clock if this fails... */ + R_TRY(this->SetClockTrimmer(speed_mode, tap_value)); + + /* Configure for the desired speed mode. */ + switch (speed_mode) { + case SpeedMode_MmcIdentification: + case SpeedMode_SdCardIdentification: + case SpeedMode_MmcLegacySpeed: + case SpeedMode_SdCardDefaultSpeed: + /* Set as normal speed, 3.3V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control, SD_REG_BITS_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, NORMAL_SPEED)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3_3V_SIGNALING)); + break; + case SpeedMode_MmcHighSpeed: + case SpeedMode_SdCardHighSpeed: + /* Set as high speed, 3.3V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control, SD_REG_BITS_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, HIGH_SPEED)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3_3V_SIGNALING)); + break; + case SpeedMode_MmcHs200: + /* Set as HS200, 1.8V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, HS200)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); + break; + case SpeedMode_MmcHs400: + /* Set as HS400, 1.8V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, HS400)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); + break; + case SpeedMode_SdCardSdr12: + /* Set as SDR12, 1.8V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, SDR12)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); + break; + case SpeedMode_SdCardSdr50: + case SpeedMode_SdCardSdr104: + case SpeedMode_GcAsicFpgaSpeed: + case SpeedMode_GcAsicSpeed: + /* Set as SDR104, 1.8V. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, SDR104)); + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + SdHostStandardController::EnsureControl(); + + /* Get the divider setting. */ + u32 target_source_clock_frequency_khz; + u16 x; + GetDividerSetting(std::addressof(target_source_clock_frequency_khz), std::addressof(x), speed_mode); + + /* Set the clock frequency. */ + u32 actual_source_clock_frequency_khz; + ClockResetController::SetClockFrequencyKHz(std::addressof(actual_source_clock_frequency_khz), this->GetClockResetModule(), target_source_clock_frequency_khz); + + /* Set the device clock frequency. */ + const u32 actual_device_clock_frequency_khz = util::DivideUp(actual_source_clock_frequency_khz, x); + SdHostStandardController::SetDeviceClockFrequencyKHz(actual_device_clock_frequency_khz); + + /* Check that the divider is correct. */ + AMS_ABORT_UNLESS((x == 1) || util::IsAligned(x, 2)); + + /* Write the divider val to clock control. */ + const u16 n = x / 2; + const u16 upper_n = n >> 8; + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_VALUE(CLOCK_CONTROL_SDCLK_FREQUENCY_SELECT, n), + SD_REG_BITS_VALUE(CLOCK_CONTROL_UPPER_BITS_OF_SDCLK_FREQUENCY_SELECT, upper_n)); + + /* Re-enable the clock, if we should. */ + if (clock_enabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + + /* If speed mode is Hs400, calibrate dll. */ + if (speed_mode == SpeedMode_MmcHs400) { + R_TRY(this->CalibrateDll()); + } + + /* Set the current speed mode. */ + this->current_speed_mode = speed_mode; + + return ResultSuccess(); + } + + Result SdmmcController::IssueTuningCommand(u32 command_index) { + /* Check that we're not power saving enable. */ + AMS_ABORT_UNLESS(!SdHostStandardController::IsPowerSavingEnable()); + + /* Wait until command inhibit is done. */ + R_TRY(SdHostStandardController::WaitWhileCommandInhibit(true)); + + /* Set transfer for tuning. */ + SdHostStandardController::SetTransferForTuning(); + + /* If necessary, clear interrupt and enable buffer read ready signal. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + this->ClearInterrupt(); + + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_signal_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); + } + #endif + + /* Set the buffer read ready enable, and read status to ensure it takes. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_int_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); + reg::Read(this->sdmmc_registers->sd_host_standard_registers.normal_int_status); + + /* Issue command with clock disabled. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + { + SdHostStandardController::SetCommandForTuning(command_index); + + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(1); + SdHostStandardController::AbortTransaction(); + } + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* When we're done waiting, ensure that we clean up appropriately. */ + ON_SCOPE_EXIT { + /* Clear the buffer read ready signal, if we should. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_signal_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, MASKED)); + #endif + + /* Clear the buffer read ready enable. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.normal_int_enable, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, MASKED)); + + /* Wait 8 clocks to ensure configuration takes. */ + SdHostStandardController::EnsureControl(); + WaitClocks(8, SdHostStandardController::GetDeviceClockFrequencyKHz()); + }; + + /* Wait for the command to finish. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + const auto result = SdHostStandardController::WaitInterrupt(TuningCommandTimeoutMilliSeconds); + if (R_SUCCEEDED(result)) { + /* If we succeeded, clear the interrupt. */ + reg::Write(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); + this->ClearInterrupt(); + return ResultSuccess(); + } else if (sdmmc::ResultWaitInterruptSoftwareTimeout::Includes(result)) { + SdHostStandardController::AbortTransaction(); + return sdmmc::ResultIssueTuningCommandSoftwareTimeout(); + } else { + return result; + } + } + #else + { + SdHostStandardController::EnsureControl(); + ManualTimer timer(TuningCommandTimeoutMilliSeconds); + while (true) { + /* Check if we received the interrupt. */ + if (reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED))) { + /* If we did, acknowledge it. */ + reg::Write(this->sdmmc_registers->sd_host_standard_registers.normal_int_status, SD_REG_BITS_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, ENABLED)); + return ResultSuccess(); + } + + /* Otherwise, check if we timed out. */ + if (!timer.Update()) { + SdHostStandardController::AbortTransaction(); + return sdmmc::ResultIssueTuningCommandSoftwareTimeout(); + } + } + } + #endif + } + + void SdmmcController::SetDriveCodeOffsets(BusPower bus_power) { + /* Get the offsets. */ + u8 pd, pu; + this->GetAutoCalOffsets(std::addressof(pd), std::addressof(pu), bus_power); + + /* Set the offsets. */ + reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_VALUE(AUTO_CAL_CONFIG_AUTO_CAL_PD_OFFSET, pd), + SD_REG_BITS_VALUE(AUTO_CAL_CONFIG_AUTO_CAL_PU_OFFSET, pu)); + } + + void SdmmcController::CalibrateDriveStrength(BusPower bus_power) { + /* Reset drive strength calibration status. */ + this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationNotCompleted(); + + /* Check if we need to temporarily disable the device clock. */ + const bool clock_enabled = reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + + /* Ensure that the clock is disabled for the period we're using it. */ + if (clock_enabled) { + /* Turn off the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + } + + /* Calibrate with the clock disabled. */ + { + /* Set SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD. */ + if (reg::HasValue(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 0))) { + reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 1)); + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(1); + } + + /* Calibrate with SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD set. */ + { + /* Begin autocal. */ + reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_START, ENABLED), + SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, ENABLED)); + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(2); + + /* Wait up to 10ms for auto cal to complete. */ + ManualTimer timer(10); + while (true) { + /* Check if auto cal is inactive. */ + if (reg::HasValue(this->sdmmc_registers->auto_cal_status, SD_REG_BITS_ENUM(AUTO_CAL_STATUS_AUTO_CAL_ACTIVE, INACTIVE))) { + /* Check the pullup status. */ + const u32 pullup = (reg::GetValue(this->sdmmc_registers->auto_cal_status, SD_REG_BITS_MASK(AUTO_CAL_STATUS_AUTO_CAL_PULLUP))) & 0x1F; + if (pullup == 0x1F) { + this->drive_strength_calibration_status = sdmmc::ResultSdmmcCompShortToGnd(); + } + if (pullup == 0) { + this->drive_strength_calibration_status = sdmmc::ResultSdmmcCompOpen(); + } + + break; + } + + /* Otherwise, check if we've timed out. */ + if (!timer.Update()) { + this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationSoftwareTimeout(); + + this->SetDriveStrengthToDefaultValues(bus_power); + reg::ReadWrite(this->sdmmc_registers->auto_cal_config, SD_REG_BITS_ENUM(AUTO_CAL_CONFIG_AUTO_CAL_ENABLE, DISABLED)); + break; + } + } + } + + /* Clear SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD. */ + reg::ReadWrite(this->sdmmc_registers->sdmemcomppadctrl, SD_REG_BITS_VALUE(SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD, 0)); + } + + /* Re-enable the clock, if we should. */ + if (clock_enabled) { + /* Turn on the clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, ENABLE)); + } + + /* If calibration didn't receive a replacement error, set internal state to success. */ + if (sdmmc::ResultDriveStrengthCalibrationNotCompleted::Includes(this->drive_strength_calibration_status)) { + this->drive_strength_calibration_status = ResultSuccess(); + } + } + + Result SdmmcController::Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) { + /* Verify that we're awake. */ + AMS_ABORT_UNLESS(this->is_awake); + + /* Release the controller from reset. */ + this->ReleaseReset(speed_mode); + + /* Mark that we're not shutdown. */ + this->is_shutdown = false; + + /* Power on the controller. */ + R_TRY(this->PowerOn(bus_power)); + + /* Start up for the specific power. */ + R_TRY(this->StartupCore(bus_power)); + + /* Set our current power/width/speed. */ + SdHostStandardController::SetBusWidth(bus_width); + SdHostStandardController::SetBusPower(bus_power); + R_TRY(this->SetSpeedMode(speed_mode)); + this->SetPowerSaving(power_saving_enable); + + /* Enable clock to the device. */ + SdHostStandardController::EnableDeviceClock(); + + /* Ensure that we can control the device. */ + SdHostStandardController::EnsureControl(); + + return ResultSuccess(); + } + + void SdmmcController::Shutdown() { + /* If we're already shut down, there's nothing to do. */ + if (this->is_shutdown) { + return; + } + + /* If we're currently awake, we need to disable clock/power. */ + if (this->is_awake) { + SdHostStandardController::DisableDeviceClock(); + SdHostStandardController::SetBusPower(BusPower_Off); + SdHostStandardController::EnsureControl(); + } + + /* Power off. */ + this->PowerOff(); + + /* If awake, assert reset. */ + if (this->is_awake) { + this->AssertReset(); + } + + /* Mark that we're shutdown. */ + this->is_shutdown = true; + } + + void SdmmcController::PutToSleep() { + /* If we're already shut down or asleep, there's nothing to do. */ + if (this->is_shutdown || !this->is_awake) { + return; + } + + /* Save values before sleep. */ + this->bus_power_before_sleep = SdHostStandardController::GetBusPower(); + this->bus_width_before_sleep = SdHostStandardController::GetBusWidth(); + this->speed_mode_before_sleep = this->current_speed_mode; + this->tap_value_before_sleep = this->GetCurrentTapValue(); + this->is_powersaving_enable_before_sleep = SdHostStandardController::IsPowerSavingEnable(); + + /* Disable clock/power to the device. */ + SdHostStandardController::DisableDeviceClock(); + SdHostStandardController::SetBusPower(BusPower_Off); + SdHostStandardController::EnsureControl(); + + /* Assert reset. */ + this->AssertReset(); + + /* Mark that we're asleep. */ + this->is_awake = false; + } + + Result SdmmcController::Awaken() { + /* If we're shut down, or if we're awake already, there's nothing to do. */ + R_SUCCEED_IF(this->is_shutdown); + R_SUCCEED_IF(this->is_awake); + + /* Mark that we're awake. */ + this->is_awake = true; + + /* Clear pad parked status. */ + this->ClearPadParked(); + + /* Release reset. */ + this->ReleaseReset(this->speed_mode_before_sleep); + + /* Start up for the correct power. */ + R_TRY(this->StartupCore(this->bus_power_before_sleep)); + + /* Configure values to what they were before sleep. */ + SdHostStandardController::SetBusWidth(this->bus_width_before_sleep); + SdHostStandardController::SetBusPower(this->bus_power_before_sleep); + R_TRY(this->SetSpeedModeWithTapValue(this->speed_mode_before_sleep, this->tap_value_before_sleep)); + this->SetPowerSaving(this->is_powersaving_enable_before_sleep); + + /* Enable clock to the device. */ + SdHostStandardController::EnableDeviceClock(); + SdHostStandardController::EnsureControl(); + + return ResultSuccess(); + } + + Result SdmmcController::SwitchToSdr12() { + /* Disable clock. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + + /* Check that the dat lines are all low. */ + R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.present_state, SD_REG_BITS_VALUE(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 0b0000)), sdmmc::ResultSdCardNotReadyToVoltageSwitch()); + + /* Set voltage to 1.8V. */ + SdHostStandardController::EnsureControl(); + R_TRY(this->LowerBusPower()); + this->SetSchmittTrigger(BusPower_1_8V); + + /* Perform drive strength calibration at the new power. */ + this->SetDriveCodeOffsets(BusPower_1_8V); + this->CalibrateDriveStrength(BusPower_1_8V); + + /* Set the bus power in standard controller. */ + SdHostStandardController::SetBusPower(BusPower_1_8V); + + /* Wait up to 5ms for the switch to take. */ + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(5000); + + /* Check that we switched to 1.8V. */ + R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 1_8V_SIGNALING)), sdmmc::ResultSdHostStandardFailSwitchTo1_8V()); + + /* Enable clock, and wait 1ms. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + SdHostStandardController::EnsureControl(); + WaitMicroSeconds(1000); + + /* Check that the dat lines are all high. */ + R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.present_state, SD_REG_BITS_VALUE(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 0b1111)), sdmmc::ResultSdCardNotCompleteVoltageSwitch()); + + return ResultSuccess(); + } + + Result SdmmcController::SetSpeedMode(SpeedMode speed_mode) { + /* Get the tap value. */ + u8 tap_value; + if (speed_mode == SpeedMode_MmcHs400) { + AMS_ABORT_UNLESS(this->is_valid_tap_value_for_hs_400); + tap_value = this->tap_value_for_hs_400; + } else { + tap_value = this->GetDefaultInboundTapValue(); + } + + /* Set the speed mode. */ + R_TRY(this->SetSpeedModeWithTapValue(speed_mode, tap_value)); + + return ResultSuccess(); + } + + void SdmmcController::SetPowerSaving(bool en) { + /* If necessary, calibrate the drive strength. */ + if (this->IsNeedPeriodicDriveStrengthCalibration() && !en && SdHostStandardController::IsDeviceClockEnable()) { + this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); + } + + return SdHostStandardController::SetPowerSaving(en); + } + + void SdmmcController::EnableDeviceClock() { + /* If necessary, calibrate the drive strength. */ + if (this->IsNeedPeriodicDriveStrengthCalibration() && !SdHostStandardController::IsPowerSavingEnable()) { + this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); + } + + return SdHostStandardController::EnableDeviceClock(); + } + + Result SdmmcController::IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) { + /* If necessary, calibrate the drive strength. */ + if (this->IsNeedPeriodicDriveStrengthCalibration() && SdHostStandardController::IsPowerSavingEnable()) { + this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); + } + + return SdHostStandardController::IssueCommand(command, xfer_data, out_num_transferred_blocks); + } + + Result SdmmcController::IssueStopTransmissionCommand(u32 *out_response) { + /* If necessary, calibrate the drive strength. */ + if (this->IsNeedPeriodicDriveStrengthCalibration() && SdHostStandardController::IsPowerSavingEnable()) { + this->CalibrateDriveStrength(SdHostStandardController::GetBusPower()); + } + + return SdHostStandardController::IssueStopTransmissionCommand(out_response); + } + + Result SdmmcController::Tuning(SpeedMode speed_mode, u32 command_index) { + /* Clear vendor tuning control 1. */ + reg::Write(this->sdmmc_registers->vendor_tuning_cntrl1, 0); + + /* Determine/configure the number of tries. */ + int num_tries; + switch (speed_mode) { + case SpeedMode_MmcHs200: + case SpeedMode_MmcHs400: + case SpeedMode_SdCardSdr104: + num_tries = 128; + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, TRIES_128)); + break; + case SpeedMode_SdCardSdr50: + case SpeedMode_GcAsicFpgaSpeed: + case SpeedMode_GcAsicSpeed: + num_tries = 256; + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_NUM_TUNING_ITERATIONS, TRIES_256)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Configure the multiplier. */ + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_VALUE(VENDOR_TUNING_CNTRL0_MUL_M, 1)); + + /* Configure tap value to be updated by hardware. */ + reg::ReadWrite(this->sdmmc_registers->vendor_tuning_cntrl0, SD_REG_BITS_ENUM(VENDOR_TUNING_CNTRL0_TAP_VALUE_UPDATED_BY_HW, UPDATED_BY_HW)); + + /* Configure to execute tuning. */ + reg::ReadWrite(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_EXECUTE_TUNING, EXECUTE_TUNING)); + + /* Perform tuning num_tries times. */ + for (int i = 0; /* ... */; ++i) { + /* Check if we've been removed. */ + R_TRY(this->CheckRemoved()); + + /* Issue the command. */ + this->IssueTuningCommand(command_index); + + /* Check if tuning is done. */ + if (i >= num_tries) { + break; + } + ++i; + + if (reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_EXECUTE_TUNING, TUNING_COMPLETED))) { + break; + } + } + + /* Check if we're using the tuned clock. */ + R_UNLESS(reg::HasValue(this->sdmmc_registers->sd_host_standard_registers.host_control2, SD_REG_BITS_ENUM(HOST_CONTROL2_SAMPLING_CLOCK, USING_TUNED_CLOCK)), sdmmc::ResultTuningFailed()); + + return ResultSuccess(); + } + + void SdmmcController::SaveTuningStatusForHs400() { + /* Save the current tap value. */ + this->tap_value_for_hs_400 = GetCurrentTapValue(); + this->is_valid_tap_value_for_hs_400 = true; + } + + Result Sdmmc1Controller::PowerOnForRegisterControl(BusPower bus_power) { + AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); + + /* Nintendo sets the current bus power regardless of whether the call succeeds. */ + ON_SCOPE_EXIT { this->current_bus_power = BusPower_3_3V; }; + + /* pcv::PowerOn(pcv::PowerControlTarget_SdCard, 3300000); */ + R_TRY(this->power_controller->PowerOn(BusPower_3_3V)); + + return ResultSuccess(); + } + + void Sdmmc1Controller::PowerOffForRegisterControl() { + /* If we're already off, there's nothing to do. */ + if (this->current_bus_power == BusPower_Off) { + return; + } + + /* If we're at 3.3V, lower to 1.8V. */ + { + /* pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + this->power_controller->LowerBusPower(); + + /* Set our bus power. */ + this->current_bus_power = BusPower_1_8V; + } + + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1OutputHigh); + + + /* pcv::PowerOff(pcv::PowerControlTarget_SdCard); */ + this->power_controller->PowerOff(); + + /* Set our bus power. */ + this->current_bus_power = BusPower_Off; + + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1ResetState); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1ResetState); + } + + Result Sdmmc1Controller::LowerBusPowerForRegisterControl() { + /* Nintendo sets the current bus power regardless of whether the call succeeds. */ + ON_SCOPE_EXIT { this->current_bus_power = BusPower_1_8V; }; + + /* pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + R_TRY(this->power_controller->LowerBusPower()); + + return ResultSuccess(); + } + + void Sdmmc1Controller::SetSchmittTriggerForRegisterControl(BusPower bus_power) { + SdHostStandardController::EnsureControl(); + + if (IsSocMariko()) { + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtEnable); + } else { + switch (bus_power) { + case BusPower_1_8V: + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtEnable); + break; + case BusPower_3_3V: + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtDisable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtDisable); + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + Result Sdmmc1Controller::PowerOnForPcvControl(BusPower bus_power) { + AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); + + /* Nintendo sets the current bus power regardless of whether the call succeeds. */ + ON_SCOPE_EXIT { this->current_bus_power = BusPower_3_3V; }; + + /* TODO: return pcv::PowerOn(pcv::PowerControlTarget_SdCard, 3300000); */ + return ResultSuccess(); + } + + void Sdmmc1Controller::PowerOffForPcvControl() { + /* If we're already off, there's nothing to do. */ + if (this->current_bus_power == BusPower_Off) { + return; + } + + /* If we're at 3.3V, lower to 1.8V. */ + { + /* TODO: pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + this->current_bus_power = BusPower_1_8V; + } + + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */ + + /* TODO: pcv::PowerOff(pcv::PowerControlTarget_SdCard); */ + this->current_bus_power = BusPower_Off; + + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1ResetState); */ + + } + + Result Sdmmc1Controller::LowerBusPowerForPcvControl() { + /* Nintendo sets the current bus power regardless of whether the call succeeds. */ + ON_SCOPE_EXIT { this->current_bus_power = BusPower_1_8V; }; + + /* TODO: return pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + return ResultSuccess(); + } + + void Sdmmc1Controller::SetSchmittTriggerForPcvControl(BusPower bus_power) { + SdHostStandardController::EnsureControl(); + + if (IsSocMariko()) { + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + } else { + switch (bus_power) { + case BusPower_1_8V: + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + break; + case BusPower_3_3V: + /* TODO: pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtDisable); */ + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } + #endif + + Result Sdmmc1Controller::PowerOn(BusPower bus_power) { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->PowerOnForPcvControl(bus_power); + } else + #endif + { + return this->PowerOnForRegisterControl(bus_power); + } + } + + void Sdmmc1Controller::PowerOff() { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->PowerOffForPcvControl(); + } else + #endif + { + return this->PowerOffForRegisterControl(); + } + } + + Result Sdmmc1Controller::LowerBusPower() { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->LowerBusPowerForPcvControl(); + } else + #endif + { + return this->LowerBusPowerForRegisterControl(); + } + } + + void Sdmmc1Controller::SetSchmittTrigger(BusPower bus_power) { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->SetSchmittTriggerForPcvControl(bus_power); + } else + #endif + { + return this->SetSchmittTriggerForRegisterControl(bus_power); + } + } + + void Sdmmc1Controller::Initialize() { + return this->InitializeForRegisterControl(); + } + + void Sdmmc1Controller::Finalize() { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + if (this->is_pcv_control) { + return this->FinalizeForPcvControl(); + } else + #endif + { + return this->FinalizeForRegisterControl(); + } + } + + void Sdmmc1Controller::InitializeForRegisterControl() { + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + /* Mark ourselves as initialized by register control. */ + this->is_pcv_control = false; + #endif + + /* pinmux::Initialize(); */ + /* This just opens a session handle to pinmux service, no work to do. */ + + /* pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */ + /* This just sets the session's internal value to the pin group name, so nothing to do here either. */ + + /* pcv::Initialize(); */ + /* This initializes a lot of globals in pcv, most of which we don't care about. */ + /* However, we do care about the Sdmmc1PowerController. */ + AMS_ABORT_UNLESS(this->power_controller == nullptr); + this->power_controller = new (GetPointer(this->power_controller_storage)) PowerController; + + /* Perform base initialization. */ + SdmmcController::Initialize(); + } + + void Sdmmc1Controller::FinalizeForRegisterControl() { + /* Perform base finalization. */ + SdmmcController::Finalize(); + + /* pcv::Finalize(); */ + /* As with initialize, we mostly don't care about the globals this touches. */ + /* However, we do want to finalize the Sdmmc1PowerController. */ + AMS_ABORT_UNLESS(this->power_controller != nullptr); + this->power_controller->~PowerController(); + this->power_controller = nullptr; + + /* pinmux::CloseSession(std::addressof(this->pinmux_session)); */ + /* This does nothing. */ + + /* pinmux::Finalize(); */ + /* This does nothing. */ + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + /* Mark ourselves as initialized by register control. */ + this->is_pcv_control = false; + #endif + } + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + void Sdmmc1Controller::InitializeForPcvControl() { + /* Mark ourselves as initialized by pcv control. */ + this->is_pcv_control = true; + + /* TODO: pinmux::Initialize(); */ + /* TODO: pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */ + /* TODO: pcv::Initialize(); */ + + /* Perform base initialization. */ + SdmmcController::Initialize(); + } + + void Sdmmc1Controller::FinalizeForPcvControl() { + /* Perform base finalization. */ + SdmmcController::Finalize(); + + /* TODO: pcv::Finalize(); */ + /* TODO: pinmux::CloseSession(std::addressof(this->pinmux_session)); */ + /* TODO: pinmux::Finalize(); */ + + /* Mark ourselves as initialized by register control. */ + this->is_pcv_control = false; + } + #endif + + namespace { + + constexpr inline dd::PhysicalAddress PmcRegistersPhysicalAddress = 0x7000E400; + + } + + Result Sdmmc1Controller::PowerController::ControlVddioSdmmc1(BusPower bus_power) { + /* Configure appropriate voltage. */ + switch (bus_power) { + case BusPower_Off: + R_TRY(SetSdCardVoltageEnabled(false)); + break; + case BusPower_1_8V: + R_TRY(SetSdCardVoltageValue(1'800'000)); + R_TRY(SetSdCardVoltageEnabled(true)); + break; + case BusPower_3_3V: + R_TRY(SetSdCardVoltageValue(3'300'000)); + R_TRY(SetSdCardVoltageEnabled(true)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + void Sdmmc1Controller::PowerController::SetSdmmcIoMode(bool is_3_3V) { + /* Determine the address we're updating. */ + constexpr dd::PhysicalAddress ApbdevPmcPwrDetValAddress = PmcRegistersPhysicalAddress + APBDEV_PMC_PWR_DET_VAL; + + /* Read the current value. */ + u32 value = dd::ReadIoRegister(ApbdevPmcPwrDetValAddress); + + /* Mask out the existing bits. */ + value &= ~(reg::EncodeMask(PMC_REG_BITS_MASK(PWR_DET_VAL_SDMMC1))); + + /* ORR in the new bits. */ + value |= reg::Encode(PMC_REG_BITS_ENUM_SEL(PWR_DET_VAL_SDMMC1, is_3_3V, ENABLE, DISABLE)); + + /* Write the new value. */ + dd::WriteIoRegister(ApbdevPmcPwrDetValAddress, value); + + /* Read the value back to be sure our write takes. */ + dd::ReadIoRegister(ApbdevPmcPwrDetValAddress); + } + + void Sdmmc1Controller::PowerController::ControlRailSdmmc1Io(bool is_power_on) { + /* Determine the address we're updating. */ + constexpr dd::PhysicalAddress ApbdevPmcNoIoPowerAddress = PmcRegistersPhysicalAddress + APBDEV_PMC_NO_IOPOWER; + + /* Read the current value. */ + u32 value = dd::ReadIoRegister(ApbdevPmcNoIoPowerAddress); + + /* Mask out the existing bits. */ + value &= ~(reg::EncodeMask(PMC_REG_BITS_MASK(NO_IOPOWER_SDMMC1))); + + /* ORR in the new bits. */ + value |= reg::Encode(PMC_REG_BITS_ENUM_SEL(NO_IOPOWER_SDMMC1, is_power_on, DISABLE, ENABLE)); + + /* Write the new value. */ + dd::WriteIoRegister(ApbdevPmcNoIoPowerAddress, value); + + /* Read the value back to be sure our write takes. */ + dd::ReadIoRegister(ApbdevPmcNoIoPowerAddress); + } + + Sdmmc1Controller::PowerController::PowerController() : current_bus_power(BusPower_Off) { + /* gpio::Initialize(); */ + /* ... */ + + /* Open gpio session. */ + /* gpio::OpenSession(std::addressof(this->gpio_pad_session), gpio::GpioPadName_PowSdEn); */ + gpio_impl::OpenSession(gpio_impl::GpioPadName_PowSdEn); + + /* Configure the gpio as low/output. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_Low); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_Low); + + /* gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Output); */ + gpio_impl::SetDirection(gpio_impl::GpioPadName_PowSdEn, gpio_impl::Direction_Output); + } + + Sdmmc1Controller::PowerController::~PowerController() { + /* gpio::CloseSession(std::addressof(this->gpio_pad_session)); */ + gpio_impl::CloseSession(gpio_impl::GpioPadName_PowSdEn); + + /* gpio::Finalize(); */ + /* ... */ + } + + Result Sdmmc1Controller::PowerController::PowerOn(BusPower bus_power) { + /* Bus power should be off, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power == BusPower_Off); + R_SUCCEED_IF(this->current_bus_power != BusPower_Off); + + /* Power on requires the target bus power be 3.3V. */ + AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); + + /* Enable the rail. */ + this->ControlRailSdmmc1Io(true); + + /* Set the SD power GPIO to high. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_High); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_High); + + /* Wait 10ms for power change to take. */ + WaitMicroSeconds(10000); + + /* Configure Sdmmc1 IO as 3.3V. */ + this->SetSdmmcIoMode(true); + R_TRY(this->ControlVddioSdmmc1(BusPower_3_3V)); + + /* Wait 130 us for changes to take. */ + WaitMicroSeconds(130); + + /* Update our current bus power. */ + this->current_bus_power = bus_power; + + return ResultSuccess(); + } + + Result Sdmmc1Controller::PowerController::PowerOff() { + /* Bus power should be on, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power != BusPower_Off); + R_SUCCEED_IF(this->current_bus_power == BusPower_Off); + + /* Bus power should be 1.8V. */ + /* NOTE: the result returned here is 0x8C0 (regulator::ResultIllegalRequest()) on newer firmwares. */ + AMS_ASSERT(this->current_bus_power == BusPower_1_8V); + R_UNLESS(this->current_bus_power == BusPower_1_8V, pcv::ResultIllegalRequest()); + + /* Disable vddio, and wait 4 ms. */ + this->ControlVddioSdmmc1(BusPower_Off); + WaitMicroSeconds(4000); + + /* Set the SD power GPIO to low. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_Low); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_Low); + + /* Wait 239ms for the gpio config to take. */ + WaitMicroSeconds(239000); + + /* Disable the rail. */ + this->ControlRailSdmmc1Io(false); + this->SetSdmmcIoMode(true); + + /* Update our current bus power. */ + this->current_bus_power = BusPower_Off; + + return ResultSuccess(); + } + + Result Sdmmc1Controller::PowerController::LowerBusPower() { + /* Bus power should be 3.3V, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power == BusPower_3_3V); + R_SUCCEED_IF(this->current_bus_power != BusPower_3_3V); + + /* Configure as 1.8V, then wait 150us for it to take. */ + R_TRY(this->ControlVddioSdmmc1(BusPower_1_8V)); + WaitMicroSeconds(150); + this->SetSdmmcIoMode(false); + + /* Update our current bus power. */ + this->current_bus_power = BusPower_1_8V; + + return ResultSuccess(); + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp new file mode 100644 index 000000000..4c5fba691 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp @@ -0,0 +1,682 @@ +/* + * 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 . + */ +#pragma once +#include +#include "sdmmc_sd_host_standard_controller.hpp" +#include "sdmmc_clock_reset_controller.hpp" + +namespace ams::sdmmc::impl { + + bool IsSocMariko(); + + constexpr inline size_t SdmmcRegistersSize = 0x200; + + constexpr inline dd::PhysicalAddress ApbMiscRegistersPhysicalAddress = UINT64_C(0x70000000); + constexpr inline size_t ApbMiscRegistersSize = 16_KB; + + class SdmmcController : public SdHostStandardController { + private: + struct SdmmcRegisters { + /* Standard registers. */ + volatile SdHostStandardRegisters sd_host_standard_registers; + + /* Vendor specific registers */ + volatile uint32_t vendor_clock_cntrl; + volatile uint32_t vendor_sys_sw_cntrl; + volatile uint32_t vendor_err_intr_status; + volatile uint32_t vendor_cap_overrides; + volatile uint32_t vendor_boot_cntrl; + volatile uint32_t vendor_boot_ack_timeout; + volatile uint32_t vendor_boot_dat_timeout; + volatile uint32_t vendor_debounce_count; + volatile uint32_t vendor_misc_cntrl; + volatile uint32_t max_current_override; + volatile uint32_t max_current_override_hi; + volatile uint32_t _0x12c[0x20]; + volatile uint32_t vendor_io_trim_cntrl; + + /* Start of sdmmc2/sdmmc4 only */ + volatile uint32_t vendor_dllcal_cfg; + volatile uint32_t vendor_dll_ctrl0; + volatile uint32_t vendor_dll_ctrl1; + volatile uint32_t vendor_dllcal_cfg_sta; + /* End of sdmmc2/sdmmc4 only */ + + volatile uint32_t vendor_tuning_cntrl0; + volatile uint32_t vendor_tuning_cntrl1; + volatile uint32_t vendor_tuning_status0; + volatile uint32_t vendor_tuning_status1; + volatile uint32_t vendor_clk_gate_hysteresis_count; + volatile uint32_t vendor_preset_val0; + volatile uint32_t vendor_preset_val1; + volatile uint32_t vendor_preset_val2; + volatile uint32_t sdmemcomppadctrl; + volatile uint32_t auto_cal_config; + volatile uint32_t auto_cal_interval; + volatile uint32_t auto_cal_status; + volatile uint32_t io_spare; + volatile uint32_t sdmmca_mccif_fifoctrl; + volatile uint32_t timeout_wcoal_sdmmca; + volatile uint32_t _0x1fc; + }; + static_assert(sizeof(SdmmcRegisters) == SdmmcRegistersSize); + private: + SdmmcRegisters *sdmmc_registers; + bool is_shutdown; + bool is_awake; + SpeedMode current_speed_mode; + BusPower bus_power_before_sleep; + BusWidth bus_width_before_sleep; + SpeedMode speed_mode_before_sleep; + u8 tap_value_before_sleep; + bool is_powersaving_enable_before_sleep; + u8 tap_value_for_hs_400; + bool is_valid_tap_value_for_hs_400; + Result drive_strength_calibration_status; + private: + void ReleaseReset(SpeedMode speed_mode); + void AssertReset(); + Result StartupCore(BusPower bus_power); + Result SetClockTrimmer(SpeedMode speed_mode, u8 tap_value); + u8 GetCurrentTapValue(); + Result CalibrateDll(); + Result SetSpeedModeWithTapValue(SpeedMode speed_mode, u8 tap_value); + Result IssueTuningCommand(u32 command_index); + protected: + void SetDriveCodeOffsets(BusPower bus_power); + void CalibrateDriveStrength(BusPower bus_power); + + virtual void SetPad() = 0; + + virtual ClockResetController::Module GetClockResetModule() const = 0; + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual int GetInterruptNumber() const = 0; + virtual os::InterruptEventType *GetInterruptEvent() const = 0; + #endif + + virtual bool IsNeedPeriodicDriveStrengthCalibration() = 0; + virtual void ClearPadParked() = 0; + virtual Result PowerOn(BusPower bus_power) = 0; + virtual void PowerOff() = 0; + virtual Result LowerBusPower() = 0; + virtual void SetSchmittTrigger(BusPower bus_power) = 0; + virtual u8 GetOutboundTapValue() const = 0; + virtual u8 GetDefaultInboundTapValue() const = 0; + virtual u8 GetVrefSelValue() const = 0; + virtual void SetSlewCodes() = 0; + virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const = 0; + virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) = 0; + public: + explicit SdmmcController(dd::PhysicalAddress registers_phys_addr) : SdHostStandardController(registers_phys_addr, SdmmcRegistersSize) { + /* Set sdmmc registers. */ + static_assert(offsetof(SdmmcRegisters, sd_host_standard_registers) == 0); + this->sdmmc_registers = reinterpret_cast(this->registers); + + this->is_shutdown = true; + this->is_awake = true; + this->is_valid_tap_value_for_hs_400 = false; + this->drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationNotCompleted(); + this->tap_value_for_hs_400 = 0; + this->current_speed_mode = SpeedMode_MmcIdentification; + this->bus_power_before_sleep = BusPower_Off; + this->bus_width_before_sleep = BusWidth_1Bit; + this->speed_mode_before_sleep = SpeedMode_MmcIdentification; + this->tap_value_before_sleep = 0; + this->is_powersaving_enable_before_sleep = false; + } + + virtual void Initialize() override { + /* Set pad. */ + this->SetPad(); + + /* Initialize our clock/reset module. */ + ClockResetController::Initialize(this->GetClockResetModule()); + + /* If necessary, initialize our interrupt event. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + os::InterruptEventType *interrupt_event = this->GetInterruptEvent(); + os::InitializeInterruptEvent(interrupt_event, this->GetInterruptNumber(), os::EventClearMode_ManualClear); + SdHostStandardController::PreSetInterruptEvent(interrupt_event); + } + #endif + + /* Perform base initialization. */ + SdHostStandardController::Initialize(); + } + + virtual void Finalize() override { + /* Perform base finalization. */ + SdHostStandardController::Finalize(); + + /* If necessary, finalize our interrupt event. */ + #if defined(AMS_SDMMC_USE_OS_EVENTS) + { + os::FinalizeInterruptEvent(this->GetInterruptEvent()); + } + #endif + + /* Finalize our clock/reset module. */ + ClockResetController::Finalize(this->GetClockResetModule()); + } + + virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) override; + virtual void Shutdown() override; + virtual void PutToSleep() override; + virtual Result Awaken() override; + virtual Result SwitchToSdr12() override; + virtual Result SetSpeedMode(SpeedMode speed_mode) override; + + virtual SpeedMode GetSpeedMode() const override { + return this->current_speed_mode; + } + + virtual void SetPowerSaving(bool en) override; + virtual void EnableDeviceClock() override; + + virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override; + virtual Result IssueStopTransmissionCommand(u32 *out_response) override; + + virtual bool IsSupportedTuning() const override { + return true; + } + + virtual Result Tuning(SpeedMode speed_mode, u32 command_index) override; + virtual void SaveTuningStatusForHs400() override; + + virtual Result GetInternalStatus() const override { + return this->drive_strength_calibration_status; + } + }; + + constexpr inline dd::PhysicalAddress Sdmmc1RegistersPhysicalAddress = UINT64_C(0x700B0000); + + class Sdmmc1Controller : public SdmmcController { + private: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + static constinit inline os::InterruptEventType s_interrupt_event{}; + #endif + + /* NOTE: This is a fascimile of pcv's Sdmmc1PowerController. */ + class PowerController { + NON_COPYABLE(PowerController); + NON_MOVEABLE(PowerController); + private: + BusPower current_bus_power; + private: + Result ControlVddioSdmmc1(BusPower bus_power); + void SetSdmmcIoMode(bool is_3_3V); + void ControlRailSdmmc1Io(bool is_power_on); + public: + PowerController(); + ~PowerController(); + + Result PowerOn(BusPower bus_power); + Result PowerOff(); + Result LowerBusPower(); + }; + private: + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + /* TODO: pinmux::PinmuxSession pinmux_session; */ + #endif + BusPower current_bus_power; + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + bool is_pcv_control; + #endif + TYPED_STORAGE(PowerController) power_controller_storage; + PowerController *power_controller; + private: + Result PowerOnForRegisterControl(BusPower bus_power); + void PowerOffForRegisterControl(); + Result LowerBusPowerForRegisterControl(); + void SetSchmittTriggerForRegisterControl(BusPower bus_power); + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + Result PowerOnForPcvControl(BusPower bus_power); + void PowerOffForPcvControl(); + Result LowerBusPowerForPcvControl(); + void SetSchmittTriggerForPcvControl(BusPower bus_power); + #endif + protected: + virtual void SetPad() override { + /* Nothing is needed here. */ + } + + virtual ClockResetController::Module GetClockResetModule() const override { + return ClockResetController::Module_Sdmmc1; + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual int GetInterruptNumber() const override { + return 46; + } + + virtual os::InterruptEventType *GetInterruptEvent() const override { + return std::addressof(s_interrupt_event); + } + #endif + + virtual bool IsNeedPeriodicDriveStrengthCalibration() override { + return !IsSocMariko(); + } + + virtual void ClearPadParked() override { + /* Nothing is needed here. */ + } + + virtual Result PowerOn(BusPower bus_power) override; + virtual void PowerOff() override; + virtual Result LowerBusPower() override; + + virtual void SetSchmittTrigger(BusPower bus_power) override; + + virtual u8 GetOutboundTapValue() const override { + if (IsSocMariko()) { + return 0xE; + } else { + return 0x2; + } + } + + virtual u8 GetDefaultInboundTapValue() const override { + if (IsSocMariko()) { + return 0xB; + } else { + return 0x4; + } + } + + virtual u8 GetVrefSelValue() const override { + if (IsSocMariko()) { + return 0x0; + } else { + return 0x7; + } + } + + virtual void SetSlewCodes() override { + if (IsSocMariko()) { + /* Do nothing. */ + } else { + /* Ensure that we can control registers. */ + SdHostStandardController::EnsureControl(); + + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Write the slew values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWR, 0x1), + APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWF, 0x1)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL); + } + } + + virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override { + /* Ensure that we can write the offsets. */ + AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr); + AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr); + + /* Set the offsets. */ + if (IsSocMariko()) { + switch (bus_power) { + case BusPower_1_8V: + *out_auto_cal_pd_offset = 6; + *out_auto_cal_pu_offset = 6; + break; + case BusPower_3_3V: + *out_auto_cal_pd_offset = 0; + *out_auto_cal_pu_offset = 0; + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } else { + switch (bus_power) { + case BusPower_1_8V: + *out_auto_cal_pd_offset = 0x7B; + *out_auto_cal_pu_offset = 0x7B; + break; + case BusPower_3_3V: + *out_auto_cal_pd_offset = 0x7D; + *out_auto_cal_pu_offset = 0; + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + } + + virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { + /* Ensure that we can control registers. */ + SdHostStandardController::EnsureControl(); + + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Determine the drive code values. */ + u8 drvdn, drvup; + if (IsSocMariko()) { + drvdn = 0x8; + drvup = 0x8; + } else { + switch (bus_power) { + case BusPower_1_8V: + drvdn = 0xF; + drvup = 0xB; + break; + case BusPower_3_3V: + drvdn = 0xC; + drvup = 0xC; + break; + case BusPower_Off: + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + /* Write the drv up/down values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVDN, drvdn), + APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVUP, drvup)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); + } + public: + Sdmmc1Controller() : SdmmcController(Sdmmc1RegistersPhysicalAddress) { + this->current_bus_power = BusPower_Off; + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + this->is_pcv_control = false; + #endif + this->power_controller = nullptr; + } + + virtual void Initialize() override; + virtual void Finalize() override; + + void InitializeForRegisterControl(); + void FinalizeForRegisterControl(); + + #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + void InitializeForPcvControl(); + void FinalizeForPcvControl(); + #endif + + virtual bool IsSupportedBusPower(BusPower bus_power) const override { + switch (bus_power) { + case BusPower_Off: return true; + case BusPower_1_8V: return true; + case BusPower_3_3V: return true; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + virtual bool IsSupportedBusWidth(BusWidth bus_width) const override { + switch (bus_width) { + case BusWidth_1Bit: return true; + case BusWidth_4Bit: return true; + case BusWidth_8Bit: return false; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + }; + + class Sdmmc2And4Controller : public SdmmcController { + protected: + virtual bool IsNeedPeriodicDriveStrengthCalibration() override { + return false; + } + + virtual Result PowerOn(BusPower bus_power) override { + /* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */ + AMS_UNUSED(bus_power); + return ResultSuccess(); + } + + virtual void PowerOff() override { + /* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */ + } + + virtual Result LowerBusPower() override { + AMS_ABORT("Sdmmc2And4Controller cannot lower bus power\n"); + } + + virtual void SetSchmittTrigger(BusPower bus_power) override { + /* Do nothing. */ + AMS_UNUSED(bus_power); + } + + virtual u8 GetOutboundTapValue() const override { + if (IsSocMariko()) { + return 0xD; + } else { + return 0x8; + } + } + + virtual u8 GetDefaultInboundTapValue() const override { + if (IsSocMariko()) { + return 0xB; + } else { + return 0x0; + } + } + + virtual u8 GetVrefSelValue() const override { + return 0x7; + } + + virtual void SetSlewCodes() override { + /* Do nothing. */ + } + + virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override { + /* Ensure that we can write the offsets. */ + AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr); + AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr); + + /* Sdmmc2And4Controller only supports 1.8v. */ + AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); + + /* Set the offsets. */ + *out_auto_cal_pd_offset = 5; + *out_auto_cal_pu_offset = 5; + } + public: + explicit Sdmmc2And4Controller(dd::PhysicalAddress registers_phys_addr) : SdmmcController(registers_phys_addr) { + /* ... */ + } + + virtual bool IsSupportedBusPower(BusPower bus_power) const override { + switch (bus_power) { + case BusPower_Off: return true; + case BusPower_1_8V: return true; + case BusPower_3_3V: return false; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + virtual bool IsSupportedBusWidth(BusWidth bus_width) const override { + switch (bus_width) { + case BusWidth_1Bit: return true; + case BusWidth_4Bit: return true; + case BusWidth_8Bit: return true; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + }; + + constexpr inline dd::PhysicalAddress Sdmmc2RegistersPhysicalAddress = UINT64_C(0x700B0200); + + class Sdmmc2Controller : public Sdmmc2And4Controller { + private: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + static constinit inline os::InterruptEventType s_interrupt_event{}; + #endif + protected: + virtual void SetPad() override { + /* Nothing is needed here. */ + } + + virtual ClockResetController::Module GetClockResetModule() const override { + return ClockResetController::Module_Sdmmc2; + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual int GetInterruptNumber() const override { + return 47; + } + + virtual os::InterruptEventType *GetInterruptEvent() const override { + return std::addressof(s_interrupt_event); + } + #endif + + virtual void ClearPadParked() override { + if (IsSocMariko()) { + /* Nothing is needed here. */ + } else { + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Clear all MISC2PMC_EMMC2_*_PARK bits. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_MISC2PMC_EMMC2_ALL_PARK, 0)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL); + } + } + + virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { + /* SDMMC2 only supports 1.8v. */ + AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); + + /* Ensure that we can control registers. */ + SdHostStandardController::EnsureControl(); + + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + if (IsSocMariko()) { + /* Write the drv up/down values to APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVDN, 0xA), + APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVUP, 0xA)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL); + } else { + /* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVDN_COMP, 0x10), + APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVUP_COMP, 0x10)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL); + } + } + public: + Sdmmc2Controller() : Sdmmc2And4Controller(Sdmmc2RegistersPhysicalAddress) { + /* ... */ + } + }; + + constexpr inline dd::PhysicalAddress Sdmmc4RegistersPhysicalAddress = UINT64_C(0x700B0600); + + class Sdmmc4Controller : public Sdmmc2And4Controller { + private: + #if defined(AMS_SDMMC_USE_OS_EVENTS) + static constinit inline os::InterruptEventType s_interrupt_event{}; + #endif + protected: + virtual void SetPad() override { + if (IsSocMariko()) { + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Enable Schmitt Trigger in emmc4 iobrick. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_ENUM(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_E_SCH, ENABLE)); + + /* Clear CMD_PULLU, CLK_PULLD, DQS_PULLD. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CMD_PUPD_PULLU, 0), + APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CLK_PUPD_PULLD, 0), + APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DQS_PUPD_PULLD, 0)); + + /* Read again to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL); + } else { + /* On Erista, we can just leave the reset value intact. */ + } + } + + virtual ClockResetController::Module GetClockResetModule() const override { + return ClockResetController::Module_Sdmmc4; + } + + #if defined(AMS_SDMMC_USE_OS_EVENTS) + virtual int GetInterruptNumber() const override { + return 63; + } + + virtual os::InterruptEventType *GetInterruptEvent() const override { + return std::addressof(s_interrupt_event); + } + #endif + + virtual void ClearPadParked() override { + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Clear all MISC2PMC_EMMC4_*_PARK bits. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_MISC2PMC_EMMC4_ALL_PARK, 0)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); + } + + virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override { + /* SDMMC4 only supports 1.8v. */ + AMS_ABORT_UNLESS(bus_power == BusPower_1_8V); + + /* Ensure that we can control registers. */ + SdHostStandardController::EnsureControl(); + + /* Get the apb registers address. */ + const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize); + + /* Determine the drv up/down values. */ + u8 drvdn, drvup; + if (IsSocMariko()) { + drvdn = 0xA; + drvup = 0xA; + } else { + drvdn = 0x10; + drvup = 0x10; + } + + /* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */ + reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVDN_COMP, drvdn), + APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVUP_COMP, drvup)); + + /* Read to be sure our config takes. */ + reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL); + } + public: + Sdmmc4Controller() : Sdmmc2And4Controller(Sdmmc4RegistersPhysicalAddress) { + /* ... */ + } + }; + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp new file mode 100644 index 000000000..9d3b9432a --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp @@ -0,0 +1,33 @@ +/* + * 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 . + */ +#pragma once +#include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) + + #include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" + + namespace ams::sdmmc::impl { + + using SdmmcControllerForPortSdCard0 = Sdmmc1Controller; + using SdmmcControllerForPortGcAsic0 = Sdmmc2Controller; + using SdmmcControllerForPortMmc0 = Sdmmc4Controller; + + } + +#else + #error "Unknown board for ams::sdmmc::SdmmcController" +#endif diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.cpp new file mode 100644 index 000000000..ae9033202 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.cpp @@ -0,0 +1,90 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_timer.hpp" + +namespace ams::sdmmc::impl { + + namespace { + + #if defined(AMS_SDMMC_USE_OS_TIMER) + void SpinWaitMicroSeconds(u32 us) { + const os::Tick timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMicroSeconds(us)) + os::Tick(1); + while (true) { + if (os::GetSystemTick() > timeout_tick) { + break; + } + } + } + + ALWAYS_INLINE void DataSynchronizationBarrier() { + #if defined(ATMOSPHERE_ARCH_ARM64) + __asm__ __volatile__("dsb sy" ::: "memory"); + #elif defined(ATMOSPHERE_ARCH_ARM) + __asm__ __volatile__("dsb" ::: "memory"); + #else + #error "Unknown architecture for DataSynchronizationBarrier" + #endif + } + + ALWAYS_INLINE void InstructionSynchronizationBarrier() { + #if defined(ATMOSPHERE_ARCH_ARM64) || defined(ATMOSPHERE_ARCH_ARM) + __asm__ __volatile__("isb" ::: "memory"); + #else + #error "Unknown architecture for InstructionSynchronizationBarrier" + #endif + } + #endif + + } + + void WaitMicroSeconds(u32 us) { + #if defined(AMS_SDMMC_USE_OS_TIMER) + /* Ensure that nothing is reordered before we wait. */ + DataSynchronizationBarrier(); + InstructionSynchronizationBarrier(); + + /* If the time is small, spinloop, otherwise pend ourselves. */ + if (us < 100) { + SpinWaitMicroSeconds(us); + } else { + os::SleepThread(TimeSpan::FromMicroSeconds(us)); + } + + /* Ensure that nothing is reordered after we wait. */ + DataSynchronizationBarrier(); + InstructionSynchronizationBarrier(); + #elif defined(AMS_SDMMC_USE_UTIL_TIMER) + util::WaitMicroSeconds(us); + #else + #error "Unknown context for ams::sdmmc::impl::WaitMicroSeconds" + #endif + } + + void WaitClocks(u32 num_clocks, u32 clock_frequency_khz) { + AMS_ABORT_UNLESS(clock_frequency_khz > 0); + WaitMicroSeconds(util::DivideUp(1000 * num_clocks, clock_frequency_khz)); + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.hpp new file mode 100644 index 000000000..5d8149591 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_timer.hpp @@ -0,0 +1,68 @@ +/* + * 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 . + */ +#pragma once +#include + +namespace ams::sdmmc::impl { + + void WaitMicroSeconds(u32 us); + void WaitClocks(u32 num_clocks, u32 clock_frequency_khz); + + #if defined(AMS_SDMMC_USE_OS_TIMER) + class ManualTimer { + private: + os::Tick timeout_tick; + bool is_timed_out; + public: + explicit ManualTimer(u32 ms) { + this->timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMilliSeconds(ms)); + this->is_timed_out = false; + } + + bool Update() { + if (this->is_timed_out) { + return false; + } + + this->is_timed_out = os::GetSystemTick() > this->timeout_tick; + return true; + } + }; + #elif defined(AMS_SDMMC_USE_UTIL_TIMER) + class ManualTimer { + private: + u32 timeout_us; + bool is_timed_out; + public: + explicit ManualTimer(u32 ms) { + this->timeout_us = util::GetMicroSeconds() + (ms * 1000); + this->is_timed_out = false; + } + + bool Update() { + if (this->is_timed_out) { + return false; + } + + this->is_timed_out = util::GetMicroSeconds() > this->timeout_us; + return true; + } + }; + #else + #error "Unknown context for ams::sdmmc::ManualTimer" + #endif + +} diff --git a/libraries/libvapours/source/sdmmc/sdmmc_common.cpp b/libraries/libvapours/source/sdmmc/sdmmc_common.cpp new file mode 100644 index 000000000..598e048bf --- /dev/null +++ b/libraries/libvapours/source/sdmmc/sdmmc_common.cpp @@ -0,0 +1,145 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "impl/sdmmc_i_host_controller.hpp" +#include "impl/sdmmc_i_device_accessor.hpp" +#include "impl/sdmmc_clock_reset_controller.hpp" +#include "impl/sdmmc_port_mmc0.hpp" +#include "impl/sdmmc_port_sd_card0.hpp" +#include "impl/sdmmc_port_gc_asic0.hpp" + +namespace ams::sdmmc { + + namespace { + + impl::IHostController *GetHostController(Port port) { + /* Get the controller. */ + impl::IHostController *host_controller = nullptr; + switch (port) { + case Port_Mmc0: host_controller = impl::GetHostControllerOfPortMmc0(); break; + case Port_SdCard0: host_controller = impl::GetHostControllerOfPortSdCard0(); break; + case Port_GcAsic0: host_controller = impl::GetHostControllerOfPortGcAsic0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(host_controller != nullptr); + return host_controller; + } + + impl::IDeviceAccessor *GetDeviceAccessor(Port port) { + /* Get the accessor. */ + impl::IDeviceAccessor *device_accessor = nullptr; + switch (port) { + case Port_Mmc0: device_accessor = impl::GetDeviceAccessorOfPortMmc0(); break; + case Port_SdCard0: device_accessor = impl::GetDeviceAccessorOfPortSdCard0(); break; + case Port_GcAsic0: device_accessor = impl::GetDeviceAccessorOfPortGcAsic0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(device_accessor != nullptr); + return device_accessor; + } + + } + + + + void Initialize(Port port) { + return GetDeviceAccessor(port)->Initialize(); + } + + void Finalize(Port port) { + return GetDeviceAccessor(port)->Finalize(); + } + +#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) + void SwitchToPcvClockResetControl() { + return impl::ClockResetController::SwitchToPcvControl(); + } +#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) { + return GetDeviceAccessor(port)->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); + } + + void UnregisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) { + return GetDeviceAccessor(port)->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address); + } +#endif + + void ChangeCheckTransferInterval(Port port, u32 ms) { + return GetHostController(port)->ChangeCheckTransferInterval(ms); + } + void SetDefaultCheckTransferInterval(Port port) { + return GetHostController(port)->SetDefaultCheckTransferInterval(); + } + + Result Activate(Port port) { + return GetDeviceAccessor(port)->Activate(); + } + + void Deactivate(Port port) { + return GetDeviceAccessor(port)->Deactivate(); + } + + Result Read(void *dst, size_t dst_size, Port port, u32 sector_index, u32 num_sectors) { + return GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, dst, dst_size, true); + } + + Result Write(Port port, u32 sector_index, u32 num_sectors, const void *src, size_t src_size) { + return GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, const_cast(src), src_size, false); + } + + Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { + return GetDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); + } + + Result GetDeviceSpeedMode(SpeedMode *out, Port port) { + return GetDeviceAccessor(port)->GetSpeedMode(out); + } + + Result GetDeviceMemoryCapacity(u32 *out_num_sectors, Port port) { + return GetDeviceAccessor(port)->GetMemoryCapacity(out_num_sectors); + } + + Result GetDeviceStatus(u32 *out_device_status, Port port) { + return GetDeviceAccessor(port)->GetDeviceStatus(out_device_status); + } + + Result GetDeviceCid(void *out, size_t out_size, Port port) { + return GetDeviceAccessor(port)->GetCid(out, out_size); + } + + Result GetDeviceCsd(void *out, size_t out_size, Port port) { + return GetDeviceAccessor(port)->GetCsd(out, out_size); + } + + void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size, Port port) { + return GetDeviceAccessor(port)->GetAndClearErrorInfo(out_error_info, out_log_size, out_log_buffer, log_buffer_size); + } + +} diff --git a/libraries/libvapours/source/sdmmc/sdmmc_gc_asic.cpp b/libraries/libvapours/source/sdmmc/sdmmc_gc_asic.cpp new file mode 100644 index 000000000..2071fd315 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/sdmmc_gc_asic.cpp @@ -0,0 +1,85 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "impl/sdmmc_gc_asic_device_accessor.hpp" +#include "impl/sdmmc_port_mmc0.hpp" +#include "impl/sdmmc_port_sd_card0.hpp" +#include "impl/sdmmc_port_gc_asic0.hpp" + +namespace ams::sdmmc { + + namespace { + + impl::GcAsicDeviceAccessor *GetGcAsicDeviceAccessor(Port port) { + /* Get the accessor. */ + impl::GcAsicDeviceAccessor *gc_asic_device_accessor = nullptr; + switch (port) { + case Port_GcAsic0: gc_asic_device_accessor = impl::GetGcAsicDeviceAccessorOfPortGcAsic0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(gc_asic_device_accessor != nullptr); + return gc_asic_device_accessor; + } + + } + + void PutGcAsicToSleep(Port port) { + return GetGcAsicDeviceAccessor(port)->PutGcAsicToSleep(); + } + + Result AwakenGcAsic(Port port) { + return GetGcAsicDeviceAccessor(port)->AwakenGcAsic(); + } + + Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size) { + return GetGcAsicDeviceAccessor(port)->WriteGcAsicOperation(op_buf, op_buf_size); + } + + Result FinishGcAsicOperation(Port port) { + return GetGcAsicDeviceAccessor(port)->FinishGcAsicOperation(); + } + + Result AbortGcAsicOperation(Port port) { + return GetGcAsicDeviceAccessor(port)->AbortGcAsicOperation(); + } + + Result SleepGcAsic(Port port) { + return GetGcAsicDeviceAccessor(port)->SleepGcAsic(); + } + + Result UpdateGcAsicKey(Port port) { + return GetGcAsicDeviceAccessor(port)->UpdateGcAsicKey(); + } + + void SignalGcRemovedEvent(Port port) { + return GetGcAsicDeviceAccessor(port)->SignalGcRemovedEvent(); + } + + void ClearGcRemovedEvent(Port port) { + return GetGcAsicDeviceAccessor(port)->ClearGcRemovedEvent(); + } + +} diff --git a/libraries/libvapours/source/sdmmc/sdmmc_mmc.cpp b/libraries/libvapours/source/sdmmc/sdmmc_mmc.cpp new file mode 100644 index 000000000..76fc5716a --- /dev/null +++ b/libraries/libvapours/source/sdmmc/sdmmc_mmc.cpp @@ -0,0 +1,81 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "impl/sdmmc_mmc_device_accessor.hpp" +#include "impl/sdmmc_port_mmc0.hpp" +#include "impl/sdmmc_port_sd_card0.hpp" +#include "impl/sdmmc_port_gc_asic0.hpp" + +namespace ams::sdmmc { + + namespace { + + impl::MmcDeviceAccessor *GetMmcDeviceAccessor(Port port) { + /* Get the accessor. */ + impl::MmcDeviceAccessor *mmc_device_accessor = nullptr; + switch (port) { + case Port_Mmc0: mmc_device_accessor = impl::GetMmcDeviceAccessorOfPortMmc0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(mmc_device_accessor != nullptr); + return mmc_device_accessor; + } + + } + + void SetMmcWorkBuffer(Port port, void *buffer, size_t buffer_size) { + return GetMmcDeviceAccessor(port)->SetMmcWorkBuffer(buffer, buffer_size); + } + + void PutMmcToSleep(Port port) { + return GetMmcDeviceAccessor(port)->PutMmcToSleep(); + } + + void AwakenMmc(Port port) { + return GetMmcDeviceAccessor(port)->AwakenMmc(); + } + + Result SelectMmcPartition(Port port, MmcPartition mmc_partition) { + return GetMmcDeviceAccessor(port)->SelectMmcPartition(mmc_partition); + } + + Result EraseMmc(Port port) { + return GetMmcDeviceAccessor(port)->EraseMmc(); + } + + Result GetMmcBootPartitionCapacity(u32 *out_num_sectors, Port port) { + return GetMmcDeviceAccessor(port)->GetMmcBootPartitionCapacity(out_num_sectors); + } + + Result GetMmcExtendedCsd(void *out_buffer, size_t buffer_size, Port port) { + return GetMmcDeviceAccessor(port)->GetMmcExtendedCsd(out_buffer, buffer_size); + } + + Result CheckMmcConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { + return GetMmcDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); + } + +} diff --git a/libraries/libvapours/source/sdmmc/sdmmc_sd_card.cpp b/libraries/libvapours/source/sdmmc/sdmmc_sd_card.cpp new file mode 100644 index 000000000..c10a38d95 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/sdmmc_sd_card.cpp @@ -0,0 +1,103 @@ +/* + * 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 . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "impl/sdmmc_sd_card_device_accessor.hpp" +#include "impl/sdmmc_port_mmc0.hpp" +#include "impl/sdmmc_port_sd_card0.hpp" +#include "impl/sdmmc_port_gc_asic0.hpp" + +namespace ams::sdmmc { + + namespace { + + impl::SdCardDeviceAccessor *GetSdCardDeviceAccessor(Port port) { + /* Get the accessor. */ + impl::SdCardDeviceAccessor *sd_card_device_accessor = nullptr; + switch (port) { + case Port_SdCard0: sd_card_device_accessor = impl::GetSdCardDeviceAccessorOfPortSdCard0(); break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + /* Ensure it's valid */ + AMS_ABORT_UNLESS(sd_card_device_accessor != nullptr); + return sd_card_device_accessor; + } + + } + + void SetSdCardWorkBuffer(Port port, void *buffer, size_t buffer_size) { + return GetSdCardDeviceAccessor(port)->SetSdCardWorkBuffer(buffer, buffer_size); + } + + void PutSdCardToSleep(Port port) { + return GetSdCardDeviceAccessor(port)->PutSdCardToSleep(); + } + + void AwakenSdCard(Port port) { + return GetSdCardDeviceAccessor(port)->AwakenSdCard(); + } + + Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors, Port port) { + return GetSdCardDeviceAccessor(port)->GetSdCardProtectedAreaCapacity(out_num_sectors); + } + + Result GetSdCardScr(void *dst, size_t dst_size, Port port) { + return GetSdCardDeviceAccessor(port)->GetSdCardScr(dst, dst_size); + } + + Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, Port port, SdCardSwitchFunction switch_function) { + return GetSdCardDeviceAccessor(port)->GetSdCardSwitchFunctionStatus(dst, dst_size, switch_function); + } + + Result GetSdCardCurrentConsumption(u16 *out_current_consumption, Port port, SpeedMode speed_mode) { + return GetSdCardDeviceAccessor(port)->GetSdCardCurrentConsumption(out_current_consumption, speed_mode); + } + + Result GetSdCardSdStatus(void *dst, size_t dst_size, Port port) { + return GetSdCardDeviceAccessor(port)->GetSdCardSdStatus(dst, dst_size); + } + + Result CheckSdCardConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) { + return GetSdCardDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width); + } + + bool IsSdCardInserted(Port port) { + return GetSdCardDeviceAccessor(port)->IsSdCardInserted(); + } + + bool IsSdCardRemoved(Port port) { + return GetSdCardDeviceAccessor(port)->IsSdCardRemoved(); + } + + + void RegisterSdCardDetectionEventCallback(Port port, DeviceDetectionEventCallback callback, void *arg) { + return GetSdCardDeviceAccessor(port)->RegisterSdCardDetectionEventCallback(callback, arg); + } + + void UnregisterSdCardDetectionEventCallback(Port port) { + return GetSdCardDeviceAccessor(port)->UnregisterSdCardDetectionEventCallback(); + } + +} + diff --git a/libraries/libvapours/source/util/util_format_string.cpp b/libraries/libvapours/source/util/util_format_string.cpp new file mode 100644 index 000000000..b6510e773 --- /dev/null +++ b/libraries/libvapours/source/util/util_format_string.cpp @@ -0,0 +1,434 @@ +/* + * 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 . + */ +#include + +namespace ams::util { + + #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::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(*(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(cur - str); + } + + int TVSNPrintfImpl(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 (const size_t i = (dst_index++); i < dst_size) { + dst[i] = 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(_width); + } else { + SetFlag(FormatSpecifierFlag_LeftJustify); + width = static_cast(-_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(_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(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(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(width); i++) { + WriteCharacter(' '); + } + } + }; + + /* Output the integer. */ + if (is_unsigned) { + uintmax_t n = 0; + if (HasFlag(FormatSpecifierFlag_LongLong)) { + n = static_cast(va_arg(vl, unsigned long long)); + } else if (HasFlag(FormatSpecifierFlag_Long)) { + n = static_cast(va_arg(vl, unsigned long)); + } else if (HasFlag(FormatSpecifierFlag_Char)) { + n = static_cast(va_arg(vl, unsigned int)); + } else if (HasFlag(FormatSpecifierFlag_Short)) { + n = static_cast(va_arg(vl, unsigned int)); + } else { + n = static_cast(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(va_arg(vl, signed long long)); + } else if (HasFlag(FormatSpecifierFlag_Long)) { + n = static_cast(va_arg(vl, signed long)); + } else if (HasFlag(FormatSpecifierFlag_Char)) { + n = static_cast(va_arg(vl, signed int)); + } else if (HasFlag(FormatSpecifierFlag_Short)) { + n = static_cast(va_arg(vl, signed int)); + } else { + n = static_cast(va_arg(vl, signed int)); + } + const bool negative = n < 0; + const uintmax_t u = (negative) ? static_cast(-n) : static_cast(n); + PrintInteger(negative, u); + } + } + break; + case 'c': + { + size_t len = 1; + if (!HasFlag(FormatSpecifierFlag_LeftJustify)) { + while (len++ < width) { + WriteCharacter(' '); + } + } + WriteCharacter(static_cast(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::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'; + + /* Return number of characters that would have been printed sans the null terminator. */ + return static_cast(dst_index) - 1; + } + + } + + #pragma GCC pop_options + + int TVSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl) { + return TVSNPrintfImpl(dst, dst_size, fmt, vl); + } + + int TSNPrintf(char *dst, size_t dst_size, const char *fmt, ...) { + std::va_list vl; + va_start(vl, fmt); + const int len = TVSNPrintf(dst, dst_size, fmt, vl); + va_end(vl); + + return len; + } + + int VSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl) { + /* TODO: floating point support? */ + return TVSNPrintfImpl(dst, dst_size, fmt, vl); + } + + int SNPrintf(char *dst, size_t dst_size, const char *fmt, ...) { + std::va_list vl; + va_start(vl, fmt); + const int len = VSNPrintf(dst, dst_size, fmt, vl); + va_end(vl); + + return len; + } + +} diff --git a/mesosphere/Makefile b/mesosphere/Makefile index 5a02471ac..d168d278e 100644 --- a/mesosphere/Makefile +++ b/mesosphere/Makefile @@ -1,26 +1,41 @@ -TARGETS := kernel.bin kernel_ldr.bin -CLEAN_TARGETS := $(foreach target,$(TARGETS),$(target:.bin=)-clean) +ATMOSPHERE_BUILD_CONFIGS := +all: release -SUBFOLDERS := $(MODULES) +define ATMOSPHERE_ADD_TARGET -all: mesosphere.bin +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) -clean: $(CLEAN_TARGETS) - @rm -f mesosphere.bin +$(strip $1): mesosphere$(strip $2).bin -mesosphere.bin: $(TARGETS) - @python build_mesosphere.py - @echo "Built mesosphere.bin..." +mesosphere$(strip $2).bin: kernel/kernel$(strip $2).bin kernel_ldr/kernel_ldr$(strip $2).bin + @python build_mesosphere.py kernel_ldr/kernel_ldr$(strip $2).bin kernel/kernel$(strip $2).bin mesosphere$(strip $2).bin + @echo "Built mesosphere$(strip $2).bin..." -$(TARGETS): check_libmeso - $(MAKE) -C $(@:.bin=) - @cp $(@:.bin=)/$(@) $(@) +kernel/kernel$(strip $2).bin: check_libmeso$(strip $1) + @$$(MAKE) -C kernel $(strip $1) -check_libmeso: - @$(MAKE) --no-print-directory -C ../libraries/libmesosphere +kernel_ldr/kernel_ldr$(strip $2).bin: check_libmeso$(strip $1) + @$$(MAKE) -C kernel_ldr $(strip $1) -$(CLEAN_TARGETS): - $(MAKE) -C $(@:-clean=) clean - @rm -f $(@:-clean=).bin +check_libmeso$(strip $1): + @$$(MAKE) -C ../libraries/libmesosphere $(strip $1) -.PHONY: all clean $(CLEAN_TARGETS) +clean-$(strip $1): + @$$(MAKE) -C ../libraries/libmesosphere clean-$(strip $1) + @$$(MAKE) -C kernel clean-$(strip $1) + @$$(MAKE) -C kernel_ldr clean-$(strip $1) + @rm -f mesosphere$(strip $2).bin + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, ,)) +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug,)) +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit,)) + +clean: + @$(MAKE) -C ../libraries/libmesosphere clean + @$(MAKE) -C kernel clean + @$(MAKE) -C kernel_ldr clean + @rm -f mesosphere*.bin + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) diff --git a/mesosphere/build_mesosphere.py b/mesosphere/build_mesosphere.py index bbd06b62c..f5fb44fc8 100644 --- a/mesosphere/build_mesosphere.py +++ b/mesosphere/build_mesosphere.py @@ -11,12 +11,12 @@ def align_up(val, algn): def main(argc, argv): - if argc != 1: - print('Usage: %s' % argv[0]) + if argc != 4: + print('Usage: %s kernel_ldr.bin kernel.bin output.bin' % argv[0]) return 1 - with open('kernel_ldr/kernel_ldr.bin', 'rb') as f: + with open(argv[1], 'rb') as f: kernel_ldr = f.read() - with open('kernel/kernel.bin', 'rb') as f: + with open(argv[2], 'rb') as f: kernel = f.read() kernel_metadata_offset = 4 assert (kernel_metadata_offset <= len(kernel) - 0x40) @@ -37,7 +37,7 @@ def main(argc, argv): kernel_ldr_end = kernel_ldr_offset + len(kernel_ldr) mesosphere_end = align_up(kernel_ldr_end, 0x1000) - with open('mesosphere.bin', 'wb') as f: + with open(argv[3], 'wb') as f: f.write(kernel[:kernel_metadata_offset + 4]) f.write(pk(' InitialProcessBinarySizeMax); - static_assert(KernelResourceRegionSize + ExtraKernelResourceSize > InitialProcessBinarySizeMax); - - /* 10.0.0 reduced the kernel resource region size by 64K. */ - if (kern::GetTargetFirmware() >= ams::TargetFirmware_10_0_0) { - resource_region_size -= KernelResourceReduction_10_0_0; - } - return resource_region_size; - } - /* Page table attributes. */ constexpr PageTableEntry KernelRoDataAttribute(PageTableEntry::Permission_KernelR, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); constexpr PageTableEntry KernelRwDataAttribute(PageTableEntry::Permission_KernelRW, PageTableEntry::PageAttribute_NormalMemory, PageTableEntry::Shareable_InnerShareable, PageTableEntry::MappingFlag_Mapped); @@ -114,10 +95,10 @@ namespace ams::kern::init { InitializeSlabResourceCounts(); /* Insert the root region for the virtual memory tree, from which all other regions will derive. */ - KMemoryLayout::GetVirtualMemoryRegionTree().insert(*KMemoryLayout::GetMemoryRegionAllocator().Create(KernelVirtualAddressSpaceBase, KernelVirtualAddressSpaceSize, 0, 0)); + KMemoryLayout::GetVirtualMemoryRegionTree().InsertDirectly(KernelVirtualAddressSpaceBase, KernelVirtualAddressSpaceSize); /* Insert the root region for the physical memory tree, from which all other regions will derive. */ - KMemoryLayout::GetPhysicalMemoryRegionTree().insert(*KMemoryLayout::GetMemoryRegionAllocator().Create(KernelPhysicalAddressSpaceBase, KernelPhysicalAddressSpaceSize, 0, 0)); + KMemoryLayout::GetPhysicalMemoryRegionTree().InsertDirectly(KernelPhysicalAddressSpaceBase, KernelPhysicalAddressSpaceSize); /* Save start and end for ease of use. */ const uintptr_t code_start_virt_addr = reinterpret_cast(_start); @@ -152,8 +133,8 @@ namespace ams::kern::init { const KVirtualAddress stack_region_start = KMemoryLayout::GetVirtualMemoryRegionTree().GetRandomAlignedRegion(StackRegionSize, StackRegionAlign, KMemoryRegionType_Kernel); MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(stack_region_start), StackRegionSize, KMemoryRegionType_KernelStack)); - /* Decide if Kernel should have enlarged resource region (slab region + page table heap region). */ - const size_t resource_region_size = GetResourceRegionSize(); + /* Determine the size of the resource region. */ + const size_t resource_region_size = KMemoryLayout::GetResourceRegionSizeForInit(); /* Determine the size of the slab region. */ const size_t slab_region_size = util::AlignUp(CalculateTotalSlabHeapSize(), PageSize); @@ -189,8 +170,13 @@ namespace ams::kern::init { /* Automatically map in devices that have auto-map attributes. */ for (auto ®ion : KMemoryLayout::GetPhysicalMemoryRegionTree()) { - /* We only care about automatically-mapped regions. */ - if (!region.IsDerivedFrom(KMemoryRegionType_KernelAutoMap)) { + /* We only care about kernel regions. */ + if (!region.IsDerivedFrom(KMemoryRegionType_Kernel)) { + continue; + } + + /* Check whether we should map the region. */ + if (!region.HasTypeAttribute(KMemoryRegionAttr_ShouldKernelMap)) { continue; } @@ -241,6 +227,14 @@ namespace ams::kern::init { /* Insert a physical region for the kernel page table heap region */ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(slab_end_phys_addr), page_table_heap_size, KMemoryRegionType_DramKernelPtHeap)); + /* Insert a physical region for the kernel trace buffer. */ + static_assert(!IsKTraceEnabled || KTraceBufferSize > 0); + if constexpr (IsKTraceEnabled) { + const auto dram_extents = KMemoryLayout::GetMainMemoryPhysicalExtents(); + const KPhysicalAddress ktrace_buffer_phys_addr = dram_extents.GetEndAddress() - KTraceBufferSize; + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(ktrace_buffer_phys_addr), KTraceBufferSize, KMemoryRegionType_KernelTraceBuffer)); + } + /* All DRAM regions that we haven't tagged by this point will be mapped under the linear mapping. Tag them. */ for (auto ®ion : KMemoryLayout::GetPhysicalMemoryRegionTree()) { if (region.GetType() == KMemoryRegionType_Dram) { @@ -258,17 +252,40 @@ namespace ams::kern::init { const uintptr_t linear_region_phys_to_virt_diff = GetInteger(linear_region_start) - GetInteger(aligned_linear_phys_start); /* Map and create regions for all the linearly-mapped data. */ - for (auto ®ion : KMemoryLayout::GetPhysicalMemoryRegionTree()) { - if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { - continue; + { + uintptr_t cur_phys_addr = 0; + uintptr_t cur_size = 0; + for (auto ®ion : KMemoryLayout::GetPhysicalMemoryRegionTree()) { + if (!region.HasTypeAttribute(KMemoryRegionAttr_LinearMapped)) { + continue; + } + + if (cur_size == 0) { + cur_phys_addr = region.GetAddress(); + cur_size = region.GetSize(); + } else if (cur_phys_addr + cur_size == region.GetAddress()) { + cur_size += region.GetSize(); + } else { + const uintptr_t cur_virt_addr = cur_phys_addr + linear_region_phys_to_virt_diff; + ttbr1_table.Map(cur_virt_addr, cur_size, cur_phys_addr, KernelRwDataAttribute, g_initial_page_allocator); + cur_phys_addr = region.GetAddress(); + cur_size = region.GetSize(); + } + + const uintptr_t region_virt_addr = region.GetAddress() + linear_region_phys_to_virt_diff; + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(region_virt_addr, region.GetSize(), GetTypeForVirtualLinearMapping(region.GetType()))); + region.SetPairAddress(region_virt_addr); + + KMemoryRegion *virt_region = KMemoryLayout::GetVirtualMemoryRegionTree().FindModifiable(region_virt_addr); + MESOSPHERE_INIT_ABORT_UNLESS(virt_region != nullptr); + virt_region->SetPairAddress(region.GetAddress()); } - const uintptr_t region_virt_addr = region.GetAddress() + linear_region_phys_to_virt_diff; - ttbr1_table.Map(region_virt_addr, region.GetSize(), region.GetAddress(), KernelRwDataAttribute, g_initial_page_allocator); - - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(region_virt_addr, region.GetSize(), GetTypeForVirtualLinearMapping(region.GetType()))); - region.SetPairAddress(region_virt_addr); - KMemoryLayout::GetVirtualMemoryRegionTree().FindContainingRegion(region_virt_addr)->SetPairAddress(region.GetAddress()); + /* Map the last block, which we may have skipped. */ + if (cur_size != 0) { + const uintptr_t cur_virt_addr = cur_phys_addr + linear_region_phys_to_virt_diff; + ttbr1_table.Map(cur_virt_addr, cur_size, cur_phys_addr, KernelRwDataAttribute, g_initial_page_allocator); + } } /* Create regions for and map all core-specific stacks. */ @@ -289,11 +306,11 @@ namespace ams::kern::init { /* Insert regions for the initial page table region. */ MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetPhysicalMemoryRegionTree().Insert(GetInteger(resource_end_phys_addr), init_page_table_region_size, KMemoryRegionType_DramKernelInitPt)); - MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(resource_end_phys_addr) + linear_region_phys_to_virt_diff, init_page_table_region_size, KMemoryRegionType_VirtualKernelInitPt)); + MESOSPHERE_INIT_ABORT_UNLESS(KMemoryLayout::GetVirtualMemoryRegionTree().Insert(GetInteger(resource_end_phys_addr) + linear_region_phys_to_virt_diff, init_page_table_region_size, KMemoryRegionType_VirtualDramKernelInitPt)); /* All linear-mapped DRAM regions that we haven't tagged by this point will be allocated to some pool partition. Tag them. */ for (auto ®ion : KMemoryLayout::GetPhysicalMemoryRegionTree()) { - if (region.GetType() == KMemoryRegionType_DramLinearMapped) { + if (region.GetType() == (KMemoryRegionType_Dram | KMemoryRegionAttr_LinearMapped)) { region.SetType(KMemoryRegionType_DramPoolPartition); } } @@ -313,6 +330,7 @@ namespace ams::kern::init { } void SetInitArguments(s32 core_id, KPhysicalAddress address, uintptr_t arg) { + /* Set the arguments. */ KInitArguments *init_args = reinterpret_cast(GetInteger(address)); init_args->ttbr0 = cpu::GetTtbr0El1(); init_args->ttbr1 = arg; @@ -325,14 +343,14 @@ namespace ams::kern::init { init_args->entrypoint = reinterpret_cast(::ams::kern::HorizonKernelMain); init_args->argument = static_cast(core_id); init_args->setup_function = reinterpret_cast(::ams::kern::init::StartOtherCore); + + /* Ensure the arguments are written to memory. */ + StoreDataCache(init_args, sizeof(*init_args)); + + /* Save the pointer to the arguments to use as argument upon core wakeup. */ g_init_arguments_phys_addr[core_id] = address; } - - void StoreInitArguments() { - StoreDataCache(g_init_arguments_phys_addr, sizeof(g_init_arguments_phys_addr)); - } - void InitializeDebugRegisters() { /* Determine how many watchpoints and breakpoints we have */ cpu::DebugFeatureRegisterAccessor aa64dfr0; @@ -357,6 +375,7 @@ namespace ams::kern::init { case ID: \ cpu::SetDbgWcr##ID##El1(__VA_ARGS__); \ cpu::SetDbgWvr##ID##El1(__VA_ARGS__); \ + [[fallthrough]]; #define MESOSPHERE_INITIALIZE_BREAKPOINT_CASE(ID, ...) \ case ID: \ diff --git a/mesosphere/kernel/source/arch/arm64/init/start.s b/mesosphere/kernel/source/arch/arm64/init/start.s index f4a6d1bde..0549679b8 100644 --- a/mesosphere/kernel/source/arch/arm64/init/start.s +++ b/mesosphere/kernel/source/arch/arm64/init/start.s @@ -61,12 +61,14 @@ __metadata_kernel_layout: .error "Incorrect Mesosphere Metadata" .endif +#ifdef ATMOSPHERE_BOARD_NINTENDO_NX .global _ZN3ams4kern17GetTargetFirmwareEv .type _ZN3ams4kern17GetTargetFirmwareEv, %function _ZN3ams4kern17GetTargetFirmwareEv: adr x0, __metadata_target_firmware ldr w0, [x0] ret +#endif /* ams::kern::init::StartCore0(uintptr_t, uintptr_t) */ .section .crt0.text._ZN3ams4kern4init10StartCore0Emm, "ax", %progbits @@ -94,6 +96,7 @@ core0_el2: core0_el1: bl _ZN3ams4kern4init19DisableMmuAndCachesEv +#ifdef ATMOSPHERE_BOARD_NINTENDO_NX /* Get the target firmware from exosphere. */ LOAD_IMMEDIATE_32(w0, 0xC3000004) mov w1, #65000 @@ -105,6 +108,7 @@ core0_el1: /* Store the target firmware. */ adr x0, __metadata_target_firmware str w1, [x0] +#endif /* We want to invoke kernel loader. */ adr x0, _start diff --git a/mesosphere/kernel_ldr/Makefile b/mesosphere/kernel_ldr/Makefile index 42e8c77ea..d5e3deb68 100644 --- a/mesosphere/kernel_ldr/Makefile +++ b/mesosphere/kernel_ldr/Makefile @@ -1,39 +1,23 @@ #--------------------------------------------------------------------------------- # pull in common atmosphere configuration #--------------------------------------------------------------------------------- -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/mesosphere.mk +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) +include $(CURRENT_DIRECTORY)/../../libraries/config/templates/mesosphere.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))) +ifneq ($(__RECURSIVE__),1) #--------------------------------------------------------------------------------- -export OUTPUT := $(CURDIR)/$(TARGET) -export TOPDIR := $(CURDIR) -export DEPSDIR := $(CURDIR)/$(BUILD) - -export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) $(CURDIR)/include \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +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 @@ -58,41 +42,72 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ -I. -export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) -.PHONY: $(BUILD) clean all +export TOPDIR := $(CURRENT_DIRECTORY) + +OUTPUT_BASE := $(TOPDIR)/$(notdir $(TOPDIR)) #--------------------------------------------------------------------------------- -all: $(BUILD) check_libmeso -$(BUILD): check_libmeso +ATMOSPHERE_BUILD_CONFIGS := +all: release + +define ATMOSPHERE_ADD_TARGET + +ATMOSPHERE_BUILD_CONFIGS += $(strip $1) + +$(strip $1): check_libmeso_$(strip $1) $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) + @$$(MAKE) __RECURSIVE__=1 OUTPUT=$$(OUTPUT_BASE)$(strip $2) $(3) \ + DEPSDIR=$$(CURDIR)/$$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + LIBMESOSPHERE_NAME=mesosphere$(strip $2) \ + --no-print-directory -C $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) \ + -f $$(THIS_MAKEFILE) + +check_libmeso_$(strip $1): + @$$(MAKE) --no-print-directory -C ../../libraries/libmesosphere $(strip $1) + +clean-$(strip $1): + @echo clean $(strip $1) ... + @rm -fr $$(ATMOSPHERE_BUILD_DIR)/$(strip $1) $$(OUTPUT_BASE)$(strip $2).bin $$(OUTPUT_BASE)$(strip $2).elf + +endef + +$(eval $(call ATMOSPHERE_ADD_TARGET, release, , \ + ATMOSPHERE_BUILD_SETTINGS="" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, debug, _debug, \ + ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_DEBUGGING" \ +)) + +$(eval $(call ATMOSPHERE_ADD_TARGET, audit, _audit, \ + ATMOSPHERE_BUILD_SETTINGS="-DMESOSPHERE_BUILD_FOR_AUDITING" \ +)) + +$(ATMOSPHERE_BUILD_DIR)/%: @[ -d $@ ] || mkdir -p $@ - @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile - -check_libmeso: - @$(MAKE) --no-print-directory -C ../../libraries/libmesosphere #--------------------------------------------------------------------------------- -clean: - @echo clean ... - @rm -fr $(BUILD) $(OUTPUT).bin $(OUTPUT).elf +clean: $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),clean-$(config)) + +.PHONY: all clean $(foreach config,$(ATMOSPHERE_BUILD_CONFIGS),$(config) clean-$(config)) #--------------------------------------------------------------------------------- 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/libmesosphere/lib/libmesosphere.a +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libmesosphere/$(ATMOSPHERE_LIBRARY_DIR)/lib$(LIBMESOSPHERE_NAME).a %.elf: @echo linking $(notdir $@) @@ -101,6 +116,8 @@ $(OUTPUT).elf : $(OFILES) ../../../libraries/libmesosphere/lib/libmesosphere.a $(OFILES_SRC) : $(HFILES_BIN) +kern_libc_generic.o: CFLAGS += -fno-builtin + #--------------------------------------------------------------------------------- # you need a rule like this for each extension you use as binary data #--------------------------------------------------------------------------------- @@ -113,4 +130,4 @@ $(OFILES_SRC) : $(HFILES_BIN) #--------------------------------------------------------------------------------------- endif -#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/mesosphere/kernel_ldr/source/arch/arm64/start.s b/mesosphere/kernel_ldr/source/arch/arm64/start.s index 29efffa5c..f8610ae72 100644 --- a/mesosphere/kernel_ldr/source/arch/arm64/start.s +++ b/mesosphere/kernel_ldr/source/arch/arm64/start.s @@ -56,6 +56,7 @@ _main: stp x2, x30, [sp, #0x10] stp xzr, xzr, [sp, #0x20] +#ifdef ATMOSPHERE_BOARD_NINTENDO_NX /* Get the target firmware from exosphere. */ LOAD_IMMEDIATE_32(w0, 0xC3000004) mov w1, #65000 @@ -67,6 +68,7 @@ _main: /* Store the target firmware. */ adr x0, __metadata_target_firmware str w1, [x0] +#endif /* Apply relocations and call init array for KernelLdr. */ adr x0, _start @@ -121,13 +123,14 @@ _main: mov sp, x2 br x1 - +#ifdef ATMOSPHERE_BOARD_NINTENDO_NX .global _ZN3ams4kern17GetTargetFirmwareEv .type _ZN3ams4kern17GetTargetFirmwareEv, %function _ZN3ams4kern17GetTargetFirmwareEv: adr x0, __metadata_target_firmware ldr w0, [x0] ret +#endif .balign 8 __external_references: diff --git a/mesosphere/kernel_ldr/source/kern_init_loader.cpp b/mesosphere/kernel_ldr/source/kern_init_loader.cpp index cafe8a0b7..7bd7667e5 100644 --- a/mesosphere/kernel_ldr/source/kern_init_loader.cpp +++ b/mesosphere/kernel_ldr/source/kern_init_loader.cpp @@ -28,32 +28,16 @@ namespace ams::kern::init::loader { namespace { - constexpr size_t KernelResourceRegionSize = 0x1728000; - constexpr size_t ExtraKernelResourceSize = 0x68000; - static_assert(ExtraKernelResourceSize + KernelResourceRegionSize == 0x1790000); - constexpr size_t KernelResourceReduction_10_0_0 = 0x10000; + static_assert(InitialProcessBinarySizeMax <= KernelResourceSize); - constexpr size_t InitialPageTableRegionSize = 0x200000; + constexpr size_t InitialPageTableRegionSizeMax = 2_MB; + static_assert(InitialPageTableRegionSizeMax < KernelPageTableHeapSize + KernelInitialPageHeapSize); /* Global Allocator. */ KInitialPageAllocator g_initial_page_allocator; KInitialPageAllocator::State g_final_page_allocator_state; - size_t GetResourceRegionSize() { - /* Decide if Kernel should have enlarged resource region. */ - const bool use_extra_resources = KSystemControl::Init::ShouldIncreaseThreadResourceLimit(); - size_t resource_region_size = KernelResourceRegionSize + (use_extra_resources ? ExtraKernelResourceSize : 0); - static_assert(KernelResourceRegionSize > InitialProcessBinarySizeMax); - static_assert(KernelResourceRegionSize + ExtraKernelResourceSize > InitialProcessBinarySizeMax); - - /* 10.0.0 reduced the kernel resource region size by 64K. */ - if (kern::GetTargetFirmware() >= ams::TargetFirmware_10_0_0) { - resource_region_size -= KernelResourceReduction_10_0_0; - } - return resource_region_size; - } - void RelocateKernelPhysically(uintptr_t &base_address, KernelLayout *&layout) { KPhysicalAddress correct_base = KSystemControl::Init::GetKernelPhysicalBaseAddress(base_address); if (correct_base != base_address) { @@ -81,12 +65,28 @@ namespace ams::kern::init::loader { cpu::DataSynchronizationBarrier(); /* Invalidate entire instruction cache. */ - cpu::InvalidateEntireInstructionCache(); + cpu::InvalidateEntireInstructionCacheForInit(); /* Invalidate entire TLB. */ cpu::InvalidateEntireTlb(); } + #ifdef ATMOSPHERE_BOARD_NINTENDO_NX + + ALWAYS_INLINE bool ShouldPerformCpuSpecificSetup() { + /* Perform cpu-specific setup only on < 10.0.0. */ + return kern::GetTargetFirmware() < ams::TargetFirmware_10_0_0; + } + + #else + + consteval ALWAYS_INLINE bool ShouldPerformCpuSpecificSetup() { + /* Always perform cpu-specific setup. */ + return true; + } + + #endif + void SetupInitialIdentityMapping(KInitialPageTable &ttbr1_table, uintptr_t base_address, uintptr_t kernel_size, uintptr_t page_table_region, size_t page_table_region_size, KInitialPageTable::IPageAllocator &allocator) { /* Make a new page table for TTBR0_EL1. */ KInitialPageTable ttbr0_table(allocator.Allocate()); @@ -116,8 +116,8 @@ namespace ams::kern::init::loader { cpu::MemoryAccessIndirectionRegisterAccessor(MairValue).Store(); cpu::TranslationControlRegisterAccessor(TcrValue).Store(); - /* Perform cpu-specific setup on < 10.0.0. */ - if (kern::GetTargetFirmware() < ams::TargetFirmware_10_0_0) { + /* Perform cpu-specific setup if needed. */ + if (ShouldPerformCpuSpecificSetup()) { SavedRegisterState saved_registers; SaveRegistersToTpidrEl1(&saved_registers); ON_SCOPE_EXIT { VerifyAndClearTpidrEl1(&saved_registers); }; @@ -260,7 +260,7 @@ namespace ams::kern::init::loader { const uintptr_t init_array_end_offset = layout->init_array_end_offset; /* Determine the size of the resource region. */ - const size_t resource_region_size = GetResourceRegionSize(); + const size_t resource_region_size = KMemoryLayout::GetResourceRegionSizeForInit(); /* Setup the INI1 header in memory for the kernel. */ const uintptr_t ini_end_address = base_address + ini_load_offset + resource_region_size; @@ -284,7 +284,7 @@ namespace ams::kern::init::loader { KInitialPageTable ttbr1_table(g_initial_page_allocator.Allocate()); /* Setup initial identity mapping. TTBR1 table passed by reference. */ - SetupInitialIdentityMapping(ttbr1_table, base_address, bss_end_offset, ini_end_address, InitialPageTableRegionSize, g_initial_page_allocator); + SetupInitialIdentityMapping(ttbr1_table, base_address, bss_end_offset, ini_end_address, InitialPageTableRegionSizeMax, g_initial_page_allocator); /* Generate a random slide for the kernel's base address. */ const KVirtualAddress virtual_base_address = GetRandomKernelBaseAddress(ttbr1_table, base_address, bss_end_offset); diff --git a/stratosphere/ams_mitm/Makefile b/stratosphere/ams_mitm/Makefile index a92187ef8..1957b6cdb 100644 --- a/stratosphere/ams_mitm/Makefile +++ b/stratosphere/ams_mitm/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/ams_mitm/source/amsmitm_initialization.cpp b/stratosphere/ams_mitm/source/amsmitm_initialization.cpp index 405a43cc1..38759498a 100644 --- a/stratosphere/ams_mitm/source/amsmitm_initialization.cpp +++ b/stratosphere/ams_mitm/source/amsmitm_initialization.cpp @@ -64,6 +64,10 @@ namespace ams::mitm { /* Emummc file protection. */ FsFile g_emummc_file; + /* Maintain exclusive access to the fusee-secondary archive. */ + FsFile g_secondary_file; + FsFile g_sept_payload_file; + constexpr inline bool IsHexadecimal(const char *str) { while (*str) { if (std::isxdigit(static_cast(*str))) { @@ -129,6 +133,15 @@ namespace ams::mitm { R_ABORT_UNLESS(fsFileWrite(&g_bis_key_file, 0, bis_keys, sizeof(bis_keys), FsWriteOption_Flush)); /* NOTE: g_bis_key_file is intentionally not closed here. This prevents any other process from opening it. */ } + + /* Open a reference to the fusee-secondary archive. */ + /* As upcoming/current atmosphere releases will contain more than one zip which users much choose between, */ + /* maintaining an open reference prevents cleanly the issue of "automatic" updaters selecting the incorrect */ + /* zip, and encourages good updating hygiene -- atmosphere should not be updated on SD while HOS is alive. */ + { + R_ABORT_UNLESS(mitm::fs::OpenSdFile(std::addressof(g_secondary_file), "/atmosphere/fusee-secondary.bin", ams::fs::OpenMode_Read)); + R_ABORT_UNLESS(mitm::fs::OpenSdFile(std::addressof(g_sept_payload_file), "/sept/payload.bin", ams::fs::OpenMode_Read)); + } } /* Initialization implementation */ diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp index 727cc61bc..0a328aa0c 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpc_ams_power_utils.cpp @@ -25,7 +25,7 @@ namespace ams::mitm::bpc { constexpr uintptr_t IramBase = 0x40000000ull; constexpr uintptr_t IramPayloadBase = 0x40010000ull; constexpr size_t IramSize = 0x40000; - constexpr size_t IramPayloadMaxSize = 0x2E000; + constexpr size_t IramPayloadMaxSize = 0x20000; /* Helper enum. */ enum class RebootType : u32 { @@ -108,6 +108,9 @@ namespace ams::mitm::bpc { /* Copy in payload. */ std::memcpy(g_reboot_payload, payload, payload_size); + /* Note to the secure monitor that we have a payload. */ + spl::smc::SetConfig(spl::ConfigItem::ExospherePayloadAddress, g_reboot_payload, nullptr, 0); + /* NOTE: Preferred reboot type may be overrwritten when parsed from settings during boot. */ g_reboot_type = RebootType::ToPayload; } diff --git a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp index 1ea2b7eb9..75f7afc0c 100644 --- a/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp +++ b/stratosphere/ams_mitm/source/bpc_mitm/bpcmitm_module.cpp @@ -37,6 +37,11 @@ namespace ams::mitm::bpc { /* Wait until initialization is complete. */ mitm::WaitInitialized(); + /* On Mariko, we can't reboot to payload/do exosphere-shutdown...so there is no point in bpc.mitm. */ + if (spl::GetSocType() == spl::SocType_Mariko) { + return; + } + /* Create bpc mitm. */ const sm::ServiceName service_name = (hos::GetVersion() >= hos::Version_2_0_0) ? MitmServiceName : DeprecatedMitmServiceName; R_ABORT_UNLESS((g_server_manager.RegisterMitmServer(service_name))); diff --git a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp index 5a3525fcc..1a925a347 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fs_mitm_service.cpp @@ -33,10 +33,27 @@ namespace ams::mitm::fs { constexpr const char AtmosphereHblWebContentDir[] = "/atmosphere/hbl_html/"; constexpr const char ProgramWebContentDir[] = "/manual_html/"; - os::Mutex g_data_storage_lock(false); - os::Mutex g_storage_cache_lock(false); + constinit os::SdkMutex g_data_storage_lock; + constinit os::SdkMutex g_storage_cache_lock; std::unordered_map> g_storage_cache; + constinit os::SdkMutex g_boot0_detect_lock; + constinit bool g_detected_boot0_kind = false; + constinit bool g_is_boot0_custom_public_key = false; + + bool IsBoot0CustomPublicKey(::FsStorage &storage) { + if (AMS_UNLIKELY(!g_detected_boot0_kind)) { + std::scoped_lock lk(g_boot0_detect_lock); + + if (AMS_LIKELY(!g_detected_boot0_kind)) { + g_is_boot0_custom_public_key = DetectBoot0CustomPublicKey(storage); + g_detected_boot0_kind = true; + } + } + + return g_is_boot0_custom_public_key; + } + std::shared_ptr GetStorageCacheEntry(ncm::ProgramId program_id) { std::scoped_lock lk(g_storage_cache_lock); @@ -275,7 +292,11 @@ namespace ams::mitm::fs { /* Set output storage. */ if (bis_partition_id == FsBisPartitionId_BootPartition1Root) { - out.SetValue(MakeSharedStorage(new Boot0Storage(bis_storage, this->client_info)), target_object_id); + if (IsBoot0CustomPublicKey(bis_storage)) { + out.SetValue(MakeSharedStorage(new CustomPublicKeyBoot0Storage(bis_storage, this->client_info, spl::GetSocType())), target_object_id); + } else { + out.SetValue(MakeSharedStorage(new Boot0Storage(bis_storage, this->client_info)), target_object_id); + } } else if (bis_partition_id == FsBisPartitionId_CalibrationBinary) { out.SetValue(MakeSharedStorage(new CalibrationBinaryStorage(bis_storage, this->client_info)), target_object_id); } else { diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp index d30758196..adfc7cea7 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.cpp @@ -22,9 +22,36 @@ namespace ams::mitm::fs { namespace { - os::Mutex g_boot0_access_mutex(false); + os::SdkMutex g_boot0_access_mutex; + constinit bool g_custom_public_key = false; u8 g_boot0_bct_buffer[Boot0Storage::BctEndOffset]; + /* Recognize special public key (https://gist.github.com/SciresM/16b63ac1d80494522bdba2c57995257c). */ + /* P = 19 */ + /* Q = 1696986749729493925354392349339746171297507422986462747526968361144447230710192316397327889522451749459854070558277878297255552508603806832852079596337539247651161831569525505882103311631577368514276343192042634740927726070847704397913856975832811679847928433261678072951551065705680482548543833651752439700272736498378724153330763357721354498194000536297732323628263256733931353143625854828275237159155585342783077681713929284136658773985266864804093157854331138230313706015557050002740810464618031715670281442110238274404626065924786185264268216336867948322976979393032640085259926883014490947373494538254895109731 */ + /* N = 0xFF696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696969696959 */ + /* E = 0x10001 */ + /* D = 6512128715229088976470211610075969347035078304643231077895577077900787352712063823560162578441773733649014439616165727455431015055675770987914713980812453585413988983206576233689754710500864883529402371292948326392791238474661859182717295176679567362482790015587820446999760239570255254879359445627372805817473978644067558931078225451477635089763009580492462097185005355990612929951162366081631888011031830742459571000203341001926135389508196521518687349554188686396554248868403128728646457247197407637887195043486221295751496667162366700477934591694110831002874992896076061627516220934290742867397720103040314639313 */ + + constexpr const u8 CustomPublicKey[0x100] = { + 0x59, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, + 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0xFF, + }; + } bool Boot0Storage::CanModifyBctPublicKey() { @@ -42,24 +69,22 @@ namespace ams::mitm::fs { Result Boot0Storage::Read(s64 offset, void *_buffer, size_t size) { std::scoped_lock lk{g_boot0_access_mutex}; + AMS_ABORT_UNLESS(!g_custom_public_key); /* Check if we have nothing to do. */ - if (size == 0) { - return ResultSuccess(); - } + R_SUCCEED_IF(size == 0); return Base::Read(offset, _buffer, size); } Result Boot0Storage::Write(s64 offset, const void *_buffer, size_t size) { std::scoped_lock lk{g_boot0_access_mutex}; + AMS_ABORT_UNLESS(!g_custom_public_key); const u8 *buffer = static_cast(_buffer); /* Check if we have nothing to do. */ - if (size == 0) { - return ResultSuccess(); - } + R_SUCCEED_IF(size == 0); /* Protect the EKS region from writes. */ if (offset <= EksStart) { @@ -75,22 +100,18 @@ namespace ams::mitm::fs { size = EksStart - offset; } } else if (offset < EksEnd) { - if (offset + size <= EksEnd) { - /* Ignore writes falling strictly within the region. */ - return ResultSuccess(); - } else { - /* Only write past the end of the region. */ - const s64 diff = EksEnd - offset; - buffer += diff; - size -= diff; - offset = EksEnd; - } + /* Ignore writes falling strictly within the region. */ + R_SUCCEED_IF(offset + size <= EksEnd); + + /* Only write past the end of the region. */ + const s64 diff = EksEnd - offset; + buffer += diff; + size -= diff; + offset = EksEnd; } /* If we have nothing to write, succeed immediately. */ - if (size == 0) { - return ResultSuccess(); - } + R_SUCCEED_IF(size == 0); /* We want to protect AutoRCM from NS on ipatched units. If we can modify bct pubks or we're not touching any of them, proceed. */ if (this->CanModifyBctPublicKey() || offset >= BctEndOffset || (util::AlignUp(offset, BctSize) >= BctEndOffset && (offset % BctSize) >= BctPubkEnd)) { @@ -118,4 +139,129 @@ namespace ams::mitm::fs { return Base::Write(0, g_boot0_bct_buffer, BctEndOffset); } + CustomPublicKeyBoot0Storage::CustomPublicKeyBoot0Storage(FsStorage &s, const sm::MitmProcessInfo &c, spl::SocType soc) : Base(s), client_info(c), soc_type(soc) { + std::scoped_lock lk{g_boot0_access_mutex}; + + /* We're custom public key. */ + g_custom_public_key = true; + } + + Result CustomPublicKeyBoot0Storage::Read(s64 offset, void *_buffer, size_t size) { + std::scoped_lock lk{g_boot0_access_mutex}; + AMS_ABORT_UNLESS(g_custom_public_key); + + /* Check if we have nothing to do. */ + R_SUCCEED_IF(size == 0); + + u8 *buffer = static_cast(_buffer); + + /* Check if we're reading the first BCTs for NS. */ + /* If we are, we want to lie about the contents of BCT0/1 so that they validate. */ + if (offset < 0x8000 && this->client_info.program_id == ncm::SystemProgramId::Ns) { + R_TRY(Base::Read(0, g_boot0_bct_buffer, Boot0Storage::BctEndOffset)); + + /* Determine the readable size. */ + const size_t readable_bct01_size = std::min(0x8000, offset + size) - offset; + std::memcpy(buffer, g_boot0_bct_buffer + 0x8000 + offset, readable_bct01_size); + + /* Advance. */ + buffer += readable_bct01_size; + offset += readable_bct01_size; + size -= readable_bct01_size; + } + + /* Check if we have nothing to do. */ + R_SUCCEED_IF(size == 0); + + /* Perform whatever remains of the read. */ + return Base::Read(offset, buffer, size); + } + + Result CustomPublicKeyBoot0Storage::Write(s64 offset, const void *_buffer, size_t size) { + std::scoped_lock lk{g_boot0_access_mutex}; + AMS_ABORT_UNLESS(g_custom_public_key); + + const u8 *buffer = static_cast(_buffer); + + /* Check if we have nothing to do. */ + R_SUCCEED_IF(size == 0); + + /* Drop writes to the first BCTs. */ + if (offset < 0x8000) { + /* Determine the writable size. */ + const size_t writable_bct01_size = std::min(0x8000, offset + size) - offset; + + /* Advance. */ + buffer += writable_bct01_size; + offset += writable_bct01_size; + size -= writable_bct01_size; + } + + /* Check if we have nothing to do. */ + R_SUCCEED_IF(size == 0); + + /* Similarly, we want to drop writes to the end of boot0, where custom bootloader lives. */ + R_SUCCEED_IF(offset >= 0x380000); + if (offset + size >= 0x380000) { + size = 0x380000 - offset; + } + + /* On erista, we want to protect the EKS region. */ + if (this->soc_type == spl::SocType_Erista) { + if (offset <= Boot0Storage::EksStart) { + if (offset + size < Boot0Storage::EksStart) { + /* Fall through, no need to do anything here. */ + } else { + if (offset + size > Boot0Storage::EksEnd) { + /* Perform portion of write falling past end of keyblobs. */ + const s64 diff = Boot0Storage::EksEnd - offset; + R_TRY(Base::Write(Boot0Storage::EksEnd, buffer + diff, size - diff)); + } + /* Adjust size to avoid writing end of data. */ + size = Boot0Storage::EksStart - offset; + } + } else if (offset < Boot0Storage::EksEnd) { + /* Ignore writes falling strictly within the region. */ + R_SUCCEED_IF(offset + size <= Boot0Storage::EksEnd); + + /* Only write past the end of the region. */ + const s64 diff = Boot0Storage::EksEnd - offset; + buffer += diff; + size -= diff; + offset = Boot0Storage::EksEnd; + } + } + + /* If we have nothing to write, succeed immediately. */ + R_SUCCEED_IF(size == 0); + + /* Perform whatever remains of the write. */ + return Base::Write(offset, buffer, size); + } + + bool DetectBoot0CustomPublicKey(::FsStorage &storage) { + /* Determine public key offset. */ + const size_t bct_pubk_offset = spl::GetSocType() == spl::SocType_Mariko ? 0x10 : 0x210; + + u8 work_buffer[0x400]; + + /* Read BCT-Normal-Main. */ + R_ABORT_UNLESS(::fsStorageRead(std::addressof(storage), 0, work_buffer, sizeof(work_buffer))); + + /* Check for custom public key. */ + if (std::memcmp(work_buffer + bct_pubk_offset, CustomPublicKey, sizeof(CustomPublicKey)) != 0) { + return false; + } + + /* Read BCT-Safe-Main. */ + R_ABORT_UNLESS(::fsStorageRead(std::addressof(storage), 0x4000, work_buffer, sizeof(work_buffer))); + + /* Check for custom public key. */ + if (std::memcmp(work_buffer + bct_pubk_offset, CustomPublicKey, sizeof(CustomPublicKey)) != 0) { + return false; + } + + return true; + } + } diff --git a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp index a1ac52ed2..09f7d1775 100644 --- a/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp +++ b/stratosphere/ams_mitm/source/fs_mitm/fsmitm_boot0storage.hpp @@ -140,10 +140,25 @@ namespace ams::mitm::fs { private: bool CanModifyBctPublicKey(); public: - Boot0Storage(FsStorage &s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ } + Boot0Storage(FsStorage &s, const sm::MitmProcessInfo &c) : Base(s), client_info(c) { /* ... */ } public: virtual Result Read(s64 offset, void *_buffer, size_t size) override; virtual Result Write(s64 offset, const void *_buffer, size_t size) override; }; + class CustomPublicKeyBoot0Storage : public SectoredStorageAdapter { + public: + using Base = SectoredStorageAdapter; + private: + sm::MitmProcessInfo client_info; + spl::SocType soc_type; + public: + CustomPublicKeyBoot0Storage(FsStorage &s, const sm::MitmProcessInfo &c, spl::SocType soc); + public: + virtual Result Read(s64 offset, void *_buffer, size_t size) override; + virtual Result Write(s64 offset, const void *_buffer, size_t size) override; + }; + + bool DetectBoot0CustomPublicKey(::FsStorage &storage); + } diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp index 4c12ff57c..d27b7cf0c 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.cpp @@ -15,37 +15,94 @@ */ #include #include "set_mitm_service.hpp" +#include "set_shim.h" namespace ams::mitm::settings { using namespace ams::settings; + namespace { + + constinit os::ProcessId g_application_process_id = os::InvalidProcessId; + constinit cfg::OverrideLocale g_application_locale; + constinit bool g_valid_language; + constinit bool g_valid_region; + } + + SetMitmService::SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c) : sf::MitmServiceImplBase(std::forward>(s), c) { + if (this->client_info.program_id == ncm::SystemProgramId::Ns) { + os::ProcessId application_process_id; + if (R_SUCCEEDED(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))) && g_application_process_id == application_process_id) { + this->locale = g_application_locale; + this->is_valid_language = g_valid_language; + this->is_valid_region = g_valid_region; + this->got_locale = true; + } else { + this->InvalidateLocale(); + } + } else { + this->InvalidateLocale(); + } + } + Result SetMitmService::EnsureLocale() { + /* Optimization: if locale has already been gotten, we can stop. */ + if (AMS_LIKELY(this->got_locale)) { + return ResultSuccess(); + } + std::scoped_lock lk(this->lock); const bool is_ns = this->client_info.program_id == ncm::SystemProgramId::Ns; if (!this->got_locale) { - std::memset(&this->locale, 0xCC, sizeof(this->locale)); ncm::ProgramId program_id = this->client_info.program_id; + os::ProcessId application_process_id = os::InvalidProcessId; + if (is_ns) { /* When NS asks for a locale, refresh to get the current application locale. */ - os::ProcessId application_process_id; - R_TRY(pm::dmnt::GetApplicationProcessId(&application_process_id)); - R_TRY(pm::info::GetProgramId(&program_id, application_process_id)); + R_TRY(pm::dmnt::GetApplicationProcessId(std::addressof(application_process_id))); + R_TRY(pm::info::GetProgramId(std::addressof(program_id), application_process_id)); + } + this->locale = cfg::GetOverrideLocale(program_id); + this->is_valid_language = settings::IsValidLanguageCode(this->locale.language_code); + this->is_valid_region = settings::IsValidRegionCode(this->locale.region_code); + this->got_locale = true; + + if (is_ns) { + g_application_locale = this->locale; + g_valid_language = this->is_valid_language; + g_valid_region = this->is_valid_region; + g_application_process_id = application_process_id; } - this->locale = cfg::GetOverrideLocale(program_id); - this->got_locale = !is_ns; } return ResultSuccess(); } + void SetMitmService::InvalidateLocale() { + std::scoped_lock lk(this->lock); + + std::memset(std::addressof(this->locale), 0xCC, sizeof(this->locale)); + this->is_valid_language = false; + this->is_valid_region = false; + this->got_locale = false; + } + Result SetMitmService::GetLanguageCode(sf::Out out) { this->EnsureLocale(); /* If there's no override locale, just use the actual one. */ - R_UNLESS(settings::IsValidLanguageCode(this->locale.language_code), sm::mitm::ResultShouldForwardToSession()); + if (AMS_UNLIKELY(!this->is_valid_language)) { + static_assert(sizeof(u64) == sizeof(settings::LanguageCode)); + R_TRY(setGetLanguageCodeFwd(this->forward_service.get(), reinterpret_cast(std::addressof(this->locale.language_code)))); + + this->is_valid_language = true; + if (this->client_info.program_id == ncm::SystemProgramId::Ns) { + g_application_locale.language_code = this->locale.language_code; + g_valid_language = true; + } + } out.SetValue(this->locale.language_code); return ResultSuccess(); @@ -55,7 +112,16 @@ namespace ams::mitm::settings { this->EnsureLocale(); /* If there's no override locale, just use the actual one. */ - R_UNLESS(settings::IsValidRegionCode(this->locale.region_code), sm::mitm::ResultShouldForwardToSession()); + if (AMS_UNLIKELY(!this->is_valid_region)) { + static_assert(sizeof(::SetRegion) == sizeof(settings::RegionCode)); + R_TRY(setGetRegionCodeFwd(this->forward_service.get(), reinterpret_cast<::SetRegion *>(std::addressof(this->locale.region_code)))); + + this->is_valid_region = true; + if (this->client_info.program_id == ncm::SystemProgramId::Ns) { + g_application_locale.region_code = this->locale.region_code; + g_valid_region = true; + } + } out.SetValue(this->locale.region_code); return ResultSuccess(); diff --git a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp index fe57f425f..a69bcaefd 100644 --- a/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp +++ b/stratosphere/ams_mitm/source/set_mitm/set_mitm_service.hpp @@ -33,8 +33,10 @@ namespace ams::mitm::settings { os::Mutex lock{false}; cfg::OverrideLocale locale; bool got_locale = false; + bool is_valid_language = false; + bool is_valid_region = false; public: - using MitmServiceImplBase::MitmServiceImplBase; + SetMitmService(std::shared_ptr<::Service> &&s, const sm::MitmProcessInfo &c); public: static bool ShouldMitm(const sm::MitmProcessInfo &client_info) { /* We will mitm: @@ -44,6 +46,7 @@ namespace ams::mitm::settings { return client_info.program_id == ncm::SystemProgramId::Ns || is_game; } private: + void InvalidateLocale(); Result EnsureLocale(); public: Result GetLanguageCode(sf::Out out); diff --git a/stratosphere/ams_mitm/source/set_mitm/set_shim.c b/stratosphere/ams_mitm/source/set_mitm/set_shim.c new file mode 100644 index 000000000..255ad7603 --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/set_shim.c @@ -0,0 +1,36 @@ +/* + * 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 . + */ +#include "set_shim.h" + +static Result _setCmdNoInOut64(Service* srv, u64 *out, u32 cmd_id) { + return serviceDispatchOut(srv, cmd_id, *out); +} + +static Result _setCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) { + return serviceDispatchOut(srv, cmd_id, *out); +} + +/* Forwarding shims. */ +Result setGetLanguageCodeFwd(Service *s, u64* out) { + return _setCmdNoInOut64(s, out, 0); +} + +Result setGetRegionCodeFwd(Service *s, SetRegion *out) { + s32 code=0; + Result rc = _setCmdNoInOutU32(s, (u32*)&code, 4); + if (R_SUCCEEDED(rc) && out) *out = code; + return rc; +} diff --git a/stratosphere/ams_mitm/source/set_mitm/set_shim.h b/stratosphere/ams_mitm/source/set_mitm/set_shim.h new file mode 100644 index 000000000..f2f3cad1a --- /dev/null +++ b/stratosphere/ams_mitm/source/set_mitm/set_shim.h @@ -0,0 +1,20 @@ +/** + * @file set_shim.h + * @brief Settings Services (fs) IPC wrapper for set.mitm. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forwarding shims. */ +Result setGetLanguageCodeFwd(Service *s, u64* out); +Result setGetRegionCodeFwd(Service *s, SetRegion *out); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp index 7f21d3c28..b1b49fe91 100644 --- a/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp +++ b/stratosphere/ams_mitm/source/set_mitm/setsys_mitm_service.cpp @@ -56,10 +56,18 @@ namespace ams::mitm::settings { const auto api_info = exosphere::GetApiInfo(); const char emummc_char = emummc::IsActive() ? 'E' : 'S'; + /* NOTE: While Mesosphere is in experimental/opt-in, we will display it as part of the firmware. */ + const char mesosphere_char = svc::IsKernelMesosphere() ? 'M' : '0'; + + /* TODO: Remove separate display for mesosphere vs not mesosphere in Atmosphere 1.0.0. */ + AMS_ABORT_UNLESS(api_info.GetMajorVersion() == 0); + /* NOTE: We have carefully accounted for the size of the string we print. */ /* No truncation occurs assuming two-digits for all version number components. */ char display_version[sizeof(g_ams_firmware_version.display_version)]; - std::snprintf(display_version, sizeof(display_version), "%s|AMS %u.%u.%u|%c", g_ams_firmware_version.display_version, api_info.GetMajorVersion(), api_info.GetMinorVersion(), api_info.GetMicroVersion(), emummc_char); + + std::snprintf(display_version, sizeof(display_version), "%s|AMS %c.%u.%u|%c", g_ams_firmware_version.display_version, mesosphere_char, api_info.GetMinorVersion(), api_info.GetMicroVersion(), emummc_char); + std::memcpy(g_ams_firmware_version.display_version, display_version, sizeof(display_version)); } diff --git a/stratosphere/boot/Makefile b/stratosphere/boot/Makefile index d7e1a1e7a..40b971879 100644 --- a/stratosphere/boot/Makefile +++ b/stratosphere/boot/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin diff --git a/stratosphere/boot/boot.json b/stratosphere/boot/boot.json index e3e6662f6..b8166d193 100644 --- a/stratosphere/boot/boot.json +++ b/stratosphere/boot/boot.json @@ -123,6 +123,15 @@ "is_io": true } }, + { + "type": "map", + "value": { + "address": "0x7000A000", + "size": "0x1000", + "is_ro": false, + "is_io": true + } + }, { "type": "map", "value": { diff --git a/stratosphere/boot/source/api_overrides/pcv_clkrst_api_for_boot.board.nintendo_nx.cpp b/stratosphere/boot/source/api_overrides/pcv_clkrst_api_for_boot.board.nintendo_nx.cpp new file mode 100644 index 000000000..11bb7fd66 --- /dev/null +++ b/stratosphere/boot/source/api_overrides/pcv_clkrst_api_for_boot.board.nintendo_nx.cpp @@ -0,0 +1,207 @@ +/* + * 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 . + */ +#include + +namespace ams { + + namespace { + + constexpr inline dd::PhysicalAddress ClkRstRegistersPhysicalAddress = 0x60006000; + constexpr inline size_t ClkRstRegistersSize = 0x1000; + + uintptr_t g_clkrst_registers = dd::QueryIoMapping(ClkRstRegistersPhysicalAddress, ClkRstRegistersSize); + + struct ClkRstDefinition { + u32 clk_src_ofs; + u32 clk_en_ofs; + u32 rst_ofs; + u32 clk_en_index; + u32 rst_index; + u8 clk_src; + u8 clk_divisor; + }; + + constexpr inline const ClkRstDefinition Definitions[] = { + { CLK_RST_CONTROLLER_CLK_SOURCE_I2C1, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_ENB_I2C1_INDEX, CLK_RST_CONTROLLER_RST_I2C1_INDEX, 0x00 /* PLLP_OUT0 */, 0x04 }, + { CLK_RST_CONTROLLER_CLK_SOURCE_I2C2, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_ENB_I2C2_INDEX, CLK_RST_CONTROLLER_RST_I2C2_INDEX, 0x00 /* PLLP_OUT0 */, 0x04 }, + { CLK_RST_CONTROLLER_CLK_SOURCE_I2C3, CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_CONTROLLER_CLK_ENB_I2C3_INDEX, CLK_RST_CONTROLLER_RST_I2C3_INDEX, 0x00 /* PLLP_OUT0 */, 0x04 }, + { CLK_RST_CONTROLLER_CLK_SOURCE_I2C4, CLK_RST_CONTROLLER_CLK_OUT_ENB_V, CLK_RST_CONTROLLER_RST_DEVICES_V, CLK_RST_CONTROLLER_CLK_ENB_I2C4_INDEX, CLK_RST_CONTROLLER_RST_I2C4_INDEX, 0x00 /* PLLP_OUT0 */, 0x04 }, + { CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_ENB_I2C5_INDEX, CLK_RST_CONTROLLER_RST_I2C5_INDEX, 0x00 /* PLLP_OUT0 */, 0x04 }, + { CLK_RST_CONTROLLER_CLK_SOURCE_I2C6, CLK_RST_CONTROLLER_CLK_OUT_ENB_X, CLK_RST_CONTROLLER_RST_DEVICES_X, CLK_RST_CONTROLLER_CLK_ENB_I2C6_INDEX, CLK_RST_CONTROLLER_RST_I2C6_INDEX, 0x00 /* PLLP_OUT0 */, 0x04 }, + { CLK_RST_CONTROLLER_CLK_SOURCE_PWM, CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_CONTROLLER_CLK_ENB_PWM_INDEX, CLK_RST_CONTROLLER_RST_PWM_INDEX, 0x00 /* PLLP_OUT0 */, 0x10 }, + }; + + constexpr inline const struct { + const ClkRstDefinition *definition; + DeviceCode device_code; + pcv::Module pcv_module; + } ClkRstDefinitionMap[] = { + { std::addressof(Definitions[0]), i2c::DeviceCode_I2c1, pcv::Module_I2c1 }, + { std::addressof(Definitions[1]), i2c::DeviceCode_I2c2, pcv::Module_I2c2 }, + { std::addressof(Definitions[2]), i2c::DeviceCode_I2c3, pcv::Module_I2c3 }, + { std::addressof(Definitions[3]), i2c::DeviceCode_I2c4, pcv::Module_I2c4 }, + { std::addressof(Definitions[4]), i2c::DeviceCode_I2c5, pcv::Module_I2c5 }, + { std::addressof(Definitions[5]), i2c::DeviceCode_I2c6, pcv::Module_I2c6 }, + { std::addressof(Definitions[6]), pwm::DeviceCode_LcdBacklight, pcv::Module_Pwm }, + }; + + ALWAYS_INLINE const ClkRstDefinition *GetDefinition(DeviceCode device_code) { + const ClkRstDefinition *def = nullptr; + + for (const auto &entry : ClkRstDefinitionMap) { + if (entry.device_code == device_code) { + def = entry.definition; + break; + } + } + + AMS_ABORT_UNLESS(def != nullptr); + return def; + } + + ALWAYS_INLINE const ClkRstDefinition &GetDefinition(pcv::Module module) { + const ClkRstDefinition *def = nullptr; + + for (const auto &entry : ClkRstDefinitionMap) { + if (entry.pcv_module == module) { + def = entry.definition; + break; + } + } + + AMS_ABORT_UNLESS(def != nullptr); + return *def; + } + + ALWAYS_INLINE const ClkRstDefinition &GetDefinition(clkrst::ClkRstSession *session) { + const ClkRstDefinition *def = nullptr; + + for (const auto &entry : ClkRstDefinitionMap) { + if (session->_session == entry.definition) { + def = entry.definition; + break; + } + } + + AMS_ABORT_UNLESS(def != nullptr); + return *def; + } + + void SetResetEnabled(const ClkRstDefinition &def, bool en) { + /* Set or clear reset. */ + reg::ReadWrite(g_clkrst_registers + def.rst_ofs, (en ? 1u : 0u) << def.rst_index, 1u << def.rst_index); + } + + void SetClockEnabled(const ClkRstDefinition &def, bool en) { + /* Set or clear reset. */ + reg::ReadWrite(g_clkrst_registers + def.clk_en_ofs, (en ? 1u : 0u) << def.clk_en_index, 1u << def.clk_en_index); + } + + void SetClockRate(const ClkRstDefinition &def, u32 hz) { + /* Enable clock. */ + reg::ReadWrite(g_clkrst_registers + def.clk_en_ofs, 1u << def.clk_en_index, 1u << def.clk_en_index); + + /* Set the clock divisor. */ + reg::ReadWrite(g_clkrst_registers + def.clk_src_ofs, CLK_RST_REG_BITS_VALUE(CLK_SOURCE_CLK_DIVISOR, def.clk_divisor)); + + /* Wait for 2us for clock setting to take. */ + os::SleepThread(TimeSpan::FromMicroSeconds(2)); + + /* Set the clock source. */ + reg::ReadWrite(g_clkrst_registers + def.clk_src_ofs, CLK_RST_REG_BITS_VALUE(CLK_SOURCE_CLK_SOURCE, def.clk_src)); + + /* Wait for 2us for clock setting to take. */ + os::SleepThread(TimeSpan::FromMicroSeconds(2)); + } + + } + + namespace clkrst { + + void Initialize() { + /* ... */ + } + + void Finalize() { + /* ... */ + } + + Result OpenSession(ClkRstSession *out, DeviceCode device_code) { + /* Get the relevant definition. */ + out->_session = const_cast(GetDefinition(device_code)); + return ResultSuccess(); + } + + void CloseSession(ClkRstSession *session) { + /* Clear the session. */ + session->_session = nullptr; + } + + void SetResetAsserted(ClkRstSession *session) { + /* Assert reset. */ + SetResetEnabled(GetDefinition(session), true); + } + + void SetResetDeasserted(ClkRstSession *session) { + /* Assert reset. */ + SetResetEnabled(GetDefinition(session), false); + } + + void SetClockRate(ClkRstSession *session, u32 hz) { + /* Set the clock rate. */ + SetClockRate(GetDefinition(session), hz); + } + + void SetClockDisabled(ClkRstSession *session) { + AMS_ABORT("SetClockDisabled not implemented for boot system module"); + } + + } + + namespace pcv { + + void Initialize() { + /* ... */ + } + + void Finalize() { + /* ... */ + } + + Result SetClockEnabled(Module module, bool en) { + /* Set clock. */ + SetClockEnabled(GetDefinition(module), en); + + return ResultSuccess(); + } + + Result SetClockRate(Module module, ClockHz hz) { + /* Set the clock rate. */ + SetClockRate(GetDefinition(module), hz); + + return ResultSuccess(); + } + + Result SetReset(Module module, bool en) { + /* Set reset. */ + SetResetEnabled(GetDefinition(module), en); + + return ResultSuccess(); + } + + } + +} diff --git a/stratosphere/boot/source/api_overrides/regulator_api_for_boot.cpp b/stratosphere/boot/source/api_overrides/regulator_api_for_boot.cpp new file mode 100644 index 000000000..4db3e7bfa --- /dev/null +++ b/stratosphere/boot/source/api_overrides/regulator_api_for_boot.cpp @@ -0,0 +1,52 @@ +/* + * 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 . + */ +#include + +namespace ams::regulator { + + void Initialize() { + /* ... */ + } + + void Finalize() { + /* ... */ + } + + Result OpenSession(RegulatorSession *out, DeviceCode device_code) { + AMS_UNUSED(out, device_code); + return ResultSuccess(); + } + + void CloseSession(RegulatorSession *session) { + AMS_UNUSED(session); + } + + bool GetVoltageEnabled(RegulatorSession *session) { + AMS_UNUSED(session); + return true; + } + + Result SetVoltageEnabled(RegulatorSession *session, bool enabled) { + AMS_UNUSED(session, enabled); + return ResultSuccess(); + } + + Result SetVoltageValue(RegulatorSession *session, u32 micro_volts) { + AMS_UNUSED(session, micro_volts); + return ResultSuccess(); + } + +} diff --git a/stratosphere/boot/source/boot_battery_driver.cpp b/stratosphere/boot/source/boot_battery_driver.cpp deleted file mode 100644 index ae6a5b776..000000000 --- a/stratosphere/boot/source/boot_battery_driver.cpp +++ /dev/null @@ -1,299 +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 . - */ -#include -#include "boot_battery_driver.hpp" -#include "boot_calibration.hpp" -#include "boot_i2c_utils.hpp" - -namespace ams::boot { - - /* Include configuration into anonymous namespace. */ - namespace { - -#include "boot_battery_parameters.inc" - - const Max17050Parameters *GetBatteryParameters() { - const u32 battery_version = GetBatteryVersion(); - const u32 battery_vendor = GetBatteryVendor(); - - if (battery_version == 2) { - if (battery_vendor == 'M') { - return &Max17050Params2M; - } else { - return &Max17050Params2; - } - } else if (battery_version == 1) { - return &Max17050Params1; - } else { - switch (battery_vendor) { - case 'M': - return &Max17050ParamsM; - case 'R': - return &Max17050ParamsR; - case 'A': - default: - return &Max17050ParamsA; - } - } - } - - } - - Result BatteryDriver::Read(u8 addr, u16 *out) { - return ReadI2cRegister(this->i2c_session, reinterpret_cast(out), sizeof(*out), &addr, sizeof(addr)); - } - - Result BatteryDriver::Write(u8 addr, u16 val) { - return WriteI2cRegister(this->i2c_session, reinterpret_cast(&val), sizeof(val), &addr, sizeof(addr)); - } - - Result BatteryDriver::ReadWrite(u8 addr, u16 mask, u16 val) { - u16 cur_val; - R_TRY(this->Read(addr, &cur_val)); - - const u16 new_val = (cur_val & ~mask) | val; - R_TRY(this->Write(addr, new_val)); - - return ResultSuccess(); - } - - bool BatteryDriver::WriteValidate(u8 addr, u16 val) { - /* Nintendo doesn't seem to check errors when doing this? */ - /* It's probably okay, since the value does get validated. */ - /* That said, we will validate the read to avoid uninit data problems. */ - this->Write(addr, val); - svcSleepThread(3'000'000ul); - - u16 new_val; - return R_SUCCEEDED(this->Read(addr, &new_val)) && new_val == val; - } - - bool BatteryDriver::IsPowerOnReset() { - /* N doesn't check result... */ - u16 val = 0; - this->Read(Max17050Status, &val); - return (val & 0x0002) == 0x0002; - } - - Result BatteryDriver::LockVfSoc() { - return this->Write(Max17050SocVfAccess, 0x0000); - } - - Result BatteryDriver::UnlockVfSoc() { - return this->Write(Max17050SocVfAccess, 0x0080); - } - - Result BatteryDriver::LockModelTable() { - R_TRY(this->Write(Max17050ModelAccess0, 0x0000)); - R_TRY(this->Write(Max17050ModelAccess1, 0x0000)); - return ResultSuccess(); - } - - Result BatteryDriver::UnlockModelTable() { - R_TRY(this->Write(Max17050ModelAccess0, 0x0059)); - R_TRY(this->Write(Max17050ModelAccess1, 0x00C4)); - return ResultSuccess(); - } - - Result BatteryDriver::SetModelTable(const u16 *model_table) { - for (size_t i = 0; i < Max17050ModelChrTblSize; i++) { - R_TRY(this->Write(Max17050ModelChrTblStart + i, model_table[i])); - } - return ResultSuccess(); - } - - bool BatteryDriver::IsModelTableLocked() { - bool locked = true; - - u16 cur_val = 0; - for (size_t i = 0; i < Max17050ModelChrTblSize; i++) { - this->Read(Max17050ModelChrTblStart + i, &cur_val); - locked &= (cur_val == 0); - } - - return locked; - } - - bool BatteryDriver::IsModelTableSet(const u16 *model_table) { - bool set = true; - - u16 cur_val = 0; - for (size_t i = 0; i < Max17050ModelChrTblSize; i++) { - this->Read(Max17050ModelChrTblStart + i, &cur_val); - set &= (cur_val == model_table[i]); - } - - return set; - } - - Result BatteryDriver::InitializeBatteryParameters() { - const Max17050Parameters *params = GetBatteryParameters(); - - if (IsPowerOnReset()) { - /* Do initial config. */ - R_TRY(this->ReadWrite(Max17050MiscCfg, 0x8000, 0x8000)); - - svcSleepThread(500'000'000ul); - - R_TRY(this->Write(Max17050Config, 0x7210)); - R_TRY(this->Write(Max17050FilterCfg, 0x8784)); - R_TRY(this->Write(Max17050RelaxCfg, params->relaxcfg)); - R_TRY(this->Write(Max17050LearnCfg, 0x2603)); - R_TRY(this->Write(Max17050FullSocThr, params->fullsocthr)); - R_TRY(this->Write(Max17050IAvgEmpty, params->iavgempty)); - - /* Unlock model table, write model table. */ - do { - R_TRY(this->UnlockModelTable()); - R_TRY(this->SetModelTable(params->modeltbl)); - } while (!this->IsModelTableSet(params->modeltbl)); - - /* Lock model table. */ - size_t lock_i = 0; - while (true) { - lock_i++; - R_TRY(this->LockModelTable()); - - if (this->IsModelTableLocked()) { - break; - } - - if (lock_i >= 8) { - /* This is regarded as guaranteed success. */ - return ResultSuccess(); - } - } - - /* Write custom parameters. */ - while (!this->WriteValidate(Max17050RComp0, params->rcomp0)) { /* ... */ } - while (!this->WriteValidate(Max17050TempCo, params->tempco)) { /* ... */ } - - R_TRY(this->Write(Max17050IChgTerm, params->ichgterm)); - R_TRY(this->Write(Max17050TGain, params->tgain)); - R_TRY(this->Write(Max17050TOff, params->toff)); - - while (!this->WriteValidate(Max17050VEmpty, params->vempty)) { /* ... */ } - while (!this->WriteValidate(Max17050QResidual00, params->qresidual00)) { /* ... */ } - while (!this->WriteValidate(Max17050QResidual10, params->qresidual10)) { /* ... */ } - while (!this->WriteValidate(Max17050QResidual20, params->qresidual20)) { /* ... */ } - while (!this->WriteValidate(Max17050QResidual30, params->qresidual30)) { /* ... */ } - - - /* Write full capacity parameters. */ - while (!this->WriteValidate(Max17050FullCap, params->fullcap)) { /* ... */ } - R_TRY(this->Write(Max17050DesignCap, params->vffullcap)); - while (!this->WriteValidate(Max17050FullCapNom, params->vffullcap)) { /* ... */ } - - svcSleepThread(350'000'000ul); - - /* Write VFSOC to VFSOC 0. */ - u16 vfsoc, qh; - { - R_TRY(this->Read(Max17050SocVf, &vfsoc)); - R_TRY(this->UnlockVfSoc()); - R_TRY(this->Write(Max17050SocVf0, vfsoc)); - R_TRY(this->Read(Max17050Qh, &qh)); - R_TRY(this->Write(Max17050Qh0, qh)); - R_TRY(this->LockVfSoc()); - } - - /* Write cycles. */ - while (!this->WriteValidate(Max17050Cycles, 0x0060)) { /* ... */ } - - /* Load new capacity parameters. */ - const u16 remcap = static_cast((vfsoc * params->vffullcap) / 0x6400); - const u16 repcap = static_cast(remcap * (params->fullcap / params->vffullcap)); - const u16 dqacc = params->vffullcap / 0x10; - while (!this->WriteValidate(Max17050RemCapMix, remcap)) { /* ... */ } - while (!this->WriteValidate(Max17050RemCapRep, repcap)) { /* ... */ } - while (!this->WriteValidate(Max17050DPAcc, 0x0C80)) { /* ... */ } - while (!this->WriteValidate(Max17050DQAcc, dqacc)) { /* ... */ } - while (!this->WriteValidate(Max17050FullCap, params->fullcap)) { /* ... */ } - R_TRY(this->Write(Max17050DesignCap, params->vffullcap)); - while (!this->WriteValidate(Max17050FullCapNom, params->vffullcap)) { /* ... */ } - R_TRY(this->Write(Max17050SocRep, vfsoc)); - - /* Finish initialization. */ - { - u16 status; - R_TRY(this->Read(Max17050Status, &status)); - while (!this->WriteValidate(Max17050Status, status & 0xFFFD)) { /* ... */ } - } - R_TRY(this->Write(Max17050CGain, 0x7FFF)); - } - - return ResultSuccess(); - } - - Result BatteryDriver::IsBatteryRemoved(bool *out) { - /* N doesn't check result, but we will. */ - u16 val = 0; - R_TRY(this->Read(Max17050Status, &val)); - *out = (val & 0x0008) == 0x0008; - return ResultSuccess(); - } - - Result BatteryDriver::GetTemperature(double *out) { - u16 val = 0; - R_TRY(this->Read(Max17050Temperature, &val)); - *out = static_cast(val) * double(0.00390625); - return ResultSuccess(); - } - - Result BatteryDriver::GetAverageVCell(u32 *out) { - u16 val = 0; - R_TRY(this->Read(Max17050AverageVCell, &val)); - *out = (625 * u32(val >> 3)) / 1000; - return ResultSuccess(); - } - - Result BatteryDriver::GetSocRep(double *out) { - u16 val = 0; - R_TRY(this->Read(Max17050SocRep, &val)); - *out = static_cast(val) * double(0.00390625); - return ResultSuccess(); - } - - Result BatteryDriver::GetBatteryPercentage(size_t *out) { - double raw_charge; - R_TRY(this->GetSocRep(&raw_charge)); - int converted_percentage = (((raw_charge - 3.93359375) * 98.0) / 94.2304688) + 2.0; - if (converted_percentage < 1) { - *out = 1; - } else if (converted_percentage > 100) { - *out = 100; - } else { - *out = static_cast(converted_percentage); - } - return ResultSuccess(); - } - - Result BatteryDriver::SetShutdownTimer() { - return this->Write(Max17050ShdnTimer, 0xE000); - } - - Result BatteryDriver::GetShutdownEnabled(bool *out) { - u16 val = 0; - R_TRY(this->Read(Max17050Config, &val)); - *out = (val & 0x0040) != 0; - return ResultSuccess(); - } - - Result BatteryDriver::SetShutdownEnabled(bool enabled) { - return this->ReadWrite(Max17050Config, 0x0040, enabled ? 0x0040 : 0x0000); - } - -} diff --git a/stratosphere/boot/source/boot_battery_driver.hpp b/stratosphere/boot/source/boot_battery_driver.hpp index c5e6ab527..c1c889a29 100644 --- a/stratosphere/boot/source/boot_battery_driver.hpp +++ b/stratosphere/boot/source/boot_battery_driver.hpp @@ -14,48 +14,60 @@ * along with this program. If not, see . */ #pragma once -#include "i2c/driver/i2c_api.hpp" +#include namespace ams::boot { class BatteryDriver { private: - i2c::driver::Session i2c_session; + powctl::Session battery_session; public: - BatteryDriver() { - i2c::driver::Initialize(); - i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max17050); + BatteryDriver() : battery_session() { + R_ABORT_UNLESS(powctl::OpenSession(std::addressof(this->battery_session), powctl::DeviceCode_Max17050, ddsf::AccessMode_ReadWrite)); } ~BatteryDriver() { - i2c::driver::CloseSession(this->i2c_session); - i2c::driver::Finalize(); + powctl::CloseSession(this->battery_session); } - private: - Result Read(u8 addr, u16 *out_data); - Result Write(u8 addr, u16 val); - Result ReadWrite(u8 addr, u16 mask, u16 val); - bool WriteValidate(u8 addr, u16 val); - - bool IsPowerOnReset(); - Result LockVfSoc(); - Result UnlockVfSoc(); - Result LockModelTable(); - Result UnlockModelTable(); - bool IsModelTableLocked(); - Result SetModelTable(const u16 *model_table); - bool IsModelTableSet(const u16 *model_table); - public: - Result InitializeBatteryParameters(); - Result IsBatteryRemoved(bool *out); - Result GetTemperature(double *out); - Result GetAverageVCell(u32 *out); - Result GetSocRep(double *out); - Result GetBatteryPercentage(size_t *out); - Result SetShutdownTimer(); - Result GetShutdownEnabled(bool *out); - Result SetShutdownEnabled(bool enabled); + Result IsBatteryRemoved(bool *out) { + bool present; + R_TRY(powctl::IsBatteryPresent(std::addressof(present), this->battery_session)); + + return present == false; + } + + Result GetSocRep(float *out) { + return powctl::GetBatterySocRep(out, this->battery_session); + } + + Result GetAverageVCell(int *out) { + return powctl::GetBatteryAverageVCell(out, this->battery_session); + } + + Result GetOpenCircuitVoltage(int *out) { + return powctl::GetBatteryOpenCircuitVoltage(out, this->battery_session); + } + + Result GetAverageCurrent(int *out) { + return powctl::GetBatteryAverageCurrent(out, this->battery_session); + } + + Result GetCurrent(int *out) { + return powctl::GetBatteryCurrent(out, this->battery_session); + } + + Result GetTemperature(float *out) { + return powctl::GetBatteryTemperature(out, this->battery_session); + } + + Result IsI2cShutdownEnabled(bool *out) { + return powctl::IsBatteryI2cShutdownEnabled(out, this->battery_session); + } + + Result SetI2cShutdownEnabled(bool en) { + return powctl::SetBatteryI2cShutdownEnabled(this->battery_session, en); + } }; } diff --git a/stratosphere/boot/source/boot_battery_icons.cpp b/stratosphere/boot/source/boot_battery_icons.cpp index af7b0e425..f580d0f35 100644 --- a/stratosphere/boot/source/boot_battery_icons.cpp +++ b/stratosphere/boot/source/boot_battery_icons.cpp @@ -22,9 +22,9 @@ namespace ams::boot { namespace { /* Pull in icon definitions. */ -#include "boot_battery_icon_low.inc" -#include "boot_battery_icon_charging.inc" -#include "boot_battery_icon_charging_red.inc" + #include "boot_battery_icon_low.inc" + #include "boot_battery_icon_charging.inc" + #include "boot_battery_icon_charging_red.inc" /* Helpers. */ void FillBatteryMeter(u32 *icon, const size_t icon_w, const size_t icon_h, const size_t meter_x, const size_t meter_y, const size_t meter_w, const size_t meter_h, const size_t fill_w) { @@ -54,12 +54,12 @@ namespace ams::boot { { /* Low battery icon is shown for 5 seconds. */ ShowDisplay(LowBatteryX, LowBatteryY, LowBatteryW, LowBatteryH, LowBattery); - svcSleepThread(5'000'000'000ul); + os::SleepThread(TimeSpan::FromSeconds(5)); } FinalizeDisplay(); } - void StartShowChargingIcon(size_t battery_percentage, bool wait) { + void StartShowChargingIcon(int battery_percentage, bool wait) { const bool is_red = battery_percentage <= 15; const size_t IconX = is_red ? ChargingRedBatteryX : ChargingBatteryX; @@ -70,7 +70,7 @@ namespace ams::boot { const size_t IconMeterY = is_red ? ChargingRedBatteryMeterY : ChargingBatteryMeterY; const size_t IconMeterW = is_red ? ChargingRedBatteryMeterW : ChargingBatteryMeterW; const size_t IconMeterH = is_red ? ChargingRedBatteryMeterH : ChargingBatteryMeterH; - const size_t MeterFillW = static_cast(IconMeterW * (1.0 - (0.0404 + 0.0096 * battery_percentage)) + 0.5); + const size_t MeterFillW = static_cast(IconMeterW * (1.0 - (0.0404 + 0.0096 * static_cast(battery_percentage))) + 0.5); /* Create stack buffer, copy icon into it, draw fill meter, draw. */ { @@ -84,7 +84,7 @@ namespace ams::boot { /* Wait for 2 seconds if we're supposed to. */ if (wait) { - svcSleepThread(2'000'000'000ul); + os::SleepThread(TimeSpan::FromSeconds(2)); } } diff --git a/stratosphere/boot/source/boot_battery_icons.hpp b/stratosphere/boot/source/boot_battery_icons.hpp index 8cd180fff..ee8b0bcf0 100644 --- a/stratosphere/boot/source/boot_battery_icons.hpp +++ b/stratosphere/boot/source/boot_battery_icons.hpp @@ -15,12 +15,21 @@ */ #pragma once #include +#include "boot_display.hpp" namespace ams::boot { /* Battery Display utilities. */ void ShowLowBatteryIcon(); - void StartShowChargingIcon(size_t battery_percentage, bool wait); + void StartShowChargingIcon(int battery_percentage, bool wait); void EndShowChargingIcon(); + inline void StartShowChargingIcon(int battery_percentage) { + return StartShowChargingIcon(battery_percentage, true); + } + + inline void StartShowLowBatteryChargingIcon() { + return StartShowChargingIcon(1, false); + } + } diff --git a/stratosphere/boot/source/boot_battery_parameters.inc b/stratosphere/boot/source/boot_battery_parameters.inc deleted file mode 100644 index fd7ca3586..000000000 --- a/stratosphere/boot/source/boot_battery_parameters.inc +++ /dev/null @@ -1,283 +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 . - */ - -constexpr u8 Max17050Status = 0x00; -constexpr u8 Max17050VAlrtThreshold = 0x01; -constexpr u8 Max17050TAlrtThreshold = 0x02; -constexpr u8 Max17050SocAlrtThreshold = 0x03; -constexpr u8 Max17050AtRate = 0x04; -constexpr u8 Max17050RemCapRep = 0x05; -constexpr u8 Max17050SocRep = 0x06; -constexpr u8 Max17050Age = 0x07; -constexpr u8 Max17050Temperature = 0x08; -constexpr u8 Max17050VCell = 0x09; -constexpr u8 Max17050Current = 0x0A; -constexpr u8 Max17050AverageCurrent = 0x0B; - -constexpr u8 Max17050SocMix = 0x0D; -constexpr u8 Max17050SocAv = 0x0E; -constexpr u8 Max17050RemCapMix = 0x0F; -constexpr u8 Max17050FullCap = 0x10; -constexpr u8 Max17050Tte = 0x11; -constexpr u8 Max17050QResidual00 = 0x12; -constexpr u8 Max17050FullSocThr = 0x13; - - -constexpr u8 Max17050AverageTemp = 0x16; -constexpr u8 Max17050Cycles = 0x17; -constexpr u8 Max17050DesignCap = 0x18; -constexpr u8 Max17050AverageVCell = 0x19; -constexpr u8 Max17050MaxMinTemp = 0x1A; -constexpr u8 Max17050MaxMinVoltage = 0x1B; -constexpr u8 Max17050MaxMinCurrent = 0x1C; -constexpr u8 Max17050Config = 0x1D; -constexpr u8 Max17050IChgTerm = 0x1E; -constexpr u8 Max17050RemCapAv = 0x1F; - -constexpr u8 Max17050Version = 0x21; -constexpr u8 Max17050QResidual10 = 0x22; -constexpr u8 Max17050FullCapNom = 0x23; -constexpr u8 Max17050TempNom = 0x24; -constexpr u8 Max17050TempLim = 0x25; - -constexpr u8 Max17050Ain = 0x27; -constexpr u8 Max17050LearnCfg = 0x28; -constexpr u8 Max17050FilterCfg = 0x29; -constexpr u8 Max17050RelaxCfg = 0x2A; -constexpr u8 Max17050MiscCfg = 0x2B; -constexpr u8 Max17050TGain = 0x2C; -constexpr u8 Max17050TOff = 0x2D; -constexpr u8 Max17050CGain = 0x2E; -constexpr u8 Max17050COff = 0x2F; - - -constexpr u8 Max17050QResidual20 = 0x32; - - - -constexpr u8 Max17050IAvgEmpty = 0x36; -constexpr u8 Max17050FCtc = 0x37; -constexpr u8 Max17050RComp0 = 0x38; -constexpr u8 Max17050TempCo = 0x39; -constexpr u8 Max17050VEmpty = 0x3A; - - -constexpr u8 Max17050FStat = 0x3D; -constexpr u8 Max17050Timer = 0x3E; -constexpr u8 Max17050ShdnTimer = 0x3F; - - -constexpr u8 Max17050QResidual30 = 0x42; - - -constexpr u8 Max17050DQAcc = 0x45; -constexpr u8 Max17050DPAcc = 0x46; - -constexpr u8 Max17050SocVf0 = 0x48; - -constexpr u8 Max17050Qh0 = 0x4C; -constexpr u8 Max17050Qh = 0x4D; - -constexpr u8 Max17050SocVfAccess = 0x60; - -constexpr u8 Max17050ModelAccess0 = 0x62; -constexpr u8 Max17050ModelAccess1 = 0x63; - -constexpr u8 Max17050ModelChrTblStart = 0x80; -constexpr u8 Max17050ModelChrTblEnd = 0xB0; - - -constexpr u8 Max17050VFocV = 0xFB; -constexpr u8 Max17050SocVf = 0xFF; - -constexpr size_t Max17050ModelChrTblSize = Max17050ModelChrTblEnd - Max17050ModelChrTblStart; - -struct Max17050Parameters { - u16 relaxcfg; - u16 rcomp0; - u16 tempco; - u16 ichgterm; - u16 tgain; - u16 toff; - u16 vempty; - u16 qresidual00; - u16 qresidual10; - u16 qresidual20; - u16 qresidual30; - u16 fullcap; - u16 vffullcap; - u16 modeltbl[Max17050ModelChrTblSize]; - u16 fullsocthr; - u16 iavgempty; -}; - -static_assert(sizeof(Max17050Parameters) == 0x7E, "Max17050Parameters definition!"); - -constexpr Max17050Parameters Max17050ParamsA = { - 0x203B, /* relaxcfg */ - 0x0053, /* rcomp0 */ - 0x1C22, /* tempco */ - 0x0333, /* ichgterm */ - 0xE1F6, /* tgain */ - 0x2BF2, /* toff */ - 0xA05F, /* vempty */ - 0x5786, /* qresidual00 */ - 0x3184, /* qresidual10 */ - 0x1E00, /* qresidual20 */ - 0x1602, /* qresidual30 */ - 0x2476, /* fullcap */ - 0x2476, /* vffullcap */ - { /* modeltbl */ - 0x9FF0, 0xAD30, 0xB5D0, 0xB9C0, 0xBAD0, 0xBBE0, 0xBC30, 0xBC90, - 0xBCE0, 0xBD40, 0xBE70, 0xC0E0, 0xC4E0, 0xC890, 0xCC90, 0xD0F0, - 0x0170, 0x0480, 0x0590, 0x0BE0, 0x0A00, 0x3C00, 0x3810, 0x3A00, - 0x3A30, 0x19F0, 0x0EF0, 0x0AF0, 0x0BD0, 0x07F0, 0x06F0, 0x06F0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - 0x5F00, /* fullsocthr */ - 0x1D2A /* iavgempty */ -}; - -constexpr Max17050Parameters Max17050ParamsM = { - 0x203B, /* relaxcfg */ - 0x0085, /* rcomp0 */ - 0x1625, /* tempco */ - 0x0333, /* ichgterm */ - 0xE1F6, /* tgain */ - 0x2BF2, /* toff */ - 0xA05F, /* vempty */ - 0x3100, /* qresidual00 */ - 0x1B00, /* qresidual10 */ - 0x1000, /* qresidual20 */ - 0x0C81, /* qresidual30 */ - 0x227A, /* fullcap */ - 0x227A, /* vffullcap */ - { /* modeltbl */ - 0xA340, 0xB840, 0xB900, 0xBB70, 0xBC90, 0xBD20, 0xBDC0, 0xBEA0, - 0xBF70, 0xC030, 0xC210, 0xC3F0, 0xC800, 0xC9E0, 0xCCA0, 0xD090, - 0x0160, 0x3800, 0x0800, 0x1E00, 0x2550, 0x3060, 0x15D0, 0x1810, - 0x1490, 0x0B80, 0x0BF0, 0x0AF0, 0x0CB0, 0x06F0, 0x09D0, 0x09D0, - 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, - 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, - }, - 0x5F00, /* fullsocthr */ - 0x1D2A /* iavgempty */ -}; - -constexpr Max17050Parameters Max17050ParamsR = { - 0x203B, /* relaxcfg */ - 0x0048, /* rcomp0 */ - 0x2034, /* tempco */ - 0x0333, /* ichgterm */ - 0xE1F6, /* tgain */ - 0x2BF2, /* toff */ - 0xA05F, /* vempty */ - 0x5A00, /* qresidual00 */ - 0x3B00, /* qresidual10 */ - 0x0F80, /* qresidual20 */ - 0x0B02, /* qresidual30 */ - 0x2466, /* fullcap */ - 0x2466, /* vffullcap */ - { /* modeltbl */ - 0x9C50, 0xAD90, 0xB270, 0xB6A0, 0xB8F0, 0xBB10, 0xBC00, 0xBD00, - 0xBD70, 0xBE70, 0xBF50, 0xC1F0, 0xC380, 0xC590, 0xC8E0, 0xD0B0, - 0x00D0, 0x0150, 0x0300, 0x0D00, 0x0E00, 0x1900, 0x2AC0, 0x2830, - 0x1760, 0x18F0, 0x0DF0, 0x0BC0, 0x0DF0, 0x0BF0, 0x06F0, 0x06F0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - 0x5F00, /* fullsocthr */ - 0x1D2A /* iavgempty */ -}; - -constexpr Max17050Parameters Max17050Params1 = { - 0x203B, /* relaxcfg */ - 0x0040, /* rcomp0 */ - 0x1624, /* tempco */ - 0x0333, /* ichgterm */ - 0xE1F6, /* tgain */ - 0x2BF2, /* toff */ - 0xA05F, /* vempty */ - 0x4690, /* qresidual00 */ - 0x2605, /* qresidual10 */ - 0x1605, /* qresidual20 */ - 0x0F05, /* qresidual30 */ - 0x1AE4, /* fullcap */ - 0x1AE4, /* vffullcap */ - { /* modeltbl */ - 0x8B50, 0x9C20, 0xACF0, 0xB160, 0xB3A0, 0xB5B0, 0xB950, 0xBBE0, - 0xBDC0, 0xBEF0, 0xC140, 0xC250, 0xC600, 0xC960, 0xCCE0, 0xD060, - 0x0070, 0x00F0, 0x0440, 0x0400, 0x0500, 0x0400, 0x0D00, 0x3270, - 0x0FB0, 0x0AF0, 0x10F0, 0x0CE0, 0x09E0, 0x07F0, 0x06F0, 0x06F0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - 0x5F00, /* fullsocthr */ - 0x1584 /* iavgempty */ -}; - -constexpr Max17050Parameters Max17050Params2 = { - 0x203B, /* relaxcfg */ - 0x004A, /* rcomp0 */ - 0x1D23, /* tempco */ - 0x0333, /* ichgterm */ - 0xE1F6, /* tgain */ - 0x2BF2, /* toff */ - 0xA05F, /* vempty */ - 0x4000, /* qresidual00 */ - 0x1E80, /* qresidual10 */ - 0x0D83, /* qresidual20 */ - 0x0783, /* qresidual30 */ - 0x1C20, /* fullcap */ - 0x1C20, /* vffullcap */ - { /* modeltbl */ - 0x8040, 0x9A30, 0xB430, 0xB770, 0xBAB0, 0xBBC0, 0xBD00, 0xBE50, - 0xBF70, 0xC0D0, 0xC300, 0xC590, 0xC960, 0xCD40, 0xD1F0, 0xD5C0, - 0x0040, 0x0060, 0x0510, 0x0D30, 0x16C0, 0x2160, 0x1380, 0x1A10, - 0x0EC0, 0x0CE0, 0x08F0, 0x0940, 0x0920, 0x06F0, 0x06C0, 0x06C0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - 0x5500, /* fullsocthr */ - 0x1680 /* iavgempty */ -}; - -constexpr Max17050Parameters Max17050Params2M = { - 0x203B, /* relaxcfg */ - 0x0049, /* rcomp0 */ - 0x222A, /* tempco */ - 0x0333, /* ichgterm */ - 0xE1F6, /* tgain */ - 0x2BF2, /* toff */ - 0xA05F, /* vempty */ - 0x4F00, /* qresidual00 */ - 0x2680, /* qresidual10 */ - 0x1205, /* qresidual20 */ - 0x0C87, /* qresidual30 */ - 0x1C68, /* fullcap */ - 0x1C68, /* vffullcap */ - { /* modeltbl */ - 0x8E40, 0xB570, 0xB8F0, 0xBB00, 0xBC20, 0xBCC0, 0xBE30, 0xBFE0, - 0xC200, 0xC400, 0xC720, 0xCB50, 0xCF00, 0xD100, 0xD480, 0xD5C0, - 0x00C0, 0x0C00, 0x0A10, 0x1800, 0x2C00, 0x1C10, 0x12D0, 0x09F0, - 0x0AF0, 0x0850, 0x09F0, 0x06F0, 0x06B0, 0x07E0, 0x01D0, 0x01D0, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, - }, - 0x5500, /* fullsocthr */ - 0x16B9 /* iavgempty */ -}; diff --git a/stratosphere/boot/source/boot_boot_reason.cpp b/stratosphere/boot/source/boot_boot_reason.cpp index 0c255434f..f6de42059 100644 --- a/stratosphere/boot/source/boot_boot_reason.cpp +++ b/stratosphere/boot/source/boot_boot_reason.cpp @@ -23,29 +23,29 @@ namespace ams::boot { namespace { /* Globals. */ - u32 g_boot_reason = 0; - bool g_detected_boot_reason = false; + constinit spl::BootReason g_boot_reason = spl::BootReason_Unknown; + constinit bool g_detected_boot_reason = false; /* Helpers. */ - u32 MakeBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) { + spl::BootReason DetectBootReason(u32 power_intr, u8 rtc_intr, u8 nv_erc, bool ac_ok) { if (power_intr & 0x08) { - return 2; + return spl::BootReason_OnKey; } if (rtc_intr & 0x02) { - return 3; + return spl::BootReason_RtcAlarm1; } if (power_intr & 0x80) { - return 1; + return spl::BootReason_AcOk; } if (rtc_intr & 0x04) { if (nv_erc != 0x80 && !spl::IsRecoveryBoot()) { - return 4; + return spl::BootReason_RtcAlarm2; } } if ((nv_erc & 0x40) && ac_ok) { - return 1; + return spl::BootReason_AcOk; } - return 0; + return spl::BootReason_Unknown; } } @@ -60,37 +60,38 @@ namespace ams::boot { /* Get values from PMIC. */ { PmicDriver pmic_driver; - R_ABORT_UNLESS(pmic_driver.GetPowerIntr(&power_intr)); - R_ABORT_UNLESS(pmic_driver.GetNvErc(&nv_erc)); - R_ABORT_UNLESS(pmic_driver.GetAcOk(&ac_ok)); + R_ABORT_UNLESS(pmic_driver.GetOnOffIrq(std::addressof(power_intr))); + R_ABORT_UNLESS(pmic_driver.GetNvErc(std::addressof(nv_erc))); + R_ABORT_UNLESS(pmic_driver.GetAcOk(std::addressof(ac_ok))); } /* Get values from RTC. */ { RtcDriver rtc_driver; - R_ABORT_UNLESS(rtc_driver.GetRtcIntr(&rtc_intr)); - R_ABORT_UNLESS(rtc_driver.GetRtcIntrM(&rtc_intr_m)); + R_ABORT_UNLESS(rtc_driver.GetRtcIntr(std::addressof(rtc_intr))); + R_ABORT_UNLESS(rtc_driver.GetRtcIntrM(std::addressof(rtc_intr_m))); } /* Set global derived boot reason. */ - g_boot_reason = MakeBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok); + g_boot_reason = DetectBootReason(power_intr, rtc_intr & ~rtc_intr_m, nv_erc, ac_ok); /* Set boot reason for SPL. */ if (hos::GetVersion() >= hos::Version_3_0_0) { + /* Create the boot reason value. */ spl::BootReasonValue boot_reason_value = {}; - boot_reason_value.power_intr = power_intr; boot_reason_value.rtc_intr = rtc_intr & ~rtc_intr_m; boot_reason_value.nv_erc = nv_erc; boot_reason_value.boot_reason = g_boot_reason; + /* Set the boot reason value. */ R_ABORT_UNLESS(spl::SetBootReason(boot_reason_value)); } g_detected_boot_reason = true; } - u32 GetBootReason() { + spl::BootReason GetBootReason() { AMS_ABORT_UNLESS(g_detected_boot_reason); return g_boot_reason; } diff --git a/stratosphere/boot/source/boot_boot_reason.hpp b/stratosphere/boot/source/boot_boot_reason.hpp index f2f307ddb..6f39648b6 100644 --- a/stratosphere/boot/source/boot_boot_reason.hpp +++ b/stratosphere/boot/source/boot_boot_reason.hpp @@ -20,6 +20,6 @@ namespace ams::boot { /* Boot Reason utilities. */ void DetectBootReason(); - u32 GetBootReason(); + spl::BootReason GetBootReason(); } diff --git a/stratosphere/boot/source/boot_bq24193_charger.hpp b/stratosphere/boot/source/boot_bq24193_charger.hpp deleted file mode 100644 index 7a59b3f99..000000000 --- a/stratosphere/boot/source/boot_bq24193_charger.hpp +++ /dev/null @@ -1,142 +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 . - */ -#pragma once -#include - -namespace ams::boot::bq24193 { - - constexpr u8 InputSourceControl = 0x00; - constexpr u8 PowerOnConfiguration = 0x01; - constexpr u8 ChargeCurrentControl = 0x02; - constexpr u8 PreChargeTerminationCurrentControl = 0x03; - constexpr u8 ChargeVoltageControl = 0x04; - constexpr u8 ChargeTerminationTimerControl = 0x05; - constexpr u8 IrCompensationThermalRegulationControl = 0x06; - constexpr u8 MiscOperationControl = 0x07; - constexpr u8 SystemStatus = 0x08; - constexpr u8 Fault = 0x09; - constexpr u8 VendorPartRevisionStatus = 0x0A; - - enum ChargerConfiguration : u8 { - ChargerConfiguration_ChargeDisable = (0 << 4), - ChargerConfiguration_ChargeBattery = (1 << 4), - ChargerConfiguration_Otg = (2 << 4), - }; - - constexpr u32 ChargeVoltageLimitMin = 3504; - constexpr u32 ChargeVoltageLimitMax = 4208; - - inline u8 EncodeChargeVoltageLimit(u32 voltage) { - AMS_ABORT_UNLESS(voltage >= ChargeVoltageLimitMin); - AMS_ABORT_UNLESS(voltage <= ChargeVoltageLimitMax); - - voltage -= ChargeVoltageLimitMin; - voltage >>= 4; - return static_cast(voltage << 2); - } - - inline u32 DecodeChargeVoltageLimit(u8 reg) { - return ChargeVoltageLimitMin + (static_cast(reg & 0xFC) << 2); - } - - constexpr u32 FastChargeCurrentLimitMin = 512; - constexpr u32 FastChargeCurrentLimitMax = 4544; - - inline u8 EncodeFastChargeCurrentLimit(u32 current) { - AMS_ABORT_UNLESS(current >= FastChargeCurrentLimitMin); - AMS_ABORT_UNLESS(current <= FastChargeCurrentLimitMax); - - current -= FastChargeCurrentLimitMin; - current >>= 6; - return static_cast(current << 2); - } - - inline u32 DecodeFastChargeCurrentLimit(u8 reg) { - return FastChargeCurrentLimitMin + (static_cast(reg & 0xFC) << 4); - } - - enum InputCurrentLimit : u8 { - InputCurrentLimit_100mA = 0, - InputCurrentLimit_150mA = 1, - InputCurrentLimit_500mA = 2, - InputCurrentLimit_900mA = 3, - InputCurrentLimit_1200mA = 4, - InputCurrentLimit_1500mA = 5, - InputCurrentLimit_2000mA = 6, - InputCurrentLimit_3000mA = 7, - }; - - constexpr u32 PreChargeCurrentLimitMin = 128; - constexpr u32 PreChargeCurrentLimitMax = 2048; - - inline u8 EncodePreChargeCurrentLimit(u32 current) { - AMS_ABORT_UNLESS(current >= PreChargeCurrentLimitMin); - AMS_ABORT_UNLESS(current <= PreChargeCurrentLimitMax); - - current -= PreChargeCurrentLimitMin; - current >>= 7; - return static_cast(current << 4); - } - - inline u32 DecodePreChargeCurrentLimit(u8 reg) { - return PreChargeCurrentLimitMin + (static_cast(reg & 0xF0) << 3); - } - - constexpr u32 TerminationCurrentLimitMin = 128; - constexpr u32 TerminationCurrentLimitMax = 2048; - - inline u8 EncodeTerminationCurrentLimit(u32 current) { - AMS_ABORT_UNLESS(current >= TerminationCurrentLimitMin); - AMS_ABORT_UNLESS(current <= TerminationCurrentLimitMax); - - current -= TerminationCurrentLimitMin; - current >>= 7; - return static_cast(current); - } - - inline u32 DecodeTerminationCurrentLimit(u8 reg) { - return TerminationCurrentLimitMin + (static_cast(reg & 0xF) << 7); - } - - constexpr u32 MinimumSystemVoltageLimitMin = 3000; - constexpr u32 MinimumSystemVoltageLimitMax = 3700; - - inline u8 EncodeMinimumSystemVoltageLimit(u32 voltage) { - AMS_ABORT_UNLESS(voltage >= MinimumSystemVoltageLimitMin); - AMS_ABORT_UNLESS(voltage <= MinimumSystemVoltageLimitMax); - - voltage -= MinimumSystemVoltageLimitMin; - voltage /= 100; - return static_cast(voltage << 1); - } - - inline u32 DecodeMinimumSystemVoltageLimit(u8 reg) { - return MinimumSystemVoltageLimitMin + (static_cast(reg & 0x0E) * 50); - } - - enum WatchdogTimerSetting : u8 { - WatchdogTimerSetting_Disabled = (0 << 4), - WatchdogTimerSetting_40s = (1 << 4), - WatchdogTimerSetting_80s = (2 << 4), - WatchdogTimerSetting_160s = (3 << 4), - }; - - enum BoostModeCurrentLimit : u8 { - BoostModeCurrentLimit_500mA = 0, - BoostModeCurrentLimit_1300mA = 1, - }; - -} diff --git a/stratosphere/boot/source/boot_calibration.cpp b/stratosphere/boot/source/boot_calibration.cpp deleted file mode 100644 index 6257609f3..000000000 --- a/stratosphere/boot/source/boot_calibration.cpp +++ /dev/null @@ -1,103 +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 . - */ -#include -#include "boot_calibration.hpp" - -namespace ams::boot { - - namespace { - - /* Convenience definitions. */ - constexpr size_t BatteryLotOffset = 0x2CE0; - constexpr size_t BatteryLotSize = 0x20; - constexpr size_t BatteryVersionOffset = 0x4310; - constexpr size_t BatteryVersionSize = 0x10; - - constexpr u32 DefaultBatteryVendor = static_cast('A'); - constexpr u32 DefaultBatteryVersion = 0; - - /* Helpers. */ - constexpr u16 GetCrc16(const void *data, size_t size) { - constexpr u16 s_crc_table[0x10] = { - 0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, - 0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400 - }; - - AMS_ABORT_UNLESS(data != nullptr); - - u16 crc16 = 0x55AA; - const u8 *data_u8 = reinterpret_cast(data); - for (size_t i = 0; i < size; i++) { - crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[data_u8[i] & 0xF]); - crc16 = (crc16 >> 4) ^ (s_crc_table[crc16 & 0xF]) ^ (s_crc_table[(data_u8[i] >> 4) & 0xF]); - } - return crc16; - } - - Result ValidateCalibrationCrc16(const void *data, size_t size) { - const u8 *data_u8 = reinterpret_cast(data); - const bool crc_valid = GetCrc16(data, size - sizeof(u16)) == *(reinterpret_cast(&data_u8[size - sizeof(u16)])); - R_UNLESS(crc_valid, cal::ResultCalibrationDataCrcError()); - return ResultSuccess(); - } - - Result GetBatteryVendorImpl(u32 *vendor) { - FsStorage s; - R_TRY(fsOpenBisStorage(&s, FsBisPartitionId_CalibrationBinary)); - ON_SCOPE_EXIT { fsStorageClose(&s); }; - - u8 battery_lot[BatteryLotSize]; - R_TRY(fsStorageRead(&s, BatteryLotOffset, battery_lot, sizeof(battery_lot))); - - R_TRY(ValidateCalibrationCrc16(battery_lot, sizeof(battery_lot))); - - *vendor = battery_lot[7]; - return ResultSuccess(); - } - - Result GetBatteryVersionImpl(u32 *version) { - FsStorage s; - R_TRY(fsOpenBisStorage(&s, FsBisPartitionId_CalibrationBinary)); - ON_SCOPE_EXIT { fsStorageClose(&s); }; - - u8 battery_version[BatteryVersionSize]; - R_TRY(fsStorageRead(&s, BatteryVersionOffset, battery_version, sizeof(battery_version))); - - R_TRY(ValidateCalibrationCrc16(battery_version, sizeof(battery_version))); - - *version = battery_version[0]; - return ResultSuccess(); - } - - } - - u32 GetBatteryVendor() { - u32 vendor; - if (R_FAILED(GetBatteryVendorImpl(&vendor))) { - return DefaultBatteryVendor; - } - return vendor; - } - - u32 GetBatteryVersion() { - u32 version; - if (R_FAILED(GetBatteryVersionImpl(&version))) { - return DefaultBatteryVersion; - } - return version; - } - -} diff --git a/stratosphere/boot/source/boot_change_voltage.cpp b/stratosphere/boot/source/boot_change_voltage.cpp index b73f38b8f..c19653e5c 100644 --- a/stratosphere/boot/source/boot_change_voltage.cpp +++ b/stratosphere/boot/source/boot_change_voltage.cpp @@ -15,7 +15,6 @@ */ #include #include "boot_change_voltage.hpp" -#include "boot_pmc_wrapper.hpp" namespace ams::boot { @@ -36,11 +35,11 @@ namespace ams::boot { void ChangeGpioVoltageTo1_8v() { /* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */ - WritePmcRegister(PmcPwrDet, VoltageChangeMask, VoltageChangeMask); - WritePmcRegister(PmcPwrDetVal, 0, VoltageChangeMask); + dd::ReadModifyWriteIoRegister(PmcPwrDet, VoltageChangeMask, VoltageChangeMask); + dd::ReadModifyWriteIoRegister(PmcPwrDetVal, 0, VoltageChangeMask); /* Sleep for 100 us. */ - svcSleepThread(100'000ul); + os::SleepThread(TimeSpan::FromMicroSeconds(100)); } } diff --git a/stratosphere/boot/source/boot_charger_driver.cpp b/stratosphere/boot/source/boot_charger_driver.cpp deleted file mode 100644 index 695d1be9a..000000000 --- a/stratosphere/boot/source/boot_charger_driver.cpp +++ /dev/null @@ -1,133 +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 . - */ -#include -#include "boot_charger_driver.hpp" - -namespace ams::boot { - - Result ChargerDriver::Read(u8 addr, u8 *out) { - return ReadI2cRegister(this->i2c_session, reinterpret_cast(out), sizeof(*out), &addr, sizeof(addr)); - } - - Result ChargerDriver::Write(u8 addr, u8 val) { - return WriteI2cRegister(this->i2c_session, reinterpret_cast(&val), sizeof(val), &addr, sizeof(addr)); - } - - Result ChargerDriver::ReadWrite(u8 addr, u8 mask, u8 val) { - u8 cur_val; - R_TRY(this->Read(addr, &cur_val)); - - const u8 new_val = (cur_val & ~mask) | val; - R_TRY(this->Write(addr, new_val)); - return ResultSuccess(); - } - - Result ChargerDriver::Initialize() { - return this->Initialize(true); - } - - Result ChargerDriver::Initialize(bool set_input_current_limit) { - if (set_input_current_limit) { - R_TRY(this->SetInputCurrentLimit(bq24193::InputCurrentLimit_500mA)); - } - - R_TRY(this->SetChargeVoltageLimit(4208)); - R_TRY(this->SetFastChargeCurrentLimit(512)); - R_TRY(this->SetForce20PercentChargeCurrent(false)); - R_TRY(this->SetPreChargeCurrentLimit(128)); - R_TRY(this->SetTerminationCurrentLimit(128)); - R_TRY(this->SetMinimumSystemVoltageLimit(3000)); - R_TRY(this->SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting_Disabled)); - R_TRY(this->SetChargingSafetyTimerEnabled(false)); - R_TRY(this->ResetWatchdogTimer()); - R_TRY(this->SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit_500mA)); - R_TRY(this->SetHiZEnabled(false)); - - return ResultSuccess(); - } - - Result ChargerDriver::SetChargeEnabled(bool enabled) { - gpio::SetValue(GpioPadName_Bq24193Charger, enabled ? GpioValue_Low : GpioValue_High); - return this->SetChargerConfiguration(bq24193::ChargerConfiguration_ChargeBattery); - } - - Result ChargerDriver::SetChargerConfiguration(bq24193::ChargerConfiguration config) { - return this->ReadWrite(bq24193::PowerOnConfiguration, 0x30, config); - } - - Result ChargerDriver::SetChargeVoltageLimit(u32 voltage) { - return this->ReadWrite(bq24193::ChargeVoltageControl, 0xFC, bq24193::EncodeChargeVoltageLimit(voltage)); - } - - Result ChargerDriver::SetFastChargeCurrentLimit(u32 current) { - return this->ReadWrite(bq24193::ChargeCurrentControl, 0xFC, bq24193::EncodeFastChargeCurrentLimit(current)); - } - - Result ChargerDriver::SetInputCurrentLimit(bq24193::InputCurrentLimit current) { - return this->ReadWrite(bq24193::InputSourceControl, 0x07, current); - } - - Result ChargerDriver::SetForce20PercentChargeCurrent(bool force) { - return this->ReadWrite(bq24193::ChargeCurrentControl, 0x01, force ? 1 : 0); - } - - Result ChargerDriver::SetPreChargeCurrentLimit(u32 current) { - return this->ReadWrite(bq24193::PreChargeTerminationCurrentControl, 0xF0, bq24193::EncodePreChargeCurrentLimit(current)); - } - - Result ChargerDriver::SetTerminationCurrentLimit(u32 current) { - return this->ReadWrite(bq24193::PreChargeTerminationCurrentControl, 0x0F, bq24193::EncodeTerminationCurrentLimit(current)); - } - - Result ChargerDriver::SetMinimumSystemVoltageLimit(u32 voltage) { - return this->ReadWrite(bq24193::PowerOnConfiguration, 0x0E, bq24193::EncodeMinimumSystemVoltageLimit(voltage)); - } - - Result ChargerDriver::SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting setting) { - return this->ReadWrite(bq24193::ChargeTerminationTimerControl, 0x30, setting); - } - - Result ChargerDriver::SetChargingSafetyTimerEnabled(bool enabled) { - return this->ReadWrite(bq24193::ChargeTerminationTimerControl, 0x08, enabled ? 0x08 : 0); - } - - Result ChargerDriver::ResetWatchdogTimer() { - return this->ReadWrite(bq24193::PowerOnConfiguration, 0x40, 0x40); - } - - Result ChargerDriver::SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit current) { - return this->ReadWrite(bq24193::PowerOnConfiguration, 0x01, current); - } - - Result ChargerDriver::SetHiZEnabled(bool enabled) { - return this->ReadWrite(bq24193::InputSourceControl, 0x80, enabled ? 0x80 : 0); - } - - Result ChargerDriver::GetInputCurrentLimit(bq24193::InputCurrentLimit *out) { - u8 limit; - R_TRY(this->Read(bq24193::InputSourceControl, &limit)); - *out = static_cast(limit); - return ResultSuccess(); - } - - Result ChargerDriver::GetChargeVoltageLimit(u32 *out) { - u8 reg; - R_TRY(this->Read(bq24193::ChargeVoltageControl, ®)); - *out = bq24193::DecodeChargeVoltageLimit(reg); - return ResultSuccess(); - } - -} \ No newline at end of file diff --git a/stratosphere/boot/source/boot_charger_driver.hpp b/stratosphere/boot/source/boot_charger_driver.hpp index 0e2421f22..40c2c65ca 100644 --- a/stratosphere/boot/source/boot_charger_driver.hpp +++ b/stratosphere/boot/source/boot_charger_driver.hpp @@ -14,56 +14,111 @@ * along with this program. If not, see . */ #pragma once -#include "boot_bq24193_charger.hpp" -#include "boot_i2c_utils.hpp" - -#include "gpio/gpio_utils.hpp" +#include namespace ams::boot { + enum ChargerStatus { + ChargerStatus_Unknown = 0, + ChargerStatus_Charging = 1, + ChargerStatus_NotCharging = 2, + ChargerStatus_ChargeTerminationDone = 3, + }; + class ChargerDriver { private: - static constexpr u32 GpioPadName_Bq24193Charger = 0xA; - private: - i2c::driver::Session i2c_session; + powctl::Session charger_session; public: - ChargerDriver() { - i2c::driver::Initialize(); - i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Bq24193); - - gpio::Configure(GpioPadName_Bq24193Charger); - gpio::SetDirection(GpioPadName_Bq24193Charger, GpioDirection_Output); + ChargerDriver() : charger_session() { + R_ABORT_UNLESS(powctl::OpenSession(std::addressof(this->charger_session), powctl::DeviceCode_Bq24193, ddsf::AccessMode_ReadWrite)); } ~ChargerDriver() { - i2c::driver::CloseSession(this->i2c_session); - i2c::driver::Finalize(); + powctl::CloseSession(this->charger_session); } - private: - Result Read(u8 addr, u8 *out_data); - Result Write(u8 addr, u8 val); - Result ReadWrite(u8 addr, u8 mask, u8 val); - Result SetInputCurrentLimit(bq24193::InputCurrentLimit current); - Result SetForce20PercentChargeCurrent(bool force); - Result SetPreChargeCurrentLimit(u32 current); - Result SetTerminationCurrentLimit(u32 current); - Result SetMinimumSystemVoltageLimit(u32 voltage); - Result SetWatchdogTimerSetting(bq24193::WatchdogTimerSetting setting); - Result SetChargingSafetyTimerEnabled(bool enabled); - Result ResetWatchdogTimer(); - Result SetBoostModeCurrentLimit(bq24193::BoostModeCurrentLimit current); - Result SetHiZEnabled(bool enabled); + Result Initialize(bool set_input_current_limit) { + /* Set input current limit to 500 ma. */ + if (set_input_current_limit) { + R_TRY(powctl::SetChargerInputCurrentLimit(this->charger_session, 500)); + } - public: - Result Initialize(); - Result Initialize(bool set_input_current_limit); - Result SetChargeVoltageLimit(u32 voltage); - Result SetFastChargeCurrentLimit(u32 current); - Result SetChargeEnabled(bool enabled); - Result SetChargerConfiguration(bq24193::ChargerConfiguration config); - Result GetInputCurrentLimit(bq24193::InputCurrentLimit *out); - Result GetChargeVoltageLimit(u32 *out); + /* Set input voltage limit to 500 mv. */ + R_TRY(powctl::SetChargerInputVoltageLimit(this->charger_session, 500)); + + /* Disable hi-z mode. */ + R_TRY(powctl::SetChargerHiZEnabled(this->charger_session, false)); + + /* Set configuration to charge battery. */ + R_TRY(powctl::SetChargerChargerConfiguration(this->charger_session, powctl::ChargerConfiguration_ChargeBattery)); + + return ResultSuccess(); + } + + Result GetChargeCurrentState(powctl::ChargeCurrentState *out) { + return powctl::GetChargerChargeCurrentState(out, this->charger_session); + } + + Result SetChargeCurrentState(powctl::ChargeCurrentState state) { + return powctl::SetChargerChargeCurrentState(this->charger_session, state); + } + + Result GetInputCurrentLimit(int *out) { + return powctl::GetChargerInputCurrentLimit(out, this->charger_session); + } + + Result SetChargerConfiguration(powctl::ChargerConfiguration cfg) { + return powctl::SetChargerChargerConfiguration(this->charger_session, cfg); + } + + Result GetFastChargeCurrentLimit(int *out) { + return powctl::GetChargerFastChargeCurrentLimit(out, this->charger_session); + } + + Result SetFastChargeCurrentLimit(int limit) { + return powctl::SetChargerFastChargeCurrentLimit(this->charger_session, limit); + } + + Result GetChargeVoltageLimit(int *out) { + return powctl::GetChargerChargeVoltageLimit(out, this->charger_session); + } + + Result SetChargeVoltageLimit(int limit) { + return powctl::SetChargerChargeVoltageLimit(this->charger_session, limit); + } + + Result GetChargerStatus(boot::ChargerStatus *out) { + /* Default to unknown. */ + *out = ChargerStatus_Unknown; + + /* Get the powctl status. */ + powctl::ChargerStatus powctl_status; + R_TRY(powctl::GetChargerChargerStatus(std::addressof(powctl_status), this->charger_session)); + + switch (powctl_status) { + case powctl::ChargerStatus_Charging: *out = boot::ChargerStatus_Charging; break; + case powctl::ChargerStatus_NotCharging: *out = boot::ChargerStatus_NotCharging; break; + case powctl::ChargerStatus_ChargeTerminationDone: *out = boot::ChargerStatus_ChargeTerminationDone; break; + } + + return ResultSuccess(); + } + + Result GetBatteryCompensation(int *out) { + return powctl::GetChargerBatteryCompensation(out, this->charger_session); + } + + Result SetBatteryCompensation(int v) { + return powctl::SetChargerBatteryCompensation(this->charger_session, v); + } + + Result GetVoltageClamp(int *out) { + return powctl::GetChargerVoltageClamp(out, this->charger_session); + } + + Result SetVoltageClamp(int v) { + return powctl::SetChargerVoltageClamp(this->charger_session, v); + } }; } diff --git a/stratosphere/boot/source/boot_check_battery.cpp b/stratosphere/boot/source/boot_check_battery.cpp index 63b6627e4..3be3c88fb 100644 --- a/stratosphere/boot/source/boot_check_battery.cpp +++ b/stratosphere/boot/source/boot_check_battery.cpp @@ -14,19 +14,25 @@ * along with this program. If not, see . */ #include -#include "boot_battery_driver.hpp" +#include "boot_check_battery.hpp" #include "boot_battery_icons.hpp" #include "boot_boot_reason.hpp" -#include "boot_calibration.hpp" -#include "boot_charger_driver.hpp" -#include "boot_check_battery.hpp" #include "boot_pmic_driver.hpp" +#include "boot_battery_driver.hpp" +#include "boot_charger_driver.hpp" #include "boot_power_utils.hpp" namespace ams::boot { namespace { + /* Value definitions. */ + constexpr inline double BatteryLevelThresholdForBoot = 3.0; + constexpr inline double BatteryLevelThresholdForFullCharge = 99.0; + + constexpr inline int BatteryVoltageThresholdConnected = 4000; + constexpr inline int BatteryVoltageThresholdDisconnected = 3650; + /* Types. */ enum class CheckBatteryResult { Success, @@ -34,251 +40,488 @@ namespace ams::boot { Reboot, }; - struct BatteryChargeParameters { - u32 temp_min; - u32 temp_low; - u32 temp_high; - u32 temp_max; - u32 allow_high_temp_charge_max_voltage; - u32 charge_voltage_limit_default; - u32 charge_voltage_limit_high_temp; - u32 allow_fast_charge_min_temp; - u32 allow_fast_charge_min_voltage; - u32 fast_charge_current_limit_default; - u32 fast_charge_current_limit_low_temp; - u32 fast_charge_current_limit_low_voltage; + class BatteryChecker { + private: + boot::ChargerDriver &charger_driver; + boot::BatteryDriver &battery_driver; + const powctl::driver::impl::ChargeParameters &charge_parameters; + powctl::driver::impl::ChargeArbiter charge_arbiter; + powctl::ChargeCurrentState charge_current_state; + int fast_charge_current_limit; + int charge_voltage_limit; + int battery_compensation; + int voltage_clamp; + TimeSpan charging_done_interval; + bool has_start_time; + TimeSpan start_time; + private: + bool IsChargeDone(); + void UpdateChargeDoneCurrent(); + void ApplyArbiterRule(); + void PrintBatteryStatus(float raw_charge, int voltage, int voltage_threshold); + CheckBatteryResult LoopCheckBattery(bool reboot_on_power_button_press, bool return_on_enough_battery, bool shutdown_on_full_battery, bool show_display, bool show_charging_display); + + void UpdateStartTime() { + /* Update start time. */ + this->start_time = os::ConvertToTimeSpan(os::GetSystemTick()); + this->has_start_time = true; + } + public: + BatteryChecker(boot::ChargerDriver &cd, boot::BatteryDriver &bd, const powctl::driver::impl::ChargeParameters &cp, int cvl) : charger_driver(cd), battery_driver(bd), charge_parameters(cp), charge_arbiter(cp.rules, cp.num_rules, cvl), charging_done_interval(TimeSpan::FromSeconds(2)), has_start_time(false) { + /* Get parameters from charger. */ + if (R_FAILED(this->charger_driver.GetChargeCurrentState(std::addressof(this->charge_current_state)))) { + boot::ShutdownSystem(); + } + if (R_FAILED(this->charger_driver.GetFastChargeCurrentLimit(std::addressof(this->fast_charge_current_limit)))) { + boot::ShutdownSystem(); + } + if (R_FAILED(this->charger_driver.GetChargeVoltageLimit(std::addressof(this->charge_voltage_limit)))) { + boot::ShutdownSystem(); + } + if (R_FAILED(this->charger_driver.GetBatteryCompensation(std::addressof(this->battery_compensation)))) { + boot::ShutdownSystem(); + } + if (R_FAILED(this->charger_driver.GetVoltageClamp(std::addressof(this->voltage_clamp)))) { + boot::ShutdownSystem(); + } + + /* Update start time. */ + this->UpdateStartTime(); + } + + CheckBatteryResult LoopCheckBattery(spl::BootReason boot_reason) { + if (boot_reason == spl::BootReason_RtcAlarm2) { + /* RTC Alarm 2 boot (QuasiOff) */ + return this->LoopCheckBattery(true, false, true, false, false); + } else if (boot_reason == spl::BootReason_AcOk) { + /* ACOK boot */ + return this->LoopCheckBattery(true, true, false, true, true); + } else { + /* Normal boot */ + return this->LoopCheckBattery(false, true, false, true, false); + } + } + + void UpdateCharger(); }; - /* Battery parameters. */ - constexpr BatteryChargeParameters BatteryChargeParameters0 = { - .temp_min = 4, - .temp_low = 17, - .temp_high = 51, - .temp_max = 60, - .allow_high_temp_charge_max_voltage = 4050, - .charge_voltage_limit_default = 4208, - .charge_voltage_limit_high_temp = 3952, - .allow_fast_charge_min_voltage = 3320, - .fast_charge_current_limit_default = 0x800, - .fast_charge_current_limit_low_temp = 0x300, - .fast_charge_current_limit_low_voltage = 0x200, - }; + void BatteryChecker::PrintBatteryStatus(float raw_charge, int voltage, int voltage_threshold) { + /* TODO: Print charge/voltage/threshold. */ + AMS_UNUSED(raw_charge, voltage, voltage_threshold); - constexpr BatteryChargeParameters BatteryChargeParameters1 = { - .temp_min = 4, - .temp_low = 17, - .temp_high = 51, - .temp_max = 59, - .allow_high_temp_charge_max_voltage = 3984, - .charge_voltage_limit_default = 4208, - .charge_voltage_limit_high_temp = 3984, - .allow_fast_charge_min_voltage = 0, - .fast_charge_current_limit_default = 0x600, - .fast_charge_current_limit_low_temp = 0x240, - .fast_charge_current_limit_low_voltage = 0x600, - }; - - constexpr BatteryChargeParameters BatteryChargeParameters2 = { - .temp_min = 4, - .temp_low = 17, - .temp_high = 51, - .temp_max = 59, - .allow_high_temp_charge_max_voltage = 4080, - .charge_voltage_limit_default = 4320, - .charge_voltage_limit_high_temp = 4080, - .allow_fast_charge_min_voltage = 0, - .fast_charge_current_limit_default = 0x680, - .fast_charge_current_limit_low_temp = 0x280, - .fast_charge_current_limit_low_voltage = 0x680, - }; - - constexpr const BatteryChargeParameters *GetBatteryChargeParameters(u32 battery_version) { - switch (battery_version) { - case 0: - return &BatteryChargeParameters0; - case 1: - return &BatteryChargeParameters1; - case 2: - return &BatteryChargeParameters2; - AMS_UNREACHABLE_DEFAULT_CASE(); + /* Get various battery metrics. */ + int avg_current, current, open_circuit_voltage; + float temp; + if (R_FAILED(this->battery_driver.GetAverageCurrent(std::addressof(avg_current)))) { + return; } + if (R_FAILED(this->battery_driver.GetCurrent(std::addressof(current)))) { + return; + } + if (R_FAILED(this->battery_driver.GetTemperature(std::addressof(temp)))) { + return; + } + if (R_FAILED(this->battery_driver.GetOpenCircuitVoltage(std::addressof(open_circuit_voltage)))) { + return; + } + + /* TODO: Print the things we just got. */ + AMS_UNUSED(avg_current, current, temp, open_circuit_voltage); } - /* Helpers. */ - void UpdateCharger(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit) { - double temperature; - u32 battery_voltage; - - if (R_FAILED(battery_driver->GetTemperature(&temperature)) || R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) { - pmic_driver->ShutdownSystem(); + bool BatteryChecker::IsChargeDone() { + /* Get the charger status. */ + boot::ChargerStatus charger_status; + if (R_FAILED(this->charger_driver.GetChargerStatus(std::addressof(charger_status)))) { + boot::ShutdownSystem(); } - bool enable_charge = true; - if (temperature < double(params->temp_min)) { - enable_charge = false; - } else if (double(params->temp_high) <= temperature && temperature < double(params->temp_max)) { - if (battery_voltage < params->allow_high_temp_charge_max_voltage) { - charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp); - } else { - enable_charge = false; + /* If charge status isn't done, we're not done. */ + if (charger_status != boot::ChargerStatus_ChargeTerminationDone) { + return false; + } + + /* Return whether a done current of zero is acceptable. */ + return this->charge_arbiter.IsBatteryDoneCurrentAcceptable(0); + } + + void BatteryChecker::UpdateChargeDoneCurrent() { + int done_current = 0; + if (this->has_start_time && (os::ConvertToTimeSpan(os::GetSystemTick()) - this->start_time) >= this->charging_done_interval) { + /* Get the current. */ + if (R_FAILED(this->battery_driver.GetCurrent(std::addressof(done_current)))) { + boot::ShutdownSystem(); } - } else if (double(params->temp_max) <= temperature) { - enable_charge = false; - if (battery_voltage < params->allow_high_temp_charge_max_voltage) { - charge_voltage_limit = std::min(charge_voltage_limit, params->charge_voltage_limit_high_temp); + } else { + /* Get the charger status. */ + boot::ChargerStatus charger_status; + if (R_FAILED(this->charger_driver.GetChargerStatus(std::addressof(charger_status)))) { + boot::ShutdownSystem(); + } + + /* If the charger status isn't done, don't update. */ + if (charger_status != boot::ChargerStatus_ChargeTerminationDone) { + return; } } - u32 fast_charge_current_limit = params->fast_charge_current_limit_default; - if (temperature < double(params->temp_low)) { - fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_temp); - } - if (battery_voltage < params->allow_fast_charge_min_voltage) { - fast_charge_current_limit = std::min(fast_charge_current_limit, params->fast_charge_current_limit_low_voltage); + /* Update done current. */ + this->charge_arbiter.SetBatteryDoneCurrent(done_current); + } + + void BatteryChecker::UpdateCharger() { + /* Get the battery temperature. */ + float temp; + if (R_FAILED(this->battery_driver.GetTemperature(std::addressof(temp)))) { + boot::ShutdownSystem(); } - if (R_FAILED(charger_driver->SetChargeEnabled(enable_charge))) { - pmic_driver->ShutdownSystem(); + /* Update the temperature level. */ + powctl::BatteryTemperatureLevel temp_level; + if (temp < static_cast(this->charge_parameters.temp_min)) { + temp_level = powctl::BatteryTemperatureLevel::TooLow; + } else if (temp < static_cast(this->charge_parameters.temp_low)) { + temp_level = powctl::BatteryTemperatureLevel::Low; + } else if (temp < static_cast(this->charge_parameters.temp_high)) { + temp_level = powctl::BatteryTemperatureLevel::Medium; + } else if (temp < static_cast(this->charge_parameters.temp_max)) { + temp_level = powctl::BatteryTemperatureLevel::High; + } else { + temp_level = powctl::BatteryTemperatureLevel::TooHigh; } - if (R_FAILED(charger_driver->SetChargeVoltageLimit(charge_voltage_limit))) { - pmic_driver->ShutdownSystem(); + this->charge_arbiter.SetBatteryTemperatureLevel(temp_level); + + /* Update average voltage. */ + int avg_v_cell; + if (R_FAILED(this->battery_driver.GetAverageVCell(std::addressof(avg_v_cell)))) { + boot::ShutdownSystem(); } - if (R_FAILED(charger_driver->SetFastChargeCurrentLimit(fast_charge_current_limit))) { - pmic_driver->ShutdownSystem(); + this->charge_arbiter.SetBatteryAverageVCell(avg_v_cell); + + /* Update open circuit voltage. */ + int ocv; + if (R_FAILED(this->battery_driver.GetOpenCircuitVoltage(std::addressof(ocv)))) { + boot::ShutdownSystem(); + } + this->charge_arbiter.SetBatteryOpenCircuitVoltage(ocv); + + /* Update charge done current. */ + this->UpdateChargeDoneCurrent(); + + /* Update arbiter power state. */ + this->charge_arbiter.SetPowerState(powctl::PowerState::ShutdownChargeMain); + + /* Apply the newly selected rule. */ + this->ApplyArbiterRule(); + } + + void BatteryChecker::ApplyArbiterRule() { + /* Get the selected rule. */ + const auto *rule = this->charge_arbiter.GetSelectedRule(); + AMS_ASSERT(rule != nullptr); + + /* Check if we need to perform charger initialization. */ + const bool reinit_charger = rule->reinitialize_charger; + const auto cur_charge_current_state = this->charge_current_state; + + /* Set the charger to not charging while we make changes. */ + if (!reinit_charger || cur_charge_current_state != powctl::ChargeCurrentState_NotCharging) { + if (R_FAILED(this->charger_driver.SetChargeCurrentState(powctl::ChargeCurrentState_NotCharging))) { + boot::ShutdownSystem(); + } + this->charge_current_state = powctl::ChargeCurrentState_NotCharging; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* Process fast charge current limit when rule is smaller. */ + const auto rule_fast_charge_current_limit = rule->fast_charge_current_limit; + const auto cur_fast_charge_current_limit = this->fast_charge_current_limit; + if (rule_fast_charge_current_limit < cur_fast_charge_current_limit) { + if (R_FAILED(this->charger_driver.SetFastChargeCurrentLimit(rule_fast_charge_current_limit))) { + boot::ShutdownSystem(); + } + this->fast_charge_current_limit = rule_fast_charge_current_limit; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* Process charge voltage limit when rule is smaller. */ + const auto rule_charge_voltage_limit = std::min(rule->charge_voltage_limit, this->charge_arbiter.GetChargeVoltageLimit()); + const auto cur_charge_voltage_limit = this->charge_voltage_limit; + if (rule_charge_voltage_limit < cur_charge_voltage_limit) { + if (R_FAILED(this->charger_driver.SetChargeVoltageLimit(rule_charge_voltage_limit))) { + boot::ShutdownSystem(); + } + this->charge_voltage_limit = rule_charge_voltage_limit; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* Process battery compensation when rule is smaller. */ + const auto rule_battery_compensation = rule->battery_compensation; + const auto cur_battery_compensation = this->battery_compensation; + if (rule_battery_compensation < cur_battery_compensation) { + if (R_FAILED(this->charger_driver.SetBatteryCompensation(rule_battery_compensation))) { + boot::ShutdownSystem(); + } + this->battery_compensation = rule_battery_compensation; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* Process voltage clamp when rule is smaller. */ + const auto rule_voltage_clamp = rule->voltage_clamp; + const auto cur_voltage_clamp = this->voltage_clamp; + if (rule_voltage_clamp < cur_voltage_clamp) { + if (R_FAILED(this->charger_driver.SetVoltageClamp(rule_voltage_clamp))) { + boot::ShutdownSystem(); + } + this->voltage_clamp = rule_voltage_clamp; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* Process voltage clamp when rule is larger. */ + if (rule_voltage_clamp > cur_voltage_clamp) { + if (R_FAILED(this->charger_driver.SetVoltageClamp(rule_voltage_clamp))) { + boot::ShutdownSystem(); + } + this->voltage_clamp = rule_voltage_clamp; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* Process battery compensation when rule is larger. */ + if (rule_battery_compensation > cur_battery_compensation) { + if (R_FAILED(this->charger_driver.SetBatteryCompensation(rule_battery_compensation))) { + boot::ShutdownSystem(); + } + this->battery_compensation = rule_battery_compensation; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* Process fast charge current limit when rule is larger. */ + if (rule_fast_charge_current_limit > cur_fast_charge_current_limit) { + if (R_FAILED(this->charger_driver.SetFastChargeCurrentLimit(rule_fast_charge_current_limit))) { + boot::ShutdownSystem(); + } + this->fast_charge_current_limit = rule_fast_charge_current_limit; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* Process charge voltage limit when rule is larger. */ + if (rule_charge_voltage_limit > cur_charge_voltage_limit) { + if (R_FAILED(this->charger_driver.SetChargeVoltageLimit(rule_charge_voltage_limit))) { + boot::ShutdownSystem(); + } + this->charge_voltage_limit = rule_charge_voltage_limit; + + /* Update start time. */ + this->UpdateStartTime(); + } + + /* If we're not charging and we expect to reinitialize the charger, do so. */ + if (cur_charge_current_state != powctl::ChargeCurrentState_Charging && reinit_charger) { + if (R_FAILED(this->charger_driver.SetChargeCurrentState(powctl::ChargeCurrentState_Charging))) { + boot::ShutdownSystem(); + } + this->charge_current_state = powctl::ChargeCurrentState_Charging; + + /* Update start time. */ + this->UpdateStartTime(); } } - bool IsSufficientBattery(u32 battery_voltage, bool ac_ok) { - /* Nintendo has stuff for updating a static variable every 10 seconds here, but this seems, again, to be debug leftovers. */ - const u32 required_voltage = ac_ok ? 4000 : 3650; - return battery_voltage >= required_voltage; - } - - CheckBatteryResult LoopCheckBattery(PmicDriver *pmic_driver, ChargerDriver *charger_driver, BatteryDriver *battery_driver, const BatteryChargeParameters *params, u32 charge_voltage_limit, bool reboot_on_power_button_pressed, bool succeed_on_sufficient_battery, bool shutdown_on_full_battery, bool can_show_battery_icon, bool can_show_charging_icon) { + CheckBatteryResult BatteryChecker::LoopCheckBattery(bool reboot_on_power_button_press, bool return_on_enough_battery, bool shutdown_on_full_battery, bool show_display, bool show_charging_display) { + /* Ensure that if we show a charging icon, we stop showing it when we're done. */ bool is_showing_charging_icon = false; ON_SCOPE_EXIT { if (is_showing_charging_icon) { - EndShowChargingIcon(); + boot::EndShowChargingIcon(); + is_showing_charging_icon = false; } }; - if (can_show_charging_icon) { - size_t battery_percentage; - if (R_FAILED(battery_driver->GetBatteryPercentage(&battery_percentage))) { + /* Show the charging display, if we should. */ + if (show_charging_display) { + /* Get the raw battery charge. */ + float raw_battery_charge; + if (R_FAILED(this->battery_driver.GetSocRep(std::addressof(raw_battery_charge)))) { return CheckBatteryResult::Shutdown; } - StartShowChargingIcon(battery_percentage, true); + + /* Display the battery with the appropriate percentage. */ + const auto battery_charge = powctl::impl::ConvertBatteryChargePercentage(raw_battery_charge); + boot::StartShowChargingIcon(battery_charge); is_showing_charging_icon = true; } + /* Loop, checking the battery status. */ + TimeSpan last_progress_time = TimeSpan(0); while (true) { - double battery_charge; - if (R_FAILED(battery_driver->GetSocRep(&battery_charge))) { + /* Get the raw battery charge. */ + float raw_battery_charge; + if (R_FAILED(this->battery_driver.GetSocRep(std::addressof(raw_battery_charge)))) { return CheckBatteryResult::Shutdown; } - if (succeed_on_sufficient_battery && battery_charge >= 3.0) { - return CheckBatteryResult::Success; - } else if (shutdown_on_full_battery && battery_charge >= 99.0) { - return CheckBatteryResult::Shutdown; - } else { - /* Nintendo has logic for checking a value every 10 seconds. */ - /* They never do anything with this value though, so it's probably just leftovers from debug? */ - } + /* Get the average vcell. */ + int battery_voltage; + if (R_FAILED(this->battery_driver.GetAverageVCell(std::addressof(battery_voltage)))) { + return CheckBatteryResult::Shutdown; + } + + /* Get whether we're connected to charger. */ bool ac_ok; - if (R_FAILED(pmic_driver->GetAcOk(&ac_ok))) { + if (R_FAILED((boot::PmicDriver().GetAcOk(std::addressof(ac_ok))))) { return CheckBatteryResult::Shutdown; } - u32 battery_voltage; - if (R_FAILED(battery_driver->GetAverageVCell(&battery_voltage))) { - return CheckBatteryResult::Shutdown; - } + /* Decide on a battery voltage threshold. */ + const auto battery_voltage_threshold = ac_ok ? BatteryVoltageThresholdConnected : BatteryVoltageThresholdDisconnected; - if (succeed_on_sufficient_battery && IsSufficientBattery(battery_voltage, ac_ok)) { - return CheckBatteryResult::Success; - } - - if (!ac_ok) { - if (can_show_battery_icon && !is_showing_charging_icon) { - ShowLowBatteryIcon(); + /* Check if we should return. */ + if (return_on_enough_battery) { + if (raw_battery_charge >= BatteryLevelThresholdForBoot || battery_voltage >= battery_voltage_threshold) { + this->PrintBatteryStatus(raw_battery_charge, battery_voltage, battery_voltage_threshold); + return CheckBatteryResult::Success; } - return CheckBatteryResult::Shutdown; } - if (reboot_on_power_button_pressed) { - bool power_button_pressed; - if (R_FAILED(pmic_driver->GetPowerButtonPressed(&power_button_pressed))) { + /* Otherwise, check if we should shut down. */ + if (shutdown_on_full_battery) { + if (raw_battery_charge >= BatteryLevelThresholdForFullCharge || this->IsChargeDone()) { return CheckBatteryResult::Shutdown; } + } + + /* Perform periodic printing. */ + constexpr TimeSpan PrintProgressInterval = TimeSpan::FromSeconds(10); + const auto cur_time = os::ConvertToTimeSpan(os::GetSystemTick()); + if ((cur_time - last_progress_time) >= PrintProgressInterval) { + last_progress_time = cur_time; + this->PrintBatteryStatus(raw_battery_charge, battery_voltage, battery_voltage_threshold); + } + + /* If we've gotten to this point, we have insufficient battery to boot. If we aren't charging, show low battery and shutdown. */ + if (!ac_ok) { + this->PrintBatteryStatus(raw_battery_charge, battery_voltage, battery_voltage_threshold); + if (show_display && !is_showing_charging_icon) { + boot::ShowLowBatteryIcon(); + } + return CheckBatteryResult::Shutdown; + } + + /* Check if we should reboot due to a power button press. */ + if (reboot_on_power_button_press) { + /* Get the power button value. */ + bool power_button_pressed; + if (R_FAILED((boot::PmicDriver().GetPowerButtonPressed(std::addressof(power_button_pressed))))) { + return CheckBatteryResult::Shutdown; + } + + /* Handle the press (or not). */ if (power_button_pressed) { return CheckBatteryResult::Reboot; } } - if (can_show_battery_icon && !is_showing_charging_icon) { - StartShowChargingIcon(1, false); + /* If we got to this point, we should show the low-battery charging screen. */ + if (show_display && !is_showing_charging_icon) { + boot::StartShowLowBatteryChargingIcon(); is_showing_charging_icon = true; } - svcSleepThread(20'000'000ul); - UpdateCharger(pmic_driver, charger_driver, battery_driver, params, charge_voltage_limit); + /* Wait a bit before checking again. */ + constexpr auto BatteryChargeCheckInterval = TimeSpan::FromMilliSeconds(20); + os::SleepThread(BatteryChargeCheckInterval); + + /* Update the charger. */ + this->UpdateCharger(); } + } + } void CheckBatteryCharge() { - PmicDriver pmic_driver; - BatteryDriver battery_driver; - ChargerDriver charger_driver; + /* Open a sessions for the charger/battery. */ + boot::ChargerDriver charger_driver; + boot::BatteryDriver battery_driver; - if (R_FAILED(battery_driver.InitializeBatteryParameters())) { - pmic_driver.ShutdownSystem(); - } + /* Check if the battery is removed. */ { - bool removed; - if (R_FAILED(battery_driver.IsBatteryRemoved(&removed)) || removed) { - pmic_driver.ShutdownSystem(); + bool removed = false; + if (R_FAILED(battery_driver.IsBatteryRemoved(std::addressof(removed))) || removed) { + boot::ShutdownSystem(); } } - const u32 boot_reason = GetBootReason(); - bq24193::InputCurrentLimit input_current_limit; - if (R_FAILED(charger_driver.Initialize(boot_reason != 4)) || R_FAILED(charger_driver.GetInputCurrentLimit(&input_current_limit))) { - pmic_driver.ShutdownSystem(); - } + /* Get the boot reason. */ + const auto boot_reason = boot::GetBootReason(); - if (input_current_limit <= bq24193::InputCurrentLimit_150mA) { - charger_driver.SetChargerConfiguration(bq24193::ChargerConfiguration_ChargeDisable); - pmic_driver.ShutdownSystem(); - } + /* Initialize the charger driver. */ + if (R_FAILED(charger_driver.Initialize(boot_reason != spl::BootReason_RtcAlarm2))) - const BatteryChargeParameters *params = GetBatteryChargeParameters(GetBatteryVersion()); - u32 charge_voltage_limit = params->charge_voltage_limit_default; - CheckBatteryResult check_result; - if (boot_reason == 4) { - if (R_FAILED(charger_driver.GetChargeVoltageLimit(&charge_voltage_limit))) { - pmic_driver.ShutdownSystem(); + /* Check that the charger input limit is greater than 150 milli-amps. */ + { + int input_current_limit_ma; + if (R_FAILED(charger_driver.GetInputCurrentLimit(std::addressof(input_current_limit_ma)))) { + boot::ShutdownSystem(); } - UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit); - check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, false, true, false, false); + + if (input_current_limit_ma <= 150) { + charger_driver.SetChargerConfiguration(powctl::ChargerConfiguration_ChargeDisable); + boot::ShutdownSystem(); + } + } + + /* Get the charge parameters. */ + const auto &charge_parameters = powctl::driver::impl::GetChargeParameters(); + + /* Get the charge voltage limit. */ + int charge_voltage_limit_mv; + if (boot_reason != spl::BootReason_RtcAlarm2) { + charge_voltage_limit_mv = charge_parameters.default_charge_voltage_limit; } else { - UpdateCharger(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit); - if (boot_reason == 1) { - check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, true, true, false, true, true); - } else { - check_result = LoopCheckBattery(&pmic_driver, &charger_driver, &battery_driver, params, charge_voltage_limit, false, true, false, true, false); + if (R_FAILED(charger_driver.GetChargeVoltageLimit(std::addressof(charge_voltage_limit_mv)))) { + boot::ShutdownSystem(); } } - switch (check_result) { + /* Create and update a battery checker. */ + BatteryChecker battery_checker(charger_driver, battery_driver, charge_parameters, charge_voltage_limit_mv); + battery_checker.UpdateCharger(); + + /* Set the display brightness to 25%. */ + boot::SetDisplayBrightness(25); + + /* Check the battery. */ + const CheckBatteryResult check_result = battery_checker.LoopCheckBattery(boot_reason); + + /* Set the display brightness to 100%. */ + boot::SetDisplayBrightness(100); + + /* Handle the check result. */ + switch (check_result) { case CheckBatteryResult::Success: break; case CheckBatteryResult::Shutdown: - pmic_driver.ShutdownSystem(); + boot::ShutdownSystem(); break; case CheckBatteryResult::Reboot: - RebootSystem(); + boot::RebootSystem(); break; AMS_UNREACHABLE_DEFAULT_CASE(); } diff --git a/stratosphere/boot/source/boot_check_clock.cpp b/stratosphere/boot/source/boot_check_clock.cpp index 4ca02d079..d5954237e 100644 --- a/stratosphere/boot/source/boot_check_clock.cpp +++ b/stratosphere/boot/source/boot_check_clock.cpp @@ -35,7 +35,8 @@ namespace ams::boot { /* Helpers. */ bool IsUsbClockValid() { - uintptr_t car_regs = dd::GetIoMapping(0x60006000ul, os::MemoryPageSize); + uintptr_t car_regs = dd::QueryIoMapping(0x60006000ul, os::MemoryPageSize); + AMS_ASSERT(car_regs != 0); const u32 pllu = reg::Read(car_regs + 0xC0); const u32 utmip = reg::Read(car_regs + 0x480); @@ -47,7 +48,7 @@ namespace ams::boot { void CheckClock() { if (!IsUsbClockValid()) { /* Sleep for 1s, then reboot. */ - svcSleepThread(1'000'000'000ul); + os::SleepThread(TimeSpan::FromSeconds(1)); RebootSystem(); } } diff --git a/stratosphere/boot/source/boot_clock_initial_configuration.cpp b/stratosphere/boot/source/boot_clock_initial_configuration.cpp index 4dd90a83f..6cf267691 100644 --- a/stratosphere/boot/source/boot_clock_initial_configuration.cpp +++ b/stratosphere/boot/source/boot_clock_initial_configuration.cpp @@ -15,15 +15,14 @@ */ #include #include "boot_clock_initial_configuration.hpp" -#include "boot_pmc_wrapper.hpp" -#include "boot_registers_pmc.hpp" namespace ams::boot { namespace { + constexpr inline dd::PhysicalAddress PmcBase = 0x7000E400; + /* Convenience definitions. */ - constexpr u32 PmcClkOutCntrl = PmcBase + APBDEV_PMC_CLK_OUT_CNTRL; constexpr u32 InitialClockOutMask1x = 0x00C4; constexpr u32 InitialClockOutMask6x = 0xC4C4; @@ -32,7 +31,7 @@ namespace ams::boot { void SetInitialClockConfiguration() { /* Write mask to APBDEV_PMC_PWR_DET, then clear APBDEV_PMC_PWR_DET_VAL. */ const u32 mask = hos::GetVersion() >= hos::Version_6_0_0 ? InitialClockOutMask6x : InitialClockOutMask1x; - WritePmcRegister(PmcClkOutCntrl, mask, mask); + dd::ReadModifyWriteIoRegister(PmcBase + APBDEV_PMC_CLK_OUT_CNTRL, mask, mask); } } diff --git a/stratosphere/boot/source/boot_display.cpp b/stratosphere/boot/source/boot_display.cpp index 8d6a78a83..126b20d17 100644 --- a/stratosphere/boot/source/boot_display.cpp +++ b/stratosphere/boot/source/boot_display.cpp @@ -16,39 +16,36 @@ #include #include "boot_display.hpp" #include "boot_i2c_utils.hpp" -#include "boot_pmc_wrapper.hpp" -#include "boot_registers_clkrst.hpp" #include "boot_registers_di.hpp" -#include "boot_registers_gpio.hpp" -#include "boot_registers_pinmux.hpp" -#include "boot_registers_pmc.hpp" namespace ams::boot { /* Display configuration included into anonymous namespace. */ namespace { -#include "boot_display_config.inc" + #include "boot_display_config.inc" } namespace { /* Helpful defines. */ - constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; - constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1; - constexpr uintptr_t FrameBufferPaddr = DisplayConfigFrameBufferAddress; - constexpr size_t FrameBufferWidth = 768; - constexpr size_t FrameBufferHeight = 1280; - constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32); + constexpr size_t DeviceAddressSpaceAlignSize = 4_MB; - constexpr uintptr_t Disp1Base = 0x54200000ul; - constexpr uintptr_t DsiBase = 0x54300000ul; - constexpr uintptr_t ClkRstBase = 0x60006000ul; - constexpr uintptr_t GpioBase = 0x6000D000ul; - constexpr uintptr_t ApbMiscBase = 0x70000000ul; - constexpr uintptr_t MipiCalBase = 0x700E3000ul; + constexpr dd::DeviceVirtualAddress FrameBufferDeviceAddress = DisplayConfigFrameBufferAddress; + + constexpr size_t FrameBufferWidth = 768; + constexpr size_t FrameBufferHeight = 1280; + constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32); + + constexpr dd::PhysicalAddress PmcBase = 0x7000E400ul; + constexpr dd::PhysicalAddress Disp1Base = 0x54200000ul; + constexpr dd::PhysicalAddress DsiBase = 0x54300000ul; + constexpr dd::PhysicalAddress ClkRstBase = 0x60006000ul; + constexpr dd::PhysicalAddress GpioBase = 0x6000D000ul; + constexpr dd::PhysicalAddress ApbMiscBase = 0x70000000ul; + constexpr dd::PhysicalAddress MipiCalBase = 0x700E3000ul; constexpr size_t Disp1Size = 3 * os::MemoryPageSize; constexpr size_t DsiSize = os::MemoryPageSize; @@ -57,35 +54,54 @@ namespace ams::boot { constexpr size_t ApbMiscSize = os::MemoryPageSize; constexpr size_t MipiCalSize = os::MemoryPageSize; - constexpr s32 DsiWaitForCommandMilliSecondsMax = 250; - constexpr s32 DsiWaitForCommandCompletionMilliSeconds = 5; - constexpr s32 DsiWaitForHostControlMilliSecondsMax = 150; + constexpr int DsiWaitForCommandMilliSecondsMax = 250; + constexpr int DsiWaitForCommandCompletionMilliSeconds = 5; + constexpr int DsiWaitForHostControlMilliSecondsMax = 150; - /* Types. */ + constexpr size_t GPIO_PORT3_CNF_0 = 0x200; + constexpr size_t GPIO_PORT3_OE_0 = 0x210; + constexpr size_t GPIO_PORT3_OUT_0 = 0x220; + + constexpr size_t GPIO_PORT6_CNF_1 = 0x504; + constexpr size_t GPIO_PORT6_OE_1 = 0x514; + constexpr size_t GPIO_PORT6_OUT_1 = 0x524; /* Globals. */ - bool g_is_display_intialized = false; - u32 *g_frame_buffer = nullptr; - spl::SocType g_soc_type = spl::SocType_Erista; - u32 g_lcd_vendor = 0; - Handle g_dc_das_hnd = INVALID_HANDLE; - u8 g_frame_buffer_storage[DeviceAddressSpaceAlignSize + FrameBufferSize]; + constinit bool g_is_display_intialized = false; + constinit spl::SocType g_soc_type = spl::SocType_Erista; - uintptr_t g_disp1_regs = 0; - uintptr_t g_dsi_regs = 0; - uintptr_t g_clk_rst_regs = 0; - uintptr_t g_gpio_regs = 0; - uintptr_t g_apb_misc_regs = 0; - uintptr_t g_mipi_cal_regs = 0; + constinit u32 g_lcd_vendor = 0; + constinit int g_display_brightness = 100; + + constinit dd::DeviceAddressSpaceType g_device_address_space; + + constinit pwm::driver::ChannelSession g_lcd_backlight_session; + + constinit u32 *g_frame_buffer = nullptr; + constinit u8 g_frame_buffer_storage[DeviceAddressSpaceAlignSize + FrameBufferSize]; + + constinit uintptr_t g_disp1_regs = 0; + constinit uintptr_t g_dsi_regs = 0; + constinit uintptr_t g_clk_rst_regs = 0; + constinit uintptr_t g_gpio_regs = 0; + constinit uintptr_t g_apb_misc_regs = 0; + constinit uintptr_t g_mipi_cal_regs = 0; /* Helper functions. */ - void InitializeRegisterBaseAddresses() { - g_disp1_regs = dd::GetIoMapping(Disp1Base, Disp1Size); - g_dsi_regs = dd::GetIoMapping(DsiBase, DsiSize); - g_clk_rst_regs = dd::GetIoMapping(ClkRstBase, ClkRstSize); - g_gpio_regs = dd::GetIoMapping(GpioBase, GpioSize); - g_apb_misc_regs = dd::GetIoMapping(ApbMiscBase, ApbMiscSize); - g_mipi_cal_regs = dd::GetIoMapping(MipiCalBase, MipiCalSize); + void InitializeRegisterVirtualAddresses() { + g_disp1_regs = dd::QueryIoMapping(Disp1Base, Disp1Size); + g_dsi_regs = dd::QueryIoMapping(DsiBase, DsiSize); + g_clk_rst_regs = dd::QueryIoMapping(ClkRstBase, ClkRstSize); + g_gpio_regs = dd::QueryIoMapping(GpioBase, GpioSize); + g_apb_misc_regs = dd::QueryIoMapping(ApbMiscBase, ApbMiscSize); + g_mipi_cal_regs = dd::QueryIoMapping(MipiCalBase, MipiCalSize); + + AMS_ABORT_UNLESS(g_disp1_regs != 0); + AMS_ABORT_UNLESS(g_dsi_regs != 0); + AMS_ABORT_UNLESS(g_clk_rst_regs != 0); + AMS_ABORT_UNLESS(g_gpio_regs != 0); + AMS_ABORT_UNLESS(g_apb_misc_regs != 0); + AMS_ABORT_UNLESS(g_mipi_cal_regs != 0); } inline void DoRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes, size_t num_writes) { @@ -98,58 +114,63 @@ namespace ams::boot { switch (g_soc_type) { case spl::SocType_Erista: DoRegisterWrites(base_address, reg_writes_erista, num_writes_erista); break; case spl::SocType_Mariko: DoRegisterWrites(base_address, reg_writes_mariko, num_writes_mariko); break; + AMS_UNREACHABLE_DEFAULT_CASE(); } } - inline void DoDsiSleepOrRegisterWrites(const DsiSleepOrRegisterWrite *reg_writes, size_t num_writes) { + inline void DoSleepOrRegisterWrites(uintptr_t base_address, const SleepOrRegisterWrite *reg_writes, size_t num_writes) { for (size_t i = 0; i < num_writes; i++) { switch (reg_writes[i].kind) { - case DsiSleepOrRegisterWriteKind_Write: - reg::Write(g_dsi_regs + sizeof(u32) * reg_writes[i].offset, reg_writes[i].value); + case SleepOrRegisterWriteKind_Write: + reg::Write(base_address + sizeof(u32) * reg_writes[i].offset, reg_writes[i].value); break; - case DsiSleepOrRegisterWriteKind_Sleep: - svcSleepThread(1'000'000ul * u64(reg_writes[i].offset)); + case SleepOrRegisterWriteKind_Sleep: + os::SleepThread(TimeSpan::FromMilliSeconds(reg_writes[i].offset)); break; AMS_UNREACHABLE_DEFAULT_CASE(); } } } -#define DO_REGISTER_WRITES(base_address, writes) DoRegisterWrites(base_address, writes, util::size(writes)) -#define DO_SOC_DEPENDENT_REGISTER_WRITES(base_address, writes) DoSocDependentRegisterWrites(base_address, writes##Erista, util::size(writes##Erista), writes##Mariko, util::size(writes##Mariko)) -#define DO_DSI_SLEEP_OR_REGISTER_WRITES(writes) DoDsiSleepOrRegisterWrites(writes, util::size(writes)) + #define DO_REGISTER_WRITES(base_address, writes) DoRegisterWrites(base_address, writes, util::size(writes)) + #define DO_SOC_DEPENDENT_REGISTER_WRITES(base_address, writes) DoSocDependentRegisterWrites(base_address, writes##Erista, util::size(writes##Erista), writes##Mariko, util::size(writes##Mariko)) + #define DO_SLEEP_OR_REGISTER_WRITES(base_address, writes) DoSleepOrRegisterWrites(base_address, writes, util::size(writes)) void InitializeFrameBuffer() { if (g_frame_buffer != nullptr) { std::memset(g_frame_buffer, 0x00, FrameBufferSize); - armDCacheFlush(g_frame_buffer, FrameBufferSize); + dd::FlushDataCache(g_frame_buffer, FrameBufferSize); } else { - const uintptr_t frame_buffer_aligned = ((reinterpret_cast(g_frame_buffer_storage) + DeviceAddressSpaceAlignMask) & ~uintptr_t(DeviceAddressSpaceAlignMask)); + const uintptr_t frame_buffer_aligned = util::AlignUp(reinterpret_cast(g_frame_buffer_storage), DeviceAddressSpaceAlignSize); g_frame_buffer = reinterpret_cast(frame_buffer_aligned); + std::memset(g_frame_buffer, 0x00, FrameBufferSize); - armDCacheFlush(g_frame_buffer, FrameBufferSize); + dd::FlushDataCache(g_frame_buffer, FrameBufferSize); /* Create Address Space. */ - R_ABORT_UNLESS(svcCreateDeviceAddressSpace(&g_dc_das_hnd, 0, (1ul << 32))); + R_ABORT_UNLESS(dd::CreateDeviceAddressSpace(std::addressof(g_device_address_space), 0, (UINT64_C(1) << 32))); + /* Attach it to the DC. */ - R_ABORT_UNLESS(svcAttachDeviceAddressSpace(svc::DeviceName_Dc, g_dc_das_hnd)); + R_ABORT_UNLESS(dd::AttachDeviceAddressSpace(std::addressof(g_device_address_space), svc::DeviceName_Dc)); /* Map the framebuffer for the DC as read-only. */ - R_ABORT_UNLESS(svcMapDeviceAddressSpaceAligned(g_dc_das_hnd, dd::GetCurrentProcessHandle(), frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr, 1)); + R_ABORT_UNLESS(dd::MapDeviceAddressSpaceAligned(std::addressof(g_device_address_space), dd::GetCurrentProcessHandle(), frame_buffer_aligned, FrameBufferSize, FrameBufferDeviceAddress, dd::MemoryPermission_ReadOnly)); } } void FinalizeFrameBuffer() { if (g_frame_buffer != nullptr) { - const uintptr_t frame_buffer_aligned = reinterpret_cast(g_frame_buffer); + const uintptr_t frame_buffer_aligned = util::AlignUp(reinterpret_cast(g_frame_buffer_storage), DeviceAddressSpaceAlignSize); /* Unmap the framebuffer from the DC. */ - R_ABORT_UNLESS(svcUnmapDeviceAddressSpace(g_dc_das_hnd, dd::GetCurrentProcessHandle(), frame_buffer_aligned, FrameBufferSize, FrameBufferPaddr)); + dd::UnmapDeviceAddressSpace(std::addressof(g_device_address_space), dd::GetCurrentProcessHandle(), frame_buffer_aligned, FrameBufferSize, FrameBufferDeviceAddress); + /* Detach address space from the DC. */ - R_ABORT_UNLESS(svcDetachDeviceAddressSpace(svc::DeviceName_Dc, g_dc_das_hnd)); - /* Close the address space. */ - R_ABORT_UNLESS(svcCloseHandle(g_dc_das_hnd)); - g_dc_das_hnd = INVALID_HANDLE; + dd::DetachDeviceAddressSpace(std::addressof(g_device_address_space), svc::DeviceName_Dc); + + /* Destroy the address space. */ + dd::DestroyDeviceAddressSpace(std::addressof(g_device_address_space)); + g_frame_buffer = nullptr; } } @@ -182,73 +203,146 @@ namespace ams::boot { } } + void EnableBacklightForVendor2050ForHardwareTypeFive(int brightness) { + /* Enable FRAME_END_INT */ + reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_ENABLE, 2); + + /* Configure DSI_LINE_TYPE as FOUR */ + reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 1); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 9); + + /* Set and wait for FRAME_END_INT */ + reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2); + while ((reg::Read(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS) & 2) != 0) { /* ... */ } + + /* Configure display brightness. */ + const u32 brightness_val = ((0x7FF * brightness) / 100); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x339); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, (brightness_val & 0x700) | ((brightness_val & 0xFF) << 16) | 0x51); + + /* Set and wait for FRAME_END_INT */ + reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2); + while ((reg::Read(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS) & 2) != 0) { /* ... */ } + + /* Set client sync point block reset. */ + reg::Write(g_dsi_regs + sizeof(u32) * DSI_INCR_SYNCPT_CNTRL, 1); + os::SleepThread(TimeSpan::FromMilliSeconds(300)); + + /* Clear client sync point block resest. */ + reg::Write(g_dsi_regs + sizeof(u32) * DSI_INCR_SYNCPT_CNTRL, 0); + os::SleepThread(TimeSpan::FromMilliSeconds(300)); + + /* Clear DSI_LINE_TYPE config. */ + reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 0); + + /* Disable FRAME_END_INT */ + reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_ENABLE, 0); + reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2); + } + + void EnableBacklightForGeneric(int brightness) { + pwm::driver::SetScale(g_lcd_backlight_session, static_cast(brightness)); + pwm::driver::SetEnabled(g_lcd_backlight_session, true); + } + } void InitializeDisplay() { /* Setup globals. */ - InitializeRegisterBaseAddresses(); + InitializeRegisterVirtualAddresses(); g_soc_type = spl::GetSocType(); InitializeFrameBuffer(); + /* Get the hardware type. */ + const auto hw_type = spl::GetHardwareType(); + /* Turn on DSI/voltage rail. */ { - i2c::driver::Session i2c_session; - i2c::driver::Initialize(); - ON_SCOPE_EXIT { i2c::driver::Finalize(); }; - - i2c::driver::OpenSession(&i2c_session, I2cDevice_Max77620Pmic); + i2c::driver::I2cSession i2c_session; + R_ABORT_UNLESS(i2c::driver::OpenSession(std::addressof(i2c_session), i2c::DeviceCode_Max77620Pmic)); if (g_soc_type == spl::SocType_Mariko) { WriteI2cRegister(i2c_session, 0x18, 0x3A); WriteI2cRegister(i2c_session, 0x1F, 0x71); } WriteI2cRegister(i2c_session, 0x23, 0xD0); + + i2c::driver::CloseSession(i2c_session); } /* Enable MIPI CAL, DSI, DISP1, HOST1X, UART_FST_MIPI_CAL, DSIA LP clocks. */ - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_CLR, 0x1010000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_SET, 0x1010000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_CLR, 0x18000000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_SET, 0x18000000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_X_SET, 0x20000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL, 0xA); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_W_SET, 0x80000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP, 0xA); + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_MIPI_CAL_RST, ENABLE), + CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_DSI_RST, ENABLE)); - /* DPD idle. */ - WritePmcRegister(PmcBase + APBDEV_PMC_IO_DPD_REQ, 0x40000000); - WritePmcRegister(PmcBase + APBDEV_PMC_IO_DPD2_REQ, 0x40000000); + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_MIPI_CAL, ENABLE), + CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_DSI, ENABLE)); + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_HOST1X_RST, ENABLE), + CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_DISP1_RST, ENABLE)); + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_HOST1X, ENABLE), + CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_DISP1, ENABLE)); + + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_X_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_X_SET_SET_CLK_ENB_UART_FST_MIPI_CAL, ENABLE)); + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL, CLK_RST_REG_BITS_VALUE(CLK_SOURCE_UART_FST_MIPI_CAL_UART_FST_MIPI_CAL_CLK_DIVISOR, 10), + CLK_RST_REG_BITS_ENUM (CLK_SOURCE_UART_FST_MIPI_CAL_UART_FST_MIPI_CAL_CLK_SRC, PLLP_OUT3)); + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_SET_SET_CLK_ENB_DSIA_LP, ENABLE)); + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP, CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DSIA_LP_DSIA_LP_CLK_DIVISOR, 10), + CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DSIA_LP_DSIA_LP_CLK_SRC, PLLP_OUT0)); + + /* Set IO_DPD_REQ to DPD_OFF. */ + dd::WriteIoRegister(PmcBase + APBDEV_PMC_IO_DPD_REQ, reg::Encode(PMC_REG_BITS_ENUM(IO_DPD_REQ_CODE, DPD_OFF))); + dd::WriteIoRegister(PmcBase + APBDEV_PMC_IO_DPD2_REQ, reg::Encode(PMC_REG_BITS_ENUM(IO_DPD2_REQ_CODE, DPD_OFF))); /* Configure LCD pinmux tristate + passthrough. */ - reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_NFC_EN, PINMUX_TRISTATE); - reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_NFC_INT, PINMUX_TRISTATE); - reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, PINMUX_TRISTATE); - reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_EN, PINMUX_TRISTATE); - reg::ClearBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_RST, PINMUX_TRISTATE); + reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_NFC_EN, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); + reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_NFC_INT, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); + reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_BL_PWM, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); + reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_BL_EN, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); + reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_RST, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE))); - /* Configure LCD power, VDD. */ - reg::SetBits(g_gpio_regs + GPIO_PORT3_CNF_0, 0x3); - reg::SetBits(g_gpio_regs + GPIO_PORT3_OE_0, 0x3); - reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1); - svcSleepThread(10'000'000ul); - reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2); - svcSleepThread(10'000'000ul); + if (hw_type == spl::HardwareType::_Five_) { + /* Configure LCD backlight. */ + reg::SetBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x4); + reg::SetBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x4); + } else { + /* Configure LCD power, VDD. */ + reg::SetBits(g_gpio_regs + GPIO_PORT3_CNF_0, 0x3); + reg::SetBits(g_gpio_regs + GPIO_PORT3_OE_0, 0x3); + reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1); + os::SleepThread(TimeSpan::FromMilliSeconds(10)); - /* Configure LCD backlight. */ - reg::SetBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x7); - reg::SetBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x7); - reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x2); + reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2); + os::SleepThread(TimeSpan::FromMilliSeconds(10)); + + /* Configure LCD backlight to use PWM. */ + reg::ClearBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x1); + reg::Write(g_apb_misc_regs + PINMUX_AUX_LCD_BL_PWM, PINMUX_REG_BITS_ENUM(AUX_LCD_BL_PWM_PM, PWM0), + PINMUX_REG_BITS_ENUM(AUX_PUPD, PULL_DOWN)); + + /* Configure LCD backlight. */ + R_ABORT_UNLESS(pwm::driver::OpenSession(std::addressof(g_lcd_backlight_session), pwm::DeviceCode_LcdBacklight)); + pwm::driver::SetPeriod(g_lcd_backlight_session, TimeSpan::FromNanoSeconds(33898)); + + reg::SetBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x6); + reg::SetBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x6); + reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x2); + } /* Configure display interface and display. */ - reg::Write(g_mipi_cal_regs + 0x060, 0); + reg::Write(g_mipi_cal_regs + MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0); if (g_soc_type == spl::SocType_Mariko) { - reg::Write(g_mipi_cal_regs + 0x058, 0); - reg::Write(g_apb_misc_regs + 0xAC0, 0); + reg::Write(g_mipi_cal_regs + MIPI_CAL_MIPI_BIAS_PAD_CFG0, 0); + reg::Write(g_apb_misc_regs + APB_MISC_GP_DSI_PAD_CONTROL, 0); } /* Execute configs. */ DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01); - DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc01); + DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc01); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init01); /* NOTE: Nintendo bug here. */ /* As of 8.0.0, Nintendo writes this list to CAR instead of DSI */ @@ -261,14 +355,18 @@ namespace ams::boot { DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init06); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init07); - - svcSleepThread(10'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(10)); /* Enable backlight reset. */ reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4); - svcSleepThread(60'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(60)); + + if (hw_type == spl::HardwareType::_Five_) { + reg::Write(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x40103); + } else { + reg::Write(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x50204); + } - reg::Write(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x50204); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x337); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); WaitDsiTrigger(); @@ -279,7 +377,7 @@ namespace ams::boot { reg::Write(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_IMM_BTA | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC); WaitDsiHostControl(); - svcSleepThread(5'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); /* Parse LCD vendor. */ { @@ -303,62 +401,77 @@ namespace ams::boot { /* LCD vendor specific configuration. */ switch (g_lcd_vendor) { - case 0xF30: /* AUO first revision screens. */ - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(180'000'000ul); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(5'000'000ul); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(5'000'000ul); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + case 0x10: /* Japan Display Inc screens. */ + DO_SLEEP_OR_REGISTER_WRITES(g_dsi_regs, DisplayConfigJdiSpecificInit01); break; case 0xF20: /* Innolux first revision screens. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(180'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(180)); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(5'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(5'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); break; - case 0x10: /* Japan Display Inc screens. */ - DO_DSI_SLEEP_OR_REGISTER_WRITES(DisplayConfigJdiSpecificInit01); + case 0xF30: /* AUO first revision screens. */ + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + os::SleepThread(TimeSpan::FromMilliSeconds(180)); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); break; + case 0x2050: /* Unknown (hardware type 5) screen. */ + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + os::SleepThread(TimeSpan::FromMilliSeconds(180)); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xA015); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x205315); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x339); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x51); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + break; + case 0x1020: /* Innolux second revision screen. */ + case 0x1030: /* AUO second revision screen. */ + case 0x1040: /* Unknown second revision screen. */ default: - /* Innolux and AUO second revision screens. */ - if ((g_lcd_vendor | 0x10) == 0x1030) { - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(120'000'000ul); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); - reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - } + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + os::SleepThread(TimeSpan::FromMilliSeconds(120)); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); break; } - svcSleepThread(20'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(20)); DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld02); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init08); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming); - DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init09); + DO_SLEEP_OR_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init09); reg::Write(g_disp1_regs + sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, SHIFT_CLK_DIVIDER(4)); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init10); - svcSleepThread(10'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(10)); /* Configure MIPI CAL. */ DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal01); @@ -373,12 +486,14 @@ namespace ams::boot { DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03); DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04); } - svcSleepThread(10'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(10)); /* Write DISP1, FrameBuffer config. */ - DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc02); - DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigFrameBuffer); - svcSleepThread(35'000'000ul); + DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc02); + DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigFrameBuffer); + if (g_lcd_vendor != 0x2050) { + os::SleepThread(TimeSpan::FromMilliSeconds(35)); + } g_is_display_intialized = true; } @@ -396,10 +511,14 @@ namespace ams::boot { } } } - armDCacheFlush(g_frame_buffer, FrameBufferSize); + dd::FlushDataCache(g_frame_buffer, FrameBufferSize); /* Enable backlight. */ - reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1); + if (g_lcd_vendor == 0x2050) { + EnableBacklightForVendor2050ForHardwareTypeFive(g_display_brightness); + } else { + EnableBacklightForGeneric(g_display_brightness); + } } void FinalizeDisplay() { @@ -408,14 +527,19 @@ namespace ams::boot { } /* Disable backlight. */ - reg::ClearBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1); + if (g_lcd_vendor == 0x2050) { + EnableBacklightForVendor2050ForHardwareTypeFive(0); + } else { + pwm::driver::SetEnabled(g_lcd_backlight_session, false); + pwm::driver::CloseSession(g_lcd_backlight_session); + } reg::Write(g_disp1_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 1); reg::Write(g_disp1_regs + sizeof(u32) * DSI_WR_DATA, 0x2805); /* Nintendo waits 5 frames before continuing. */ { - const uintptr_t host1x_vaddr = dd::GetIoMapping(0x500030a4, 4); + const uintptr_t host1x_vaddr = dd::GetIoMapping(0x500030A4, 4); const u32 start_val = reg::Read(host1x_vaddr); while (reg::Read(host1x_vaddr) < start_val + 5) { /* spinlock here. */ @@ -425,80 +549,105 @@ namespace ams::boot { reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_STATE_ACCESS, (READ_MUX | WRITE_MUX)); reg::Write(g_disp1_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 0); + DO_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc01Fini01); + os::SleepThread(TimeSpan::FromMilliSeconds(40)); DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Fini01); DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming); DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Fini02); - svcSleepThread(10'000'000ul); + if (g_lcd_vendor != 0x2050) { + os::SleepThread(TimeSpan::FromMilliSeconds(10)); + } /* Vendor specific shutdown. */ switch (g_lcd_vendor) { case 0x10: /* Japan Display Inc screens. */ - DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigJdiSpecificFini01); + DO_SLEEP_OR_REGISTER_WRITES(g_dsi_regs, DisplayConfigJdiSpecificFini01); break; case 0xF30: /* AUO first revision screens. */ - DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigAuoRev1SpecificFini01); - svcSleepThread(5'000'000ul); + DO_SLEEP_OR_REGISTER_WRITES(g_dsi_regs, DisplayConfigAuoRev1SpecificFini01); break; case 0x1020: /* Innolux second revision screens. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(5'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71143209); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x115631); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(5'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); break; case 0x1030: /* AUO second revision screens. */ reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(5'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71143209); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x114D31); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(5'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); + break; + case 0x1040: /* Unknown second revision screens. */ + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xB39); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x731348B1); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x71243209); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x4C31); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); + os::SleepThread(TimeSpan::FromMilliSeconds(5)); break; default: break; } - svcSleepThread(5'000'000ul); reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1005); reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST); - svcSleepThread(50'000'000ul); + os::SleepThread(g_lcd_vendor == 0x2050 ? TimeSpan::FromMilliSeconds(120) : TimeSpan::FromMilliSeconds(50)); /* Disable backlight RST/Voltage. */ reg::ClearBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4); - svcSleepThread(10'000'000ul); - reg::ClearBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2); - svcSleepThread(10'000'000ul); - reg::ClearBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1); - svcSleepThread(10'000'000ul); + if (g_lcd_vendor == 0x2050) { + os::SleepThread(TimeSpan::FromMilliSeconds(30)); + } else { + os::SleepThread(TimeSpan::FromMilliSeconds(10)); + reg::ClearBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2); + os::SleepThread(TimeSpan::FromMilliSeconds(10)); + reg::ClearBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1); + os::SleepThread(TimeSpan::FromMilliSeconds(10)); + } /* Cut clock to DSI. */ - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_SET, 0x1010000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, 0x1010000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_SET, 0x18000000); - reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, 0x18000000); + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_MIPI_CAL_RST, ENABLE), + CLK_RST_REG_BITS_ENUM(RST_DEV_H_SET_SET_DSI_RST, ENABLE)); + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_CLR_CLR_CLK_ENB_MIPI_CAL, ENABLE), + CLK_RST_REG_BITS_ENUM(CLK_ENB_H_CLR_CLR_CLK_ENB_DSI, ENABLE)); + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_HOST1X_RST, ENABLE), + CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_DISP1_RST, ENABLE)); + + reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLR_CLR_CLK_ENB_HOST1X, ENABLE), + CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLR_CLR_CLK_ENB_DISP1, ENABLE)); + reg::Write(g_dsi_regs + sizeof(u32) * DSI_PAD_CONTROL_0, (DSI_PAD_CONTROL_VS1_PULLDN_CLK | DSI_PAD_CONTROL_VS1_PULLDN(0xF) | DSI_PAD_CONTROL_VS1_PDIO_CLK | DSI_PAD_CONTROL_VS1_PDIO(0xF))); reg::Write(g_dsi_regs + sizeof(u32) * DSI_POWER_CONTROL, 0); - /* Final LCD config for PWM */ - reg::ClearBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x1); - reg::SetBits(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, PINMUX_TRISTATE); - reg::ReadWrite(g_apb_misc_regs + 0x3000 + PINMUX_AUX_LCD_BL_PWM, 1, 0x3); - /* Unmap framebuffer from DC virtual address space. */ FinalizeFrameBuffer(); g_is_display_intialized = false; } + void SetDisplayBrightness(int percentage) { + g_display_brightness = percentage; + } + } diff --git a/stratosphere/boot/source/boot_display.hpp b/stratosphere/boot/source/boot_display.hpp index e046e1ebc..de58eec03 100644 --- a/stratosphere/boot/source/boot_display.hpp +++ b/stratosphere/boot/source/boot_display.hpp @@ -23,4 +23,6 @@ namespace ams::boot { void ShowDisplay(size_t x, size_t y, size_t width, size_t height, const u32 *img); void FinalizeDisplay(); + void SetDisplayBrightness(int percentage); + } diff --git a/stratosphere/boot/source/boot_display_config.inc b/stratosphere/boot/source/boot_display_config.inc index 246b8eea8..21377b97c 100644 --- a/stratosphere/boot/source/boot_display_config.inc +++ b/stratosphere/boot/source/boot_display_config.inc @@ -19,135 +19,135 @@ struct RegisterWrite { u32 value; }; -enum DsiSleepOrRegisterWriteKind : u16 { - DsiSleepOrRegisterWriteKind_Write = 0, - DsiSleepOrRegisterWriteKind_Sleep = 1, +enum SleepOrRegisterWriteKind : u16 { + SleepOrRegisterWriteKind_Write = 0, + SleepOrRegisterWriteKind_Sleep = 1, }; -struct DsiSleepOrRegisterWrite { - DsiSleepOrRegisterWriteKind kind; +struct SleepOrRegisterWrite { + SleepOrRegisterWriteKind kind; u16 offset; u32 value; }; -constexpr RegisterWrite DisplayConfigPlld01Erista[] = { +constexpr const RegisterWrite DisplayConfigPlld01Erista[] = { {CLK_RST_CONTROLLER_CLK_SOURCE_DISP1, 0x40000000}, {CLK_RST_CONTROLLER_PLLD_BASE, 0x4830A001}, {CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000020}, {CLK_RST_CONTROLLER_PLLD_MISC, 0x002D0AAA}, }; -constexpr RegisterWrite DisplayConfigPlld01Mariko[] = { +constexpr const RegisterWrite DisplayConfigPlld01Mariko[] = { {CLK_RST_CONTROLLER_CLK_SOURCE_DISP1, 0x40000000}, {CLK_RST_CONTROLLER_PLLD_BASE, 0x4830A001}, {CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000000}, {CLK_RST_CONTROLLER_PLLD_MISC, 0x002DFC00}, }; -constexpr RegisterWrite DisplayConfigDc01[] = { - {sizeof(u32) * DC_CMD_STATE_ACCESS, 0}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, - {sizeof(u32) * DC_CMD_REG_ACT_CONTROL, 0x54}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_DISP_DC_MCCIF_FIFOCTRL, 0}, - {sizeof(u32) * DC_DISP_DISP_MEM_HIGH_PRIORITY, 0}, - {sizeof(u32) * DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_POWER_CONTROL, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE}, - {sizeof(u32) * DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL}, - {sizeof(u32) * DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE | 0x9}, // 9: SYNCPT - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}, - {sizeof(u32) * DC_CMD_STATE_ACCESS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_DV_CONTROL, 0}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, +constexpr const SleepOrRegisterWrite DisplayConfigDc01[] = { + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_ACCESS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_REG_ACT_CONTROL, 0x54}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DC_MCCIF_FIFOCTRL, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_MEM_HIGH_PRIORITY, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_POWER_CONTROL, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL, SYNCPT_CNTRL_NO_STALL}, + {SleepOrRegisterWriteKind_Write, DC_CMD_CONT_SYNCPT_VSYNC, SYNCPT_VSYNC_ENABLE | 0x9}, // 9: SYNCPT + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_ACCESS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_DV_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, /* Setup default YUV colorspace conversion coefficients */ - {sizeof(u32) * DC_WIN_CSC_YOF, 0xF0}, - {sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A}, - {sizeof(u32) * DC_WIN_CSC_KUR, 0}, - {sizeof(u32) * DC_WIN_CSC_KVR, 0x198}, - {sizeof(u32) * DC_WIN_CSC_KUG, 0x39B}, - {sizeof(u32) * DC_WIN_CSC_KVG, 0x32F}, - {sizeof(u32) * DC_WIN_CSC_KUB, 0x204}, - {sizeof(u32) * DC_WIN_CSC_KVB, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_YOF, 0xF0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KYRGB, 0x12A}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUR, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVR, 0x198}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUG, 0x39B}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVG, 0x32F}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUB, 0x204}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVB, 0}, /* End of color coefficients */ - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_DV_CONTROL, 0}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_DV_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, /* Setup default YUV colorspace conversion coefficients */ - {sizeof(u32) * DC_WIN_CSC_YOF, 0xF0}, - {sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A}, - {sizeof(u32) * DC_WIN_CSC_KUR, 0}, - {sizeof(u32) * DC_WIN_CSC_KVR, 0x198}, - {sizeof(u32) * DC_WIN_CSC_KUG, 0x39B}, - {sizeof(u32) * DC_WIN_CSC_KVG, 0x32F}, - {sizeof(u32) * DC_WIN_CSC_KUB, 0x204}, - {sizeof(u32) * DC_WIN_CSC_KVB, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_YOF, 0xF0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KYRGB, 0x12A}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUR, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVR, 0x198}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUG, 0x39B}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVG, 0x32F}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUB, 0x204}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVB, 0}, /* End of color coefficients */ - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_DV_CONTROL, 0}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_DV_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, /* Setup default YUV colorspace conversion coefficients */ - {sizeof(u32) * DC_WIN_CSC_YOF, 0xF0}, - {sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A}, - {sizeof(u32) * DC_WIN_CSC_KUR, 0}, - {sizeof(u32) * DC_WIN_CSC_KVR, 0x198}, - {sizeof(u32) * DC_WIN_CSC_KUG, 0x39B}, - {sizeof(u32) * DC_WIN_CSC_KVG, 0x32F}, - {sizeof(u32) * DC_WIN_CSC_KUB, 0x204}, - {sizeof(u32) * DC_WIN_CSC_KVB, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_YOF, 0xF0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KYRGB, 0x12A}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUR, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVR, 0x198}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUG, 0x39B}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVG, 0x32F}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUB, 0x204}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVB, 0}, /* End of color coefficients */ - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888}, - {sizeof(u32) * DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C}, - {sizeof(u32) * DC_COM_PIN_OUTPUT_POLARITY(1), 0x1000000}, - {sizeof(u32) * DC_COM_PIN_OUTPUT_POLARITY(3), 0}, - {sizeof(u32) * 0x4E4, 0}, - {sizeof(u32) * DC_COM_CRC_CONTROL, 0}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * 0x716, 0x10000FF}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * 0x716, 0x10000FF}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * 0x716, 0x10000FF}, - {sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_COMMAND, 0}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ} + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C}, + {SleepOrRegisterWriteKind_Write, DC_COM_PIN_OUTPUT_POLARITY(1), 0x1000000}, + {SleepOrRegisterWriteKind_Write, DC_COM_PIN_OUTPUT_POLARITY(3), 0}, + {SleepOrRegisterWriteKind_Write, 0x4E4, 0}, + {SleepOrRegisterWriteKind_Write, DC_COM_CRC_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, 0x716, 0x10000FF}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, 0x716, 0x10000FF}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, 0x716, 0x10000FF}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_COMMAND_OPTION0, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_COMMAND, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ} }; -constexpr RegisterWrite DisplayConfigDsi01Init01[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init01[] = { {sizeof(u32) * DSI_WR_DATA, 0x0}, {sizeof(u32) * DSI_INT_ENABLE, 0x0}, {sizeof(u32) * DSI_INT_STATUS, 0x0}, @@ -158,15 +158,15 @@ constexpr RegisterWrite DisplayConfigDsi01Init01[] = { {sizeof(u32) * DSI_INIT_SEQ_DATA_3, 0x0}, }; -constexpr RegisterWrite DisplayConfigDsi01Init02Erista[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init02Erista[] = { {sizeof(u32) * DSI_INIT_SEQ_DATA_15, 0x0}, }; -constexpr RegisterWrite DisplayConfigDsi01Init02Mariko[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init02Mariko[] = { {sizeof(u32) * DSI_INIT_SEQ_DATA_15_MARIKO, 0x0}, }; -constexpr RegisterWrite DisplayConfigDsi01Init03[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init03[] = { {sizeof(u32) * DSI_DCS_CMDS, 0}, {sizeof(u32) * DSI_PKT_SEQ_0_LO, 0}, {sizeof(u32) * DSI_PKT_SEQ_1_LO, 0}, @@ -182,11 +182,11 @@ constexpr RegisterWrite DisplayConfigDsi01Init03[] = { {sizeof(u32) * DSI_PKT_SEQ_5_HI, 0}, }; -constexpr RegisterWrite DisplayConfigDsi01Init04Erista[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init04Erista[] = { /* No register writes. */ }; -constexpr RegisterWrite DisplayConfigDsi01Init04Mariko[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init04Mariko[] = { {sizeof(u32) * DSI_PAD_CONTROL_1, 0}, {sizeof(u32) * DSI_PAD_CONTROL_2, 0}, {sizeof(u32) * DSI_PAD_CONTROL_3, 0}, @@ -196,7 +196,7 @@ constexpr RegisterWrite DisplayConfigDsi01Init04Mariko[] = { {sizeof(u32) * DSI_PAD_CONTROL_7_MARIKO, 0}, }; -constexpr RegisterWrite DisplayConfigDsi01Init05[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init05[] = { {sizeof(u32) * DSI_PAD_CONTROL_CD, 0}, {sizeof(u32) * DSI_SOL_DELAY, 0x18}, {sizeof(u32) * DSI_MAX_THRESHOLD, 0x1E0}, @@ -209,7 +209,7 @@ constexpr RegisterWrite DisplayConfigDsi01Init05[] = { {sizeof(u32) * DSI_PAD_CONTROL_1, 0}, }; -constexpr RegisterWrite DisplayConfigDsi01Init06[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init06[] = { {sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05}, {sizeof(u32) * DSI_PHY_TIMING_2, 0x30109}, {sizeof(u32) * DSI_BTA_TIMING, 0x190A14}, @@ -225,7 +225,7 @@ constexpr RegisterWrite DisplayConfigDsi01Init06[] = { }; -constexpr RegisterWrite DisplayConfigDsi01Init07[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init07[] = { {sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05}, {sizeof(u32) * DSI_PHY_TIMING_2, 0x30118}, {sizeof(u32) * DSI_BTA_TIMING, 0x190A14}, @@ -242,124 +242,124 @@ constexpr RegisterWrite DisplayConfigDsi01Init07[] = { {sizeof(u32) * DSI_INIT_SEQ_CONTROL, 0} }; -constexpr RegisterWrite DisplayConfigDsiPhyTimingErista[] = { +constexpr const RegisterWrite DisplayConfigDsiPhyTimingErista[] = { {sizeof(u32) * DSI_PHY_TIMING_0, 0x6070601}, }; -constexpr RegisterWrite DisplayConfigDsiPhyTimingMariko[] = { +constexpr const RegisterWrite DisplayConfigDsiPhyTimingMariko[] = { {sizeof(u32) * DSI_PHY_TIMING_0, 0x6070603}, }; -constexpr DsiSleepOrRegisterWrite DisplayConfigJdiSpecificInit01[] = { - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x9483FFB9}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xBD15}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1939}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAD8}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAEB}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAEBAAAA}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAAA}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAEB}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAEBAAAA}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAA}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1BD15}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2739}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFD8}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2BD15}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xF39}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFD8}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFF}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xBD15}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x6D915}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xB9}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1105}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, - {DsiSleepOrRegisterWriteKind_Sleep, 0xB4, 0}, - {DsiSleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2905}, - {DsiSleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, +constexpr const SleepOrRegisterWrite DisplayConfigJdiSpecificInit01[] = { + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x9483FFB9}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xBD15}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1939}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAD8}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAEB}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAEBAAAA}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAAA}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAAAAAEB}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAAEBAAAA}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xAA}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1BD15}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2739}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFD8}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2BD15}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xF39}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFD8}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xFFFFFF}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xBD15}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x6D915}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xB9}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x1105}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Sleep, 180, 0}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2905}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, }; -constexpr RegisterWrite DisplayConfigPlld02Erista[] = { +constexpr const RegisterWrite DisplayConfigPlld02Erista[] = { {CLK_RST_CONTROLLER_PLLD_BASE, 0x4810c001}, {CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000020}, {CLK_RST_CONTROLLER_PLLD_MISC, 0x002D0AAA}, }; -constexpr RegisterWrite DisplayConfigPlld02Mariko[] = { +constexpr const RegisterWrite DisplayConfigPlld02Mariko[] = { {CLK_RST_CONTROLLER_PLLD_BASE, 0x4810c001}, {CLK_RST_CONTROLLER_PLLD_MISC1, 0x00000000}, {CLK_RST_CONTROLLER_PLLD_MISC, 0x002DFC00}, }; -constexpr RegisterWrite DisplayConfigDsi01Init08[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init08[] = { {sizeof(u32) * DSI_PAD_CONTROL_1, 0}, }; -constexpr RegisterWrite DisplayConfigDsi01Init09[] = { - {sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05}, - {sizeof(u32) * DSI_PHY_TIMING_2, 0x30172}, - {sizeof(u32) * DSI_BTA_TIMING, 0x190A14}, - {sizeof(u32) * DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xA40)}, - {sizeof(u32) * DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x5A2F) | DSI_TIMEOUT_TA(0x2000)}, - {sizeof(u32) * DSI_TO_TALLY, 0}, - {sizeof(u32) * DSI_PKT_SEQ_0_LO, 0x40000208}, - {sizeof(u32) * DSI_PKT_SEQ_2_LO, 0x40000308}, - {sizeof(u32) * DSI_PKT_SEQ_4_LO, 0x40000308}, - {sizeof(u32) * DSI_PKT_SEQ_1_LO, 0x40000308}, - {sizeof(u32) * DSI_PKT_SEQ_3_LO, 0x3F3B2B08}, - {sizeof(u32) * DSI_PKT_SEQ_3_HI, 0x2CC}, - {sizeof(u32) * DSI_PKT_SEQ_5_LO, 0x3F3B2B08}, - {sizeof(u32) * DSI_PKT_SEQ_5_HI, 0x2CC}, - {sizeof(u32) * DSI_PKT_LEN_0_1, 0xCE0000}, - {sizeof(u32) * DSI_PKT_LEN_2_3, 0x87001A2}, - {sizeof(u32) * DSI_PKT_LEN_4_5, 0x190}, - {sizeof(u32) * DSI_PKT_LEN_6_7, 0x190}, - {sizeof(u32) * DSI_HOST_CONTROL, 0}, +constexpr const SleepOrRegisterWrite DisplayConfigDsi01Init09[] = { + {SleepOrRegisterWriteKind_Write, DSI_PHY_TIMING_1, 0x40A0E05}, + {SleepOrRegisterWriteKind_Write, DSI_PHY_TIMING_2, 0x30172}, + {SleepOrRegisterWriteKind_Write, DSI_BTA_TIMING, 0x190A14}, + {SleepOrRegisterWriteKind_Write, DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xA40)}, + {SleepOrRegisterWriteKind_Write, DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x5A2F) | DSI_TIMEOUT_TA(0x2000)}, + {SleepOrRegisterWriteKind_Write, DSI_TO_TALLY, 0}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_SEQ_0_LO, 0x40000208}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_SEQ_2_LO, 0x40000308}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_SEQ_4_LO, 0x40000308}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_SEQ_1_LO, 0x40000308}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_SEQ_3_LO, 0x3F3B2B08}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_SEQ_3_HI, 0x2CC}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_SEQ_5_LO, 0x3F3B2B08}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_SEQ_5_HI, 0x2CC}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_LEN_0_1, 0xCE0000}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_LEN_2_3, 0x87001A2}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_LEN_4_5, 0x190}, + {SleepOrRegisterWriteKind_Write, DSI_PKT_LEN_6_7, 0x190}, + {SleepOrRegisterWriteKind_Write, DSI_HOST_CONTROL, 0}, }; -constexpr RegisterWrite DisplayConfigDsi01Init10[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init10[] = { {sizeof(u32) * DSI_TRIGGER, 0}, {sizeof(u32) * DSI_CONTROL, 0}, {sizeof(u32) * DSI_SOL_DELAY, 6}, {sizeof(u32) * DSI_MAX_THRESHOLD, 0x1E0}, {sizeof(u32) * DSI_POWER_CONTROL, DSI_POWER_CONTROL_ENABLE}, {sizeof(u32) * DSI_CONTROL, DSI_CONTROL_HS_CLK_CTRL | DSI_CONTROL_FORMAT(3) | DSI_CONTROL_LANES(3) | DSI_CONTROL_VIDEO_ENABLE}, - {sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_FIFO_SEL| DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC}, + {sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_FIFO_SEL | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC}, {sizeof(u32) * DSI_CONTROL, DSI_CONTROL_HS_CLK_CTRL | DSI_CONTROL_FORMAT(3) | DSI_CONTROL_LANES(3) | DSI_CONTROL_VIDEO_ENABLE}, {sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC}, {sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC} }; -constexpr RegisterWrite DisplayConfigDsi01Init11Erista[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init11Erista[] = { {sizeof(u32) * DSI_PAD_CONTROL_1, 0}, {sizeof(u32) * DSI_PAD_CONTROL_2, 0}, {sizeof(u32) * DSI_PAD_CONTROL_3, DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3)}, {sizeof(u32) * DSI_PAD_CONTROL_4, 0} }; -constexpr RegisterWrite DisplayConfigDsi01Init11Mariko[] = { +constexpr const RegisterWrite DisplayConfigDsi01Init11Mariko[] = { {sizeof(u32) * DSI_PAD_CONTROL_1, 0}, {sizeof(u32) * DSI_PAD_CONTROL_2, 0}, {sizeof(u32) * DSI_PAD_CONTROL_3, 0}, @@ -369,226 +369,242 @@ constexpr RegisterWrite DisplayConfigDsi01Init11Mariko[] = { {sizeof(u32) * DSI_PAD_CONTROL_7_MARIKO, 0}, }; -constexpr RegisterWrite DisplayConfigMipiCal01[] = { - {0x60, 0}, - {0x08, 0xF3F10000}, - {0x58, 1}, - {0x60, 0}, +constexpr const RegisterWrite DisplayConfigMipiCal01[] = { + {MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0}, + {MIPI_CAL_CIL_MIPI_CAL_STATUS, 0xF3F10000}, + {MIPI_CAL_MIPI_BIAS_PAD_CFG0, 1}, + {MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0}, }; -constexpr RegisterWrite DisplayConfigMipiCal02Erista[] = { - {0x60, 0x10010}, - {0x5C, 0x300}, +constexpr const RegisterWrite DisplayConfigMipiCal02Erista[] = { + {MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0x10010}, + {MIPI_CAL_MIPI_BIAS_PAD_CFG1, 0x300}, }; -constexpr RegisterWrite DisplayConfigMipiCal02Mariko[] = { - {0x60, 0x10010}, - {0x5C, 0}, +constexpr const RegisterWrite DisplayConfigMipiCal02Mariko[] = { + {MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0x10010}, + {MIPI_CAL_MIPI_BIAS_PAD_CFG1, 0}, }; -constexpr RegisterWrite DisplayConfigMipiCal03Erista[] = { - {0x38, 0x200200}, - {0x3C, 0x200200}, - {0x64, 0x200002}, - {0x68, 0x200002}, - {0x14, 0}, - {0x18, 0}, +constexpr const RegisterWrite DisplayConfigMipiCal03Erista[] = { + {MIPI_CAL_DSIA_MIPI_CAL_CONFIG, 0x200200}, + {MIPI_CAL_DSIB_MIPI_CAL_CONFIG, 0x200200}, + {MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2, 0x200002}, + {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0x200002}, + {MIPI_CAL_CILA_MIPI_CAL_CONFIG, 0}, + {MIPI_CAL_CILB_MIPI_CAL_CONFIG, 0}, }; -constexpr RegisterWrite DisplayConfigMipiCal03Mariko[] = { - {0x38, 0x200006}, - {0x3C, 0x200006}, - {0x64, 0x260000}, - {0x68, 0x260000}, - {0x14, 0}, - {0x18, 0}, +constexpr const RegisterWrite DisplayConfigMipiCal03Mariko[] = { + {MIPI_CAL_DSIA_MIPI_CAL_CONFIG, 0x200006}, + {MIPI_CAL_DSIB_MIPI_CAL_CONFIG, 0x200006}, + {MIPI_CAL_DSIA_MIPI_CAL_CONFIG_2, 0x260000}, + {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0x260000}, + {MIPI_CAL_CILA_MIPI_CAL_CONFIG, 0}, + {MIPI_CAL_CILB_MIPI_CAL_CONFIG, 0}, }; -constexpr RegisterWrite DisplayConfigMipiCal04[] = { - {0x1C, 0}, - {0x20, 0}, - {0x24, 0}, - {0x28, 0}, - {0x40, 0}, - {0x44, 0}, - {0x68, 0}, - {0x70, 0}, - {0x74, 0}, - {0x00, 0x2A000001}, +constexpr const RegisterWrite DisplayConfigMipiCal04[] = { + {MIPI_CAL_CILC_MIPI_CAL_CONFIG, 0}, + {MIPI_CAL_CILD_MIPI_CAL_CONFIG, 0}, + {MIPI_CAL_CILE_MIPI_CAL_CONFIG, 0}, + {MIPI_CAL_CILF_MIPI_CAL_CONFIG, 0}, + {MIPI_CAL_DSIC_MIPI_CAL_CONFIG, 0}, + {MIPI_CAL_DSID_MIPI_CAL_CONFIG, 0}, + {MIPI_CAL_DSIB_MIPI_CAL_CONFIG_2, 0}, + {MIPI_CAL_DSIC_MIPI_CAL_CONFIG_2, 0}, + {MIPI_CAL_DSID_MIPI_CAL_CONFIG_2, 0}, + {MIPI_CAL_MIPI_CAL_CTRL, 0x2A000001}, }; -constexpr RegisterWrite DisplayConfigDc02[] = { - {sizeof(u32) * DC_CMD_STATE_ACCESS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_DV_CONTROL, 0}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, +constexpr const SleepOrRegisterWrite DisplayConfigDc02[] = { + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_ACCESS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_DV_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, /* Setup default YUV colorspace conversion coefficients */ - {sizeof(u32) * DC_WIN_CSC_YOF, 0xF0}, - {sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A}, - {sizeof(u32) * DC_WIN_CSC_KUR, 0}, - {sizeof(u32) * DC_WIN_CSC_KVR, 0x198}, - {sizeof(u32) * DC_WIN_CSC_KUG, 0x39B}, - {sizeof(u32) * DC_WIN_CSC_KVG, 0x32F}, - {sizeof(u32) * DC_WIN_CSC_KUB, 0x204}, - {sizeof(u32) * DC_WIN_CSC_KVB, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_YOF, 0xF0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KYRGB, 0x12A}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUR, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVR, 0x198}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUG, 0x39B}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVG, 0x32F}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUB, 0x204}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVB, 0}, /* End of color coefficients */ - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_DV_CONTROL, 0}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_DV_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, /* Setup default YUV colorspace conversion coefficients */ - {sizeof(u32) * DC_WIN_CSC_YOF, 0xF0}, - {sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A}, - {sizeof(u32) * DC_WIN_CSC_KUR, 0}, - {sizeof(u32) * DC_WIN_CSC_KVR, 0x198}, - {sizeof(u32) * DC_WIN_CSC_KUG, 0x39B}, - {sizeof(u32) * DC_WIN_CSC_KVG, 0x32F}, - {sizeof(u32) * DC_WIN_CSC_KUB, 0x204}, - {sizeof(u32) * DC_WIN_CSC_KVB, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_YOF, 0xF0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KYRGB, 0x12A}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUR, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVR, 0x198}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUG, 0x39B}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVG, 0x32F}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUB, 0x204}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVB, 0}, /* End of color coefficients */ - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_DV_CONTROL, 0}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_DV_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, /* Setup default YUV colorspace conversion coefficients */ - {sizeof(u32) * DC_WIN_CSC_YOF, 0xF0}, - {sizeof(u32) * DC_WIN_CSC_KYRGB, 0x12A}, - {sizeof(u32) * DC_WIN_CSC_KUR, 0}, - {sizeof(u32) * DC_WIN_CSC_KVR, 0x198}, - {sizeof(u32) * DC_WIN_CSC_KUG, 0x39B}, - {sizeof(u32) * DC_WIN_CSC_KVG, 0x32F}, - {sizeof(u32) * DC_WIN_CSC_KUB, 0x204}, - {sizeof(u32) * DC_WIN_CSC_KVB, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_YOF, 0xF0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KYRGB, 0x12A}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUR, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVR, 0x198}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUG, 0x39B}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVG, 0x32F}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KUB, 0x204}, + {SleepOrRegisterWriteKind_Write, DC_WIN_CSC_KVB, 0}, /* End of color coefficients */ - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888}, - {sizeof(u32) * DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C}, - {sizeof(u32) * DC_COM_PIN_OUTPUT_POLARITY(1), 0x1000000}, - {sizeof(u32) * DC_COM_PIN_OUTPUT_POLARITY(3), 0}, - {sizeof(u32) * 0x4E4, 0}, - {sizeof(u32) * DC_COM_CRC_CONTROL, 0}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * 0x716, 0x10000FF}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * 0x716, 0x10000FF}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * 0x716, 0x10000FF}, - {sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_COMMAND, 0}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}, - {sizeof(u32) * DC_CMD_STATE_ACCESS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C}, + {SleepOrRegisterWriteKind_Write, DC_COM_PIN_OUTPUT_POLARITY(1), 0x1000000}, + {SleepOrRegisterWriteKind_Write, DC_COM_PIN_OUTPUT_POLARITY(3), 0}, + {SleepOrRegisterWriteKind_Write, 0x4E4, 0}, + {SleepOrRegisterWriteKind_Write, DC_COM_CRC_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, 0x716, 0x10000FF}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, 0x716, 0x10000FF}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, 0x716, 0x10000FF}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_COMMAND_OPTION0, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_COMMAND, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_ACCESS, 0}, /* Set Display timings */ - {sizeof(u32) * DC_DISP_DISP_TIMING_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_REF_TO_SYNC, (1 << 16)}, // h_ref_to_sync = 0, v_ref_to_sync = 1. - {sizeof(u32) * DC_DISP_SYNC_WIDTH, 0x10048}, - {sizeof(u32) * DC_DISP_BACK_PORCH, 0x90048}, - {sizeof(u32) * DC_DISP_ACTIVE, 0x50002D0}, - {sizeof(u32) * DC_DISP_FRONT_PORCH, 0xA0088}, // Sources say that this should be above the DC_DISP_ACTIVE cmd. + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_TIMING_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_REF_TO_SYNC, (1 << 16)}, // h_ref_to_sync = 0, v_ref_to_sync = 1. + {SleepOrRegisterWriteKind_Write, DC_DISP_SYNC_WIDTH, 0x10048}, + {SleepOrRegisterWriteKind_Write, DC_DISP_BACK_PORCH, 0x90048}, + {SleepOrRegisterWriteKind_Write, DC_DISP_ACTIVE, 0x50002D0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_FRONT_PORCH, 0xA0088}, // Sources say that this should be above the DC_DISP_ACTIVE cmd. /* End of Display timings */ - {sizeof(u32) * DC_DISP_SHIFT_CLOCK_OPTIONS, SC1_H_QUALIFIER_NONE | SC0_H_QUALIFIER_NONE}, - {sizeof(u32) * DC_COM_PIN_OUTPUT_ENABLE(1), 0}, - {sizeof(u32) * DC_DISP_DATA_ENABLE_OPTIONS, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL}, - {sizeof(u32) * DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C}, - {sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, - {sizeof(u32) * DC_CMD_STATE_ACCESS, READ_MUX | WRITE_MUX}, - {sizeof(u32) * DC_DISP_FRONT_PORCH, 0xA0088}, - {sizeof(u32) * DC_CMD_STATE_ACCESS, 0}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, - {sizeof(u32) * DC_CMD_GENERAL_INCR_SYNCPT, 0x301}, - {sizeof(u32) * DC_CMD_GENERAL_INCR_SYNCPT, 0x301}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, - {sizeof(u32) * DC_CMD_STATE_ACCESS, 0}, - {sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(4)}, - {sizeof(u32) * DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888}, - {sizeof(u32) * DC_CMD_DISPLAY_COMMAND_OPTION0, 0} + {SleepOrRegisterWriteKind_Write, DC_DISP_SHIFT_CLOCK_OPTIONS, SC1_H_QUALIFIER_NONE | SC0_H_QUALIFIER_NONE}, + {SleepOrRegisterWriteKind_Write, DC_COM_PIN_OUTPUT_ENABLE(1), 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DATA_ENABLE_OPTIONS, DE_SELECT_ACTIVE | DE_CONTROL_NORMAL}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_INTERFACE_CONTROL, DISP_DATA_FORMAT_DF1P1C}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_CLOCK_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_COMMAND_OPTION0, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_ACCESS, READ_MUX | WRITE_MUX}, + {SleepOrRegisterWriteKind_Write, DC_DISP_FRONT_PORCH, 0xA0088}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_ACCESS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_GENERAL_INCR_SYNCPT, 0x301}, + {SleepOrRegisterWriteKind_Write, DC_CMD_GENERAL_INCR_SYNCPT, 0x301}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_ACCESS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_CLOCK_CONTROL, PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(4)}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_COLOR_CONTROL, BASE_COLOR_SIZE_888}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_COMMAND_OPTION0, 0} }; constexpr u32 DisplayConfigFrameBufferAddress = 0xC0000000; -constexpr RegisterWrite DisplayConfigFrameBuffer[] = { - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, //Enable window C. - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, //Enable window B. - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, //Enable window A. - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE - {sizeof(u32) * DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, //T_A8R8G8B8 //NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8 - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_WIN_POSITION, 0}, //(0,0) - {sizeof(u32) * DC_WIN_H_INITIAL_DDA, 0}, - {sizeof(u32) * DC_WIN_V_INITIAL_DDA, 0}, - {sizeof(u32) * DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(2880)}, //Pre-scaled size: 1280x2880 bytes. - {sizeof(u32) * DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, - {sizeof(u32) * DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)}, //Window size: 1280 vertical lines x 720 horizontal pixels. - {sizeof(u32) * DC_WIN_LINE_STRIDE, 0x6000C00}, //768*2x768*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements. - {sizeof(u32) * DC_WIN_BUFFER_CONTROL, 0}, - {sizeof(u32) * DC_WINBUF_SURFACE_KIND, 0}, //Regular surface. - {sizeof(u32) * DC_WINBUF_START_ADDR, DisplayConfigFrameBufferAddress}, //Framebuffer address. - {sizeof(u32) * DC_WINBUF_ADDR_H_OFFSET, 0}, - {sizeof(u32) * DC_WINBUF_ADDR_V_OFFSET, 0}, - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE - {sizeof(u32) * DC_WIN_WIN_OPTIONS, 0}, - {sizeof(u32) * DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE - {sizeof(u32) * DC_WIN_WIN_OPTIONS, WIN_ENABLE}, //Enable window AD. - {sizeof(u32) * DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, //DISPLAY_CTRL_MODE: continuous display. - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE}, //General update; window A update. - {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ} //General activation request; window A activation request. +constexpr const SleepOrRegisterWrite DisplayConfigFrameBuffer[] = { + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_C_SELECT}, //Enable window C. + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_B_SELECT}, //Enable window B. + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_WINDOW_HEADER, WINDOW_A_SELECT}, //Enable window A. + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE + {SleepOrRegisterWriteKind_Write, DC_WIN_COLOR_DEPTH, WIN_COLOR_DEPTH_B8G8R8A8}, //T_A8R8G8B8 //NX Default: T_A8B8G8R8, WIN_COLOR_DEPTH_R8G8B8A8 + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_POSITION, 0}, //(0,0) + {SleepOrRegisterWriteKind_Write, DC_WIN_H_INITIAL_DDA, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_V_INITIAL_DDA, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_PRESCALED_SIZE, V_PRESCALED_SIZE(1280) | H_PRESCALED_SIZE(2880)}, //Pre-scaled size: 1280x2880 bytes. + {SleepOrRegisterWriteKind_Write, DC_WIN_DDA_INC, V_DDA_INC(0x1000) | H_DDA_INC(0x1000)}, + {SleepOrRegisterWriteKind_Write, DC_WIN_SIZE, V_SIZE(1280) | H_SIZE(720)}, //Window size: 1280 vertical lines x 720 horizontal pixels. + {SleepOrRegisterWriteKind_Write, DC_WIN_LINE_STRIDE, 0x6000C00}, //768*2x768*4 (= 0x600 x 0xC00) bytes, see TRM for alignment requirements. + {SleepOrRegisterWriteKind_Write, DC_WIN_BUFFER_CONTROL, 0}, + {SleepOrRegisterWriteKind_Write, DC_WINBUF_SURFACE_KIND, 0}, //Regular surface. + {SleepOrRegisterWriteKind_Write, DC_WINBUF_START_ADDR, DisplayConfigFrameBufferAddress}, //Framebuffer address. + {SleepOrRegisterWriteKind_Write, DC_WINBUF_ADDR_H_OFFSET, 0}, + {SleepOrRegisterWriteKind_Write, DC_WINBUF_ADDR_V_OFFSET, 0}, + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, 0}, + {SleepOrRegisterWriteKind_Write, DC_DISP_DISP_WIN_OPTIONS, DSI_ENABLE}, //DSI_ENABLE + {SleepOrRegisterWriteKind_Write, DC_WIN_WIN_OPTIONS, WIN_ENABLE}, //Enable window AD. + {SleepOrRegisterWriteKind_Write, DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_C_DISPLAY}, //DISPLAY_CTRL_MODE: continuous display. + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_UPDATE | WIN_A_UPDATE}, //General update; window A update. + {SleepOrRegisterWriteKind_Write, DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ | WIN_A_ACT_REQ} //General activation request; window A activation request. }; -constexpr RegisterWrite DisplayConfigDsi01Fini01[] = { +constexpr const RegisterWrite DisplayConfigDc01Fini01[] = { + {sizeof(u32) * DC_DISP_FRONT_PORCH, 0xA0088}, + {sizeof(u32) * DC_CMD_INT_MASK, 0}, + {sizeof(u32) * DC_CMD_STATE_ACCESS, 0}, + {sizeof(u32) * DC_CMD_INT_ENABLE, 0}, + {sizeof(u32) * DC_CMD_CONT_SYNCPT_VSYNC, 0}, + {sizeof(u32) * DC_CMD_DISPLAY_COMMAND, DISP_CTRL_MODE_STOP}, + {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, + {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, + {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, + {sizeof(u32) * DC_CMD_GENERAL_INCR_SYNCPT, 0x301}, + {sizeof(u32) * DC_CMD_GENERAL_INCR_SYNCPT, 0x301}, + {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_UPDATE}, + {sizeof(u32) * DC_CMD_STATE_CONTROL, GENERAL_ACT_REQ}, +}; + +constexpr const RegisterWrite DisplayConfigDsi01Fini01[] = { {sizeof(u32) * DSI_POWER_CONTROL, 0}, {sizeof(u32) * DSI_PAD_CONTROL_1, 0}, }; -constexpr RegisterWrite DisplayConfigDsi01Fini02[] = { +constexpr const RegisterWrite DisplayConfigDsi01Fini02[] = { {sizeof(u32) * DSI_PHY_TIMING_1, 0x40A0E05}, - {sizeof(u32) * DSI_PHY_TIMING_2, 0x30109}, + {sizeof(u32) * DSI_PHY_TIMING_2, 0x30118}, {sizeof(u32) * DSI_BTA_TIMING, 0x190A14}, {sizeof(u32) * DSI_TIMEOUT_0, DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(0xFFFF) }, - {sizeof(u32) * DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x765) | DSI_TIMEOUT_TA(0x2000)}, + {sizeof(u32) * DSI_TIMEOUT_1, DSI_TIMEOUT_PR(0x1343) | DSI_TIMEOUT_TA(0x2000)}, {sizeof(u32) * DSI_TO_TALLY, 0}, {sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC}, {sizeof(u32) * DSI_CONTROL, DSI_CONTROL_LANES(3) | DSI_CONTROL_HOST_ENABLE}, @@ -599,67 +615,68 @@ constexpr RegisterWrite DisplayConfigDsi01Fini02[] = { {sizeof(u32) * DSI_INIT_SEQ_CONTROL, 0} }; -constexpr RegisterWrite DisplayConfigJdiSpecificFini01[] = { - {sizeof(u32) * DSI_WR_DATA, 0x439}, - {sizeof(u32) * DSI_WR_DATA, 0x9483FFB9}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, - {sizeof(u32) * DSI_WR_DATA, 0x2139}, - {sizeof(u32) * DSI_WR_DATA, 0x191919D5}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, - {sizeof(u32) * DSI_WR_DATA, 0xB39}, - {sizeof(u32) * DSI_WR_DATA, 0x4F0F41B1}, - {sizeof(u32) * DSI_WR_DATA, 0xF179A433}, - {sizeof(u32) * DSI_WR_DATA, 0x2D81}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, - {sizeof(u32) * DSI_WR_DATA, 0x439}, - {sizeof(u32) * DSI_WR_DATA, 0xB9}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, +constexpr const SleepOrRegisterWrite DisplayConfigJdiSpecificFini01[] = { + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x9483FFB9}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2139}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x191919D5}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xB39}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x4F0F41B1}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xF179A433}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2D81}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xB9}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, }; -constexpr RegisterWrite DisplayConfigAuoRev1SpecificFini01[] = { - {sizeof(u32) * DSI_WR_DATA, 0x439}, - {sizeof(u32) * DSI_WR_DATA, 0x9483FFB9}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, - {sizeof(u32) * DSI_WR_DATA, 0x2C39}, - {sizeof(u32) * DSI_WR_DATA, 0x191919D5}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, - {sizeof(u32) * DSI_WR_DATA, 0x2C39}, - {sizeof(u32) * DSI_WR_DATA, 0x191919D6}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_WR_DATA, 0x19191919}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, - {sizeof(u32) * DSI_WR_DATA, 0xB39}, - {sizeof(u32) * DSI_WR_DATA, 0x711148B1}, - {sizeof(u32) * DSI_WR_DATA, 0x71143209}, - {sizeof(u32) * DSI_WR_DATA, 0x114D31}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, - {sizeof(u32) * DSI_WR_DATA, 0x439}, - {sizeof(u32) * DSI_WR_DATA, 0xB9}, - {sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST}, +constexpr const SleepOrRegisterWrite DisplayConfigAuoRev1SpecificFini01[] = { + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x9483FFB9}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2C39}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x191919D5}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x2C39}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x191919D6}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x19191919}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xB39}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x711148B1}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x71143209}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x114D31}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0x439}, + {SleepOrRegisterWriteKind_Write, DSI_WR_DATA, 0xB9}, + {SleepOrRegisterWriteKind_Write, DSI_TRIGGER, DSI_TRIGGER_HOST}, + {SleepOrRegisterWriteKind_Sleep, 5, 0}, }; diff --git a/stratosphere/boot/source/boot_driver_management.cpp b/stratosphere/boot/source/boot_driver_management.cpp new file mode 100644 index 000000000..1382c1fe7 --- /dev/null +++ b/stratosphere/boot/source/boot_driver_management.cpp @@ -0,0 +1,65 @@ +/* + * 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 . + */ +#include +#include "boot_driver_management.hpp" + +namespace ams::boot { + + void InitializeGpioDriverLibrary() { + /* Initialize the gpio client library with the server manager object. */ + gpio::InitializeWith(gpio::server::GetServiceObject()); + + /* Initialize the board driver without enabling interrupt handlers. */ + gpio::driver::board::Initialize(false); + + /* Initialize the driver library. */ + gpio::driver::Initialize(); + } + + void InitializeI2cDriverLibrary() { + /* Initialize the i2c client library with the server manager object. */ + i2c::InitializeWith(i2c::server::GetServiceObject(), i2c::server::GetServiceObjectPowerBus()); + + /* Initialize the board driver. */ + i2c::driver::board::Initialize(); + + /* Initialize the driver library. */ + i2c::driver::Initialize(); + + /* Initialize the pwm client library with the server manager object. */ + pwm::InitializeWith(pwm::server::GetServiceObject()); + + /* Initialize the pwm board driver. */ + pwm::driver::board::Initialize(); + + /* Initialize the pwm driver library. */ + pwm::driver::Initialize(); + } + + void FinalizeI2cDriverLibrary() { + /* Finalize the i2c driver library. */ + i2c::driver::Finalize(); + + /* Finalize the i2c client library. */ + i2c::Finalize(); + + /* NOTE: Unknown finalize function is called here by Nintendo. */ + + /* Finalize the pwm client library. */ + pwm::Finalize(); + } + +} diff --git a/stratosphere/boot/source/boot_calibration.hpp b/stratosphere/boot/source/boot_driver_management.hpp similarity index 85% rename from stratosphere/boot/source/boot_calibration.hpp rename to stratosphere/boot/source/boot_driver_management.hpp index d8dd1f5b1..aab6e5523 100644 --- a/stratosphere/boot/source/boot_calibration.hpp +++ b/stratosphere/boot/source/boot_driver_management.hpp @@ -14,12 +14,13 @@ * along with this program. If not, see . */ #pragma once -#include namespace ams::boot { - /* Calibration utilities. */ - u32 GetBatteryVersion(); - u32 GetBatteryVendor(); + void InitializeGpioDriverLibrary(); + void InitializeI2cDriverLibrary(); + void FinalizeI2cDriverLibrary(); + } + diff --git a/stratosphere/boot/source/boot_fan_enable.cpp b/stratosphere/boot/source/boot_fan_enable.cpp index 26d0cf8a4..282ab1224 100644 --- a/stratosphere/boot/source/boot_fan_enable.cpp +++ b/stratosphere/boot/source/boot_fan_enable.cpp @@ -16,22 +16,14 @@ #include #include "boot_fan_enable.hpp" -#include "gpio/gpio_utils.hpp" - namespace ams::boot { - namespace { - - /* Convenience definitions. */ - constexpr u32 GpioPadName_FanEnable = 0x4B; - - } - - void SetFanEnabled() { + void SetFanPowerEnabled() { if (spl::GetHardwareType() == spl::HardwareType::Copper) { - gpio::Configure(GpioPadName_FanEnable); - gpio::SetDirection(GpioPadName_FanEnable, GpioDirection_Output); - gpio::SetValue(GpioPadName_FanEnable, GpioValue_High); + /* TODO */ + /* boot::gpio::Configure(GpioPadName_FanEnable); */ + /* boot::gpio::SetDirection(GpioPadName_FanEnable, GpioDirection_Output); */ + /* boot::gpio::SetValue(GpioPadName_FanEnable, GpioValue_High); */ } } diff --git a/stratosphere/boot/source/boot_fan_enable.hpp b/stratosphere/boot/source/boot_fan_enable.hpp index 6e70ab1db..5fd24b555 100644 --- a/stratosphere/boot/source/boot_fan_enable.hpp +++ b/stratosphere/boot/source/boot_fan_enable.hpp @@ -18,6 +18,6 @@ namespace ams::boot { - void SetFanEnabled(); + void SetFanPowerEnabled(); } diff --git a/stratosphere/boot/source/boot_i2c_utils.cpp b/stratosphere/boot/source/boot_i2c_utils.cpp index 541761b23..c996f6ef7 100644 --- a/stratosphere/boot/source/boot_i2c_utils.cpp +++ b/stratosphere/boot/source/boot_i2c_utils.cpp @@ -22,54 +22,51 @@ namespace ams::boot { template constexpr Result RetryUntilSuccess(F f) { - constexpr u64 timeout = 10'000'000'000ul; - constexpr u64 retry_interval = 20'000'000ul; + constexpr auto Timeout = TimeSpan::FromSeconds(10); + constexpr auto RetryInterval = TimeSpan::FromMilliSeconds(20); - u64 cur_time = 0; + TimeSpan cur_time = TimeSpan(0); while (true) { const auto retry_result = f(); R_SUCCEED_IF(R_SUCCEEDED(retry_result)); - cur_time += retry_interval; - if (cur_time < timeout) { - svcSleepThread(retry_interval); - continue; - } + cur_time += RetryInterval; + R_UNLESS(cur_time < Timeout, retry_result); - return retry_result; + os::SleepThread(RetryInterval); } } } - Result ReadI2cRegister(i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) { + Result ReadI2cRegister(i2c::driver::I2cSession &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size) { AMS_ABORT_UNLESS(dst != nullptr && dst_size > 0); AMS_ABORT_UNLESS(cmd != nullptr && cmd_size > 0); - u8 cmd_list[i2c::CommandListFormatter::MaxCommandListSize]; - + u8 cmd_list[i2c::CommandListLengthMax]; i2c::CommandListFormatter formatter(cmd_list, sizeof(cmd_list)); - R_ABORT_UNLESS(formatter.EnqueueSendCommand(I2cTransactionOption_Start, cmd, cmd_size)); - R_ABORT_UNLESS(formatter.EnqueueReceiveCommand(static_cast(I2cTransactionOption_Start | I2cTransactionOption_Stop), dst_size)); - return RetryUntilSuccess([&]() { return i2c::driver::ExecuteCommandList(session, dst, dst_size, cmd_list, formatter.GetCurrentSize()); }); + R_ABORT_UNLESS(formatter.EnqueueSendCommand(i2c::TransactionOption_StartCondition, cmd, cmd_size)); + R_ABORT_UNLESS(formatter.EnqueueReceiveCommand(static_cast(i2c::TransactionOption_StartCondition | i2c::TransactionOption_StopCondition), dst_size)); + + return RetryUntilSuccess([&]() { return i2c::driver::ExecuteCommandList(dst, dst_size, session, cmd_list, formatter.GetCurrentLength()); }); } - Result WriteI2cRegister(i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) { + Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size) { AMS_ABORT_UNLESS(src != nullptr && src_size > 0); AMS_ABORT_UNLESS(cmd != nullptr && cmd_size > 0); u8 cmd_list[0x20]; /* N doesn't use a CommandListFormatter here... */ - std::memcpy(&cmd_list[0], cmd, cmd_size); - std::memcpy(&cmd_list[cmd_size], src, src_size); + std::memcpy(cmd_list + 0, cmd, cmd_size); + std::memcpy(cmd_list + cmd_size, src, src_size); - return RetryUntilSuccess([&]() { return i2c::driver::Send(session, cmd_list, src_size + cmd_size, static_cast(I2cTransactionOption_Start | I2cTransactionOption_Stop)); }); + return RetryUntilSuccess([&]() { return i2c::driver::Send(session, cmd_list, src_size + cmd_size, static_cast(i2c::TransactionOption_StartCondition | i2c::TransactionOption_StopCondition)); }); } - Result WriteI2cRegister(i2c::driver::Session &session, const u8 address, const u8 value) { - return WriteI2cRegister(session, &value, sizeof(value), &address, sizeof(address)); + Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 address, const u8 value) { + return WriteI2cRegister(session, std::addressof(value), sizeof(value), &address, sizeof(address)); } } diff --git a/stratosphere/boot/source/boot_i2c_utils.hpp b/stratosphere/boot/source/boot_i2c_utils.hpp index b069183e9..b2f328a4e 100644 --- a/stratosphere/boot/source/boot_i2c_utils.hpp +++ b/stratosphere/boot/source/boot_i2c_utils.hpp @@ -16,13 +16,11 @@ #pragma once #include -#include "i2c/driver/i2c_api.hpp" - namespace ams::boot { /* I2C Utilities. */ - Result ReadI2cRegister(i2c::driver::Session &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size); - Result WriteI2cRegister(i2c::driver::Session &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size); - Result WriteI2cRegister(i2c::driver::Session &session, const u8 address, const u8 value); + Result ReadI2cRegister(i2c::driver::I2cSession &session, u8 *dst, size_t dst_size, const u8 *cmd, size_t cmd_size); + Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 *src, size_t src_size, const u8 *cmd, size_t cmd_size); + Result WriteI2cRegister(i2c::driver::I2cSession &session, const u8 address, const u8 value); } diff --git a/stratosphere/boot/source/boot_main.cpp b/stratosphere/boot/source/boot_main.cpp index 07e6edc3c..b5d3e8c21 100644 --- a/stratosphere/boot/source/boot_main.cpp +++ b/stratosphere/boot/source/boot_main.cpp @@ -19,13 +19,11 @@ #include "boot_check_battery.hpp" #include "boot_check_clock.hpp" #include "boot_clock_initial_configuration.hpp" +#include "boot_driver_management.hpp" #include "boot_fan_enable.hpp" +#include "boot_pinmux_initial_configuration.hpp" #include "boot_repair_boot_images.hpp" #include "boot_splash_screen.hpp" -#include "boot_wake_pins.hpp" - -#include "gpio/gpio_initial_configuration.hpp" -#include "pinmux/pinmux_initial_configuration.hpp" #include "boot_power_utils.hpp" @@ -38,7 +36,7 @@ extern "C" { u32 __nx_fs_num_sessions = 1; /* TODO: Evaluate to what extent this can be reduced further. */ - #define INNER_HEAP_SIZE 0x20000 + #define INNER_HEAP_SIZE 0x1000 size_t nx_inner_heap_size = INNER_HEAP_SIZE; char nx_inner_heap[INNER_HEAP_SIZE]; @@ -71,6 +69,42 @@ namespace ams { using namespace ams; +namespace { + + constinit u8 g_exp_heap_memory[20_KB]; + constinit u8 g_unit_heap_memory[5_KB]; + constinit lmem::HeapHandle g_exp_heap_handle; + constinit lmem::HeapHandle g_unit_heap_handle; + + constinit sf::ExpHeapMemoryResource g_exp_heap_memory_resource; + constinit sf::UnitHeapMemoryResource g_unit_heap_memory_resource; + + void *Allocate(size_t size) { + void *mem = lmem::AllocateFromExpHeap(g_exp_heap_handle, size); + return mem; + } + + void Deallocate(void *p, size_t size) { + AMS_UNUSED(size); + lmem::FreeToExpHeap(g_exp_heap_handle, p); + } + + void InitializeHeaps() { + /* Create the heaps. */ + g_exp_heap_handle = lmem::CreateExpHeap(g_exp_heap_memory, sizeof(g_exp_heap_memory), lmem::CreateOption_ThreadSafe); + g_unit_heap_handle = lmem::CreateUnitHeap(g_unit_heap_memory, sizeof(g_unit_heap_memory), sizeof(ddsf::DeviceCodeEntryHolder), lmem::CreateOption_ThreadSafe); + + /* Attach the memory resources. */ + g_exp_heap_memory_resource.Attach(g_exp_heap_handle); + g_unit_heap_memory_resource.Attach(g_unit_heap_handle); + + /* Register with ddsf. */ + ddsf::SetMemoryResource(std::addressof(g_exp_heap_memory_resource)); + ddsf::SetDeviceCodeEntryHolderMemoryResource(std::addressof(g_unit_heap_memory_resource)); + } + +} + void __libnx_exception_handler(ThreadExceptionDump *ctx) { ams::CrashHandler(ctx); } @@ -85,11 +119,15 @@ void __libnx_initheap(void) { fake_heap_start = (char*)addr; fake_heap_end = (char*)addr + size; + + InitializeHeaps(); } void __appInit(void) { hos::InitializeForStratosphere(); + fs::SetAllocator(Allocate, Deallocate); + /* Initialize services we need (TODO: NCM) */ sm::DoWithSession([&]() { R_ABORT_UNLESS(fsInitialize()); @@ -107,6 +145,30 @@ void __appExit(void) { fsExit(); } +void *operator new(size_t size) { + return Allocate(size); +} + +void *operator new(size_t size, const std::nothrow_t &) { + return Allocate(size); +} + +void operator delete(void *p) { + return Deallocate(p, 0); +} + +void *operator new[](size_t size) { + return Allocate(size); +} + +void *operator new[](size_t size, const std::nothrow_t &) { + return Allocate(size); +} + +void operator delete[](void *p) { + return Deallocate(p, 0); +} + int main(int argc, char **argv) { /* Set thread name. */ @@ -122,8 +184,22 @@ int main(int argc, char **argv) /* Change voltage from 3.3v to 1.8v for select devices. */ boot::ChangeGpioVoltageTo1_8v(); - /* Setup GPIO. */ - gpio::SetInitialConfiguration(); + /* Setup gpio. */ + gpio::driver::SetInitialGpioConfig(); + + /* Initialize the gpio server library. */ + boot::InitializeGpioDriverLibrary(); + + /* Initialize the i2c server library. */ + boot::InitializeI2cDriverLibrary(); + + /* Get the hardware type. */ + const auto hw_type = spl::GetHardwareType(); + + /* Initialize the power control library without interrupt event handling. */ + if (hw_type != spl::HardwareType::Calcio) { + powctl::Initialize(false); + } /* Check USB PLL/UTMIP clock. */ boot::CheckClock(); @@ -131,8 +207,8 @@ int main(int argc, char **argv) /* Talk to PMIC/RTC, set boot reason with SPL. */ boot::DetectBootReason(); - const auto hw_type = spl::GetHardwareType(); - if (hw_type != spl::HardwareType::Copper && hw_type != spl::HardwareType::Calcio) { + /* Display the splash screen and check the battery charge. */ + if (hw_type != spl::HardwareType::Calcio) { /* Display splash screen for two seconds. */ boot::ShowSplashScreen(); @@ -141,22 +217,30 @@ int main(int argc, char **argv) } /* Configure pinmux + drive pads. */ - pinmux::SetInitialConfiguration(); + boot::SetInitialPinmuxConfiguration(); /* Configure the PMC wake pin settings. */ - boot::SetInitialWakePinConfiguration(); + gpio::driver::SetInitialWakePinConfig(); /* Configure output clock. */ - if (hw_type != spl::HardwareType::Copper && hw_type != spl::HardwareType::Calcio) { + if (hw_type != spl::HardwareType::Calcio) { boot::SetInitialClockConfiguration(); } /* Set Fan enable config (Copper only). */ - boot::SetFanEnabled(); + boot::SetFanPowerEnabled(); /* Repair boot partitions in NAND if needed. */ boot::CheckAndRepairBootImages(); + /* Finalize the power control library. */ + if (hw_type != spl::HardwareType::Calcio) { + powctl::Finalize(); + } + + /* Finalize the i2c server library. */ + boot::FinalizeI2cDriverLibrary(); + /* Tell PM to start boot2. */ R_ABORT_UNLESS(pmshellNotifyBootFinished()); diff --git a/stratosphere/boot/source/boot_pcv.cpp b/stratosphere/boot/source/boot_pcv.cpp deleted file mode 100644 index 59212a9aa..000000000 --- a/stratosphere/boot/source/boot_pcv.cpp +++ /dev/null @@ -1,74 +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 . - */ -#include -#include "i2c/i2c_types.hpp" -#include "i2c/driver/impl/i2c_pcv.hpp" -#include "i2c/driver/impl/i2c_registers.hpp" - -using namespace ams::i2c::driver::impl; - -namespace ams::pcv { - - void Initialize() { - /* Don't do anything. */ - } - - void Finalize() { - /* Don't do anything. */ - } - - Result SetClockRate(PcvModule module, u32 hz) { - /* Get clock/reset registers. */ - ClkRstRegisters regs; - regs.SetBus(ConvertFromPcvModule(module)); - /* Set clock enabled/source. */ - reg::SetBits(regs.clk_en_reg, regs.mask); - reg::ReadWrite(regs.clk_src_reg, 0x4, 0xFF); - svcSleepThread(1000ul); - reg::ReadWrite(regs.clk_src_reg, 0, 0xE0000000); - svcSleepThread(2000ul); - - return ResultSuccess(); - } - - Result SetClockEnabled(PcvModule module, bool enabled) { - return ResultSuccess(); - } - - Result SetVoltageEnabled(u32 domain, bool enabled) { - return ResultSuccess(); - } - - Result SetVoltageValue(u32 domain, u32 voltage) { - return ResultSuccess(); - } - - Result SetReset(PcvModule module, bool reset) { - /* Get clock/reset registers. */ - ClkRstRegisters regs; - regs.SetBus(ConvertFromPcvModule(module)); - - /* Set/clear reset. */ - if (reset) { - reg::SetBits(regs.rst_reg, regs.mask); - } else { - reg::ClearBits(regs.rst_reg, regs.mask); - } - - return ResultSuccess(); - } - -} diff --git a/stratosphere/boot/source/boot_pinmux_initial_configuration.cpp b/stratosphere/boot/source/boot_pinmux_initial_configuration.cpp new file mode 100644 index 000000000..fe545e2b4 --- /dev/null +++ b/stratosphere/boot/source/boot_pinmux_initial_configuration.cpp @@ -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 . + */ +#include +#include "boot_pinmux_initial_configuration.hpp" + +namespace ams::boot { + + void SetInitialPinmuxConfiguration() { + pinmux::driver::Initialize(); + pinmux::driver::SetInitialConfig(); + pinmux::driver::SetInitialDrivePadConfig(); + pinmux::driver::Finalize(); + } + +} diff --git a/stratosphere/boot/source/boot_wake_pins.hpp b/stratosphere/boot/source/boot_pinmux_initial_configuration.hpp similarity index 94% rename from stratosphere/boot/source/boot_wake_pins.hpp rename to stratosphere/boot/source/boot_pinmux_initial_configuration.hpp index 845ba09f6..3636b1e74 100644 --- a/stratosphere/boot/source/boot_wake_pins.hpp +++ b/stratosphere/boot/source/boot_pinmux_initial_configuration.hpp @@ -18,6 +18,6 @@ namespace ams::boot { - void SetInitialWakePinConfiguration(); + void SetInitialPinmuxConfiguration(); } diff --git a/stratosphere/boot/source/boot_pmc_wrapper.cpp b/stratosphere/boot/source/boot_pmc_wrapper.cpp deleted file mode 100644 index 9003009d4..000000000 --- a/stratosphere/boot/source/boot_pmc_wrapper.cpp +++ /dev/null @@ -1,52 +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 . - */ -#include -#include "boot_pmc_wrapper.hpp" - -namespace ams::boot { - - namespace { - - /* Convenience definitions. */ - constexpr u32 SmcFunctionId_AtmosphereReadWriteRegister = 0xF0000002; - - constexpr u32 PmcPhysStart = 0x7000E400; - constexpr u32 PmcPhysEnd = 0x7000EFFF; - - /* Helpers. */ - bool IsValidPmcAddress(u32 phys_addr) { - return (phys_addr & 3) == 0 && PmcPhysStart <= phys_addr && phys_addr <= PmcPhysEnd; - } - - inline u32 ReadWriteRegisterImpl(uintptr_t phys_addr, u32 value, u32 mask) { - u32 out_value; - R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::AtmosphereReadWriteRegister(phys_addr, mask, value, &out_value))); - return out_value; - } - - } - - u32 ReadPmcRegister(u32 phys_addr) { - AMS_ABORT_UNLESS(IsValidPmcAddress(phys_addr)); - return ReadWriteRegisterImpl(phys_addr, 0, 0); - } - - void WritePmcRegister(u32 phys_addr, u32 value, u32 mask) { - AMS_ABORT_UNLESS(IsValidPmcAddress(phys_addr)); - ReadWriteRegisterImpl(phys_addr, value, mask); - } - -} diff --git a/stratosphere/boot/source/boot_pmic_driver.cpp b/stratosphere/boot/source/boot_pmic_driver.cpp index 8dbe39365..877d534de 100644 --- a/stratosphere/boot/source/boot_pmic_driver.cpp +++ b/stratosphere/boot/source/boot_pmic_driver.cpp @@ -20,90 +20,87 @@ namespace ams::boot { void PmicDriver::ShutdownSystem() { - R_ABORT_UNLESS(this->ShutdownSystem(false)); + this->ShutdownSystem(false); } void PmicDriver::RebootSystem() { - R_ABORT_UNLESS(this->ShutdownSystem(true)); + this->ShutdownSystem(true); } Result PmicDriver::GetAcOk(bool *out) { u8 power_status; - R_TRY(this->GetPowerStatus(&power_status)); + R_TRY(this->GetPowerStatus(std::addressof(power_status))); *out = (power_status & 0x02) != 0; return ResultSuccess(); } - Result PmicDriver::GetPowerIntr(u8 *out) { + Result PmicDriver::GetOnOffIrq(u8 *out) { const u8 addr = 0x0B; - return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr)); + return ReadI2cRegister(this->i2c_session, out, sizeof(*out), std::addressof(addr), sizeof(addr)); } Result PmicDriver::GetPowerStatus(u8 *out) { const u8 addr = 0x15; - return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr)); + return ReadI2cRegister(this->i2c_session, out, sizeof(*out), std::addressof(addr), sizeof(addr)); } Result PmicDriver::GetNvErc(u8 *out) { const u8 addr = 0x0C; - return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &addr, sizeof(addr)); + return ReadI2cRegister(this->i2c_session, out, sizeof(*out), std::addressof(addr), sizeof(addr)); } Result PmicDriver::GetPowerButtonPressed(bool *out) { - u8 power_intr; - R_TRY(this->GetPowerIntr(&power_intr)); - *out = (power_intr & 0x08) != 0; + u8 on_off_irq; + R_TRY(this->GetOnOffIrq(std::addressof(on_off_irq))); + *out = (on_off_irq & 0x08) != 0; return ResultSuccess(); } - Result PmicDriver::ShutdownSystem(bool reboot) { + void PmicDriver::ShutdownSystem(bool reboot) { const u8 on_off_1_addr = 0x41; const u8 on_off_2_addr = 0x42; /* Get value, set or clear software reset mask. */ u8 on_off_2_val = 0; - R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr))); + R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, std::addressof(on_off_2_val), sizeof(on_off_2_val), std::addressof(on_off_2_addr), sizeof(on_off_2_addr))); if (reboot) { on_off_2_val |= 0x80; } else { on_off_2_val &= ~0x80; } - R_ABORT_UNLESS(WriteI2cRegister(this->i2c_session, &on_off_2_val, sizeof(on_off_2_val), &on_off_2_addr, sizeof(on_off_2_addr))); + R_ABORT_UNLESS(WriteI2cRegister(this->i2c_session, std::addressof(on_off_2_val), sizeof(on_off_2_val), std::addressof(on_off_2_addr), sizeof(on_off_2_addr))); /* Get value, set software reset mask. */ u8 on_off_1_val = 0; - R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr))); + R_ABORT_UNLESS(ReadI2cRegister(this->i2c_session, std::addressof(on_off_1_val), sizeof(on_off_1_val), std::addressof(on_off_1_addr), sizeof(on_off_1_addr))); on_off_1_val |= 0x80; /* Finalize the battery on non-Calcio. */ if (spl::GetHardwareType() != spl::HardwareType::Calcio) { BatteryDriver battery_driver; - this->FinalizeBattery(&battery_driver); + this->FinalizeBattery(battery_driver); } /* Actually write the value to trigger shutdown/reset. */ - R_ABORT_UNLESS(WriteI2cRegister(this->i2c_session, &on_off_1_val, sizeof(on_off_1_val), &on_off_1_addr, sizeof(on_off_1_addr))); + R_ABORT_UNLESS(WriteI2cRegister(this->i2c_session, std::addressof(on_off_1_val), sizeof(on_off_1_val), std::addressof(on_off_1_addr), sizeof(on_off_1_addr))); /* Allow up to 5 seconds for shutdown/reboot to take place. */ - svcSleepThread(5'000'000'000ul); - AMS_ABORT_UNLESS(false); + os::SleepThread(TimeSpan::FromSeconds(5)); + AMS_ABORT("Shutdown failed"); } - void PmicDriver::FinalizeBattery(BatteryDriver *battery_driver) { - /* Set shutdown timer. */ - battery_driver->SetShutdownTimer(); - + void PmicDriver::FinalizeBattery(BatteryDriver &battery_driver) { /* Get whether shutdown is enabled. */ bool shutdown_enabled; - if (R_FAILED(battery_driver->GetShutdownEnabled(&shutdown_enabled))) { + if (R_FAILED(battery_driver.IsI2cShutdownEnabled(std::addressof(shutdown_enabled)))) { return; } /* On Hoag, we don't want to use the desired shutdown value when battery charged. */ bool use_desired_shutdown = true; if (spl::GetHardwareType() == spl::HardwareType::Hoag) { - double battery_charge; - if (R_FAILED(battery_driver->GetSocRep(&battery_charge)) || battery_charge >= 80.0) { + float battery_charge_raw; + if (R_FAILED(battery_driver.GetSocRep(std::addressof(battery_charge_raw))) || battery_charge_raw >= 80.0) { use_desired_shutdown = false; } } @@ -119,7 +116,7 @@ namespace ams::boot { desired_shutdown_enabled &= use_desired_shutdown; if (shutdown_enabled != desired_shutdown_enabled) { - battery_driver->SetShutdownEnabled(desired_shutdown_enabled); + battery_driver.SetI2cShutdownEnabled(desired_shutdown_enabled); } } diff --git a/stratosphere/boot/source/boot_pmic_driver.hpp b/stratosphere/boot/source/boot_pmic_driver.hpp index a9bb2f049..fc17dbaa0 100644 --- a/stratosphere/boot/source/boot_pmic_driver.hpp +++ b/stratosphere/boot/source/boot_pmic_driver.hpp @@ -21,26 +21,24 @@ namespace ams::boot { /* Driver object. */ class PmicDriver { private: - i2c::driver::Session i2c_session; + i2c::driver::I2cSession i2c_session; public: PmicDriver() { - i2c::driver::Initialize(); - i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Pmic); + R_ABORT_UNLESS(i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max77620Pmic)); } ~PmicDriver() { i2c::driver::CloseSession(this->i2c_session); - i2c::driver::Finalize(); } private: Result GetPowerStatus(u8 *out); - Result ShutdownSystem(bool reboot); - void FinalizeBattery(BatteryDriver *battery_driver); + void ShutdownSystem(bool reboot); + void FinalizeBattery(BatteryDriver &battery_driver); public: void ShutdownSystem(); void RebootSystem(); Result GetAcOk(bool *out); - Result GetPowerIntr(u8 *out); + Result GetOnOffIrq(u8 *out); Result GetNvErc(u8 *out); Result GetPowerButtonPressed(bool *out); }; diff --git a/stratosphere/boot/source/boot_power_utils.cpp b/stratosphere/boot/source/boot_power_utils.cpp index 3b18807b0..59228e3c8 100644 --- a/stratosphere/boot/source/boot_power_utils.cpp +++ b/stratosphere/boot/source/boot_power_utils.cpp @@ -15,6 +15,7 @@ */ #include #include "boot_power_utils.hpp" +#include "boot_pmic_driver.hpp" #include "fusee-primary_bin.h" namespace ams::boot { @@ -25,7 +26,7 @@ namespace ams::boot { constexpr uintptr_t IramBase = 0x40000000ull; constexpr uintptr_t IramPayloadBase = 0x40010000ull; constexpr size_t IramSize = 0x40000; - constexpr size_t IramPayloadMaxSize = 0x2E000; + constexpr size_t IramPayloadMaxSize = 0x20000; /* Globals. */ alignas(os::MemoryPageSize) u8 g_work_page[os::MemoryPageSize]; @@ -65,7 +66,16 @@ namespace ams::boot { } void RebootSystem() { - DoRebootToPayload(nullptr); + if (spl::GetSocType() == spl::SocType_Erista) { + DoRebootToPayload(nullptr); + } else { + /* On Mariko, we can't reboot to payload, so we should just do a reboot. */ + PmicDriver().RebootSystem(); + } + } + + void ShutdownSystem() { + PmicDriver().ShutdownSystem(); } void SetInitialRebootPayload() { diff --git a/stratosphere/boot/source/boot_registers_clkrst.hpp b/stratosphere/boot/source/boot_registers_clkrst.hpp deleted file mode 100644 index f361e4ef0..000000000 --- a/stratosphere/boot/source/boot_registers_clkrst.hpp +++ /dev/null @@ -1,116 +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 . - */ -#pragma once -#include - -static constexpr size_t CLK_RST_CONTROLLER_RST_SOURCE = 0x0; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_L = 0x4; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_H = 0x8; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_U = 0xC; -static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_L = 0x10; -static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_H = 0x14; -static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_U = 0x18; -static constexpr size_t CLK_RST_CONTROLLER_CCLK_BURST_POLICY = 0x20; -static constexpr size_t CLK_RST_CONTROLLER_SUPER_CCLK_DIVIDER = 0x24; -static constexpr size_t CLK_RST_CONTROLLER_SCLK_BURST_POLICY = 0x28; -static constexpr size_t CLK_RST_CONTROLLER_SUPER_SCLK_DIVIDER = 0x2C; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SYSTEM_RATE = 0x30; -static constexpr size_t CLK_RST_CONTROLLER_MISC_CLK_ENB = 0x48; -static constexpr size_t CLK_RST_CONTROLLER_OSC_CTRL = 0x50; -static constexpr size_t CLK_RST_CONTROLLER_PLLC_BASE = 0x80; -static constexpr size_t CLK_RST_CONTROLLER_PLLC_MISC = 0x88; -static constexpr size_t CLK_RST_CONTROLLER_PLLM_BASE = 0x90; -static constexpr size_t CLK_RST_CONTROLLER_PLLM_MISC1 = 0x98; -static constexpr size_t CLK_RST_CONTROLLER_PLLM_MISC2 = 0x9C; -static constexpr size_t CLK_RST_CONTROLLER_PLLP_BASE = 0xA0; -static constexpr size_t CLK_RST_CONTROLLER_PLLD_BASE = 0xD0; -static constexpr size_t CLK_RST_CONTROLLER_PLLD_MISC1 = 0xD8; -static constexpr size_t CLK_RST_CONTROLLER_PLLD_MISC = 0xDC; -static constexpr size_t CLK_RST_CONTROLLER_PLLX_BASE = 0xE0; -static constexpr size_t CLK_RST_CONTROLLER_PLLX_MISC = 0xE4; -static constexpr size_t CLK_RST_CONTROLLER_PLLE_BASE = 0xE8; -static constexpr size_t CLK_RST_CONTROLLER_PLLE_MISC = 0xEC; -static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA = 0xF8; -static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB = 0xFC; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_PWM = 0x110; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 = 0x124; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 = 0x128; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_DISP1 = 0x138; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_VI = 0x148; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 = 0x150; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 = 0x154; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 = 0x164; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_UARTA = 0x178; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_UARTB = 0x17C; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X = 0x180; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_UARTC = 0x1A0; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 = 0x1B8; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 = 0x1BC; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_CSITE = 0x1D4; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_EMC = 0x19C; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_TSEC = 0x1F4; -static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_X = 0x280; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_X_SET = 0x284; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_X_CLR = 0x288; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_X = 0x28C; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_X_SET = 0x290; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_X_CLR = 0x294; -static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_Y = 0x298; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_Y_SET = 0x29C; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_Y_CLR = 0x2A0; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_Y = 0x2A4; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_Y_SET = 0x2A8; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_Y_CLR = 0x2AC; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_L_SET = 0x300; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_L_CLR = 0x304; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_H_SET = 0x308; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_H_CLR = 0x30C; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_U_SET = 0x310; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEV_U_CLR = 0x314; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_L_SET = 0x320; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_L_CLR = 0x324; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_H_SET = 0x328; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_H_CLR = 0x32C; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_U_SET = 0x330; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_U_CLR = 0x334; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_V = 0x358; -static constexpr size_t CLK_RST_CONTROLLER_RST_DEVICES_W = 0x35C; -static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_V = 0x360; -static constexpr size_t CLK_RST_CONTROLLER_CLK_OUT_ENB_W = 0x364; -static constexpr size_t CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 = 0x388; -static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC = 0x3A0; -static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD = 0x3A4; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT = 0x3B4; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 = 0x410; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SE = 0x42C; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_V_SET = 0x440; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_W_SET = 0x448; -static constexpr size_t CLK_RST_CONTROLLER_CLK_ENB_W_CLR = 0x44C; -static constexpr size_t CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET = 0x450; -static constexpr size_t CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR = 0x454; -static constexpr size_t CLK_RST_CONTROLLER_UTMIP_PLL_CFG2 = 0x488; -static constexpr size_t CLK_RST_CONTROLLER_PLLE_AUX = 0x48C; -static constexpr size_t CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S0 = 0x4A0; -static constexpr size_t CLK_RST_CONTROLLER_PLLX_MISC_3 = 0x518; -static constexpr size_t CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE = 0x554; -static constexpr size_t CLK_RST_CONTROLLER_SPARE_REG0 = 0x55C; -static constexpr size_t CLK_RST_CONTROLLER_PLLMB_BASE = 0x5E8; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP = 0x620; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_EMC_DLL = 0x664; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL = 0x66C; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM = 0x694; -static constexpr size_t CLK_RST_CONTROLLER_CLK_SOURCE_NVENC = 0x6A0; -static constexpr size_t CLK_RST_CONTROLLER_SE_SUPER_CLK_DIVIDER = 0x704; diff --git a/stratosphere/boot/source/boot_registers_di.hpp b/stratosphere/boot/source/boot_registers_di.hpp index 2cd55fa74..d49001a31 100644 --- a/stratosphere/boot/source/boot_registers_di.hpp +++ b/stratosphere/boot/source/boot_registers_di.hpp @@ -44,6 +44,7 @@ #define PM0_ENABLE (1 << 16) #define PM1_ENABLE (1 << 18) +#define DC_CMD_INT_STATUS 0x37 #define DC_CMD_INT_MASK 0x38 #define DC_CMD_INT_ENABLE 0x39 @@ -241,6 +242,8 @@ /*! Display serial interface registers. */ #define _DSIREG(reg) ((reg) * 4) +#define DSI_INCR_SYNCPT_CNTRL 0x1 + #define DSI_RD_DATA 0x9 #define DSI_WR_DATA 0xA @@ -346,5 +349,4 @@ #define DSI_PAD_CONTROL_7_MARIKO 0x55 #define DSI_INIT_SEQ_DATA_15 0x5F -#define MIPI_CAL_MIPI_BIAS_PAD_CFG2 0x60 #define DSI_INIT_SEQ_DATA_15_MARIKO 0x62 diff --git a/stratosphere/boot/source/boot_registers_pinmux.hpp b/stratosphere/boot/source/boot_registers_pinmux.hpp deleted file mode 100644 index 5ae6da61a..000000000 --- a/stratosphere/boot/source/boot_registers_pinmux.hpp +++ /dev/null @@ -1,72 +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 . - */ -#pragma once -#include - -static constexpr size_t APB_MISC_GP_SDMMC1_CLK_LPBK_CONTROL = 0x8D4; -static constexpr size_t APB_MISC_GP_SDMMC3_CLK_LPBK_CONTROL = 0x8D8; -static constexpr size_t APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL = 0xA98; -static constexpr size_t APB_MISC_GP_VGPIO_GPIO_MUX_SEL = 0xB74; - -static constexpr size_t PINMUX_AUX_SDMMC1_CLK = 0x00; -static constexpr size_t PINMUX_AUX_SDMMC1_CMD = 0x04; -static constexpr size_t PINMUX_AUX_SDMMC1_DAT3 = 0x08; -static constexpr size_t PINMUX_AUX_SDMMC1_DAT2 = 0x0C; -static constexpr size_t PINMUX_AUX_SDMMC1_DAT1 = 0x10; -static constexpr size_t PINMUX_AUX_SDMMC1_DAT0 = 0x14; -static constexpr size_t PINMUX_AUX_SDMMC3_CLK = 0x1C; -static constexpr size_t PINMUX_AUX_SDMMC3_CMD = 0x20; -static constexpr size_t PINMUX_AUX_SDMMC3_DAT0 = 0x24; -static constexpr size_t PINMUX_AUX_SDMMC3_DAT1 = 0x28; -static constexpr size_t PINMUX_AUX_SDMMC3_DAT2 = 0x2C; -static constexpr size_t PINMUX_AUX_SDMMC3_DAT3 = 0x30; -static constexpr size_t PINMUX_AUX_DMIC3_CLK = 0xB4; -static constexpr size_t PINMUX_AUX_UART2_TX = 0xF4; -static constexpr size_t PINMUX_AUX_UART3_TX = 0x104; -static constexpr size_t PINMUX_AUX_WIFI_EN = 0x1B4; -static constexpr size_t PINMUX_AUX_WIFI_RST = 0x1B8; -static constexpr size_t PINMUX_AUX_NFC_EN = 0x1D0; -static constexpr size_t PINMUX_AUX_NFC_INT = 0x1D4; -static constexpr size_t PINMUX_AUX_LCD_BL_PWM = 0x1FC; -static constexpr size_t PINMUX_AUX_LCD_BL_EN = 0x200; -static constexpr size_t PINMUX_AUX_LCD_RST = 0x204; -static constexpr size_t PINMUX_AUX_GPIO_PE6 = 0x248; -static constexpr size_t PINMUX_AUX_GPIO_PH6 = 0x250; -static constexpr size_t PINMUX_AUX_GPIO_PZ1 = 0x280; - -static constexpr u32 PINMUX_FUNC_MASK = (3 << 0); - -static constexpr u32 PINMUX_PULL_MASK = (3 << 2); -static constexpr u32 PINMUX_PULL_NONE = (0 << 2); -static constexpr u32 PINMUX_PULL_DOWN = (1 << 2); -static constexpr u32 PINMUX_PULL_UP = (2 << 2); - -static constexpr u32 PINMUX_TRISTATE = (1 << 4); -static constexpr u32 PINMUX_PARKED = (1 << 5); -static constexpr u32 PINMUX_INPUT_ENABLE = (1 << 6); -static constexpr u32 PINMUX_LOCK = (1 << 7); -static constexpr u32 PINMUX_LPDR = (1 << 8); -static constexpr u32 PINMUX_HSM = (1 << 9); - -static constexpr u32 PINMUX_IO_HV = (1 << 10); -static constexpr u32 PINMUX_OPEN_DRAIN = (1 << 11); -static constexpr u32 PINMUX_SCHMT = (1 << 12); - -static constexpr u32 PINMUX_DRIVE_1X = (0 << 13) ; -static constexpr u32 PINMUX_DRIVE_2X = (1 << 13); -static constexpr u32 PINMUX_DRIVE_3X = (2 << 13); -static constexpr u32 PINMUX_DRIVE_4X = (3 << 13); - diff --git a/stratosphere/boot/source/boot_registers_pmc.hpp b/stratosphere/boot/source/boot_registers_pmc.hpp deleted file mode 100644 index 8d0d53f2f..000000000 --- a/stratosphere/boot/source/boot_registers_pmc.hpp +++ /dev/null @@ -1,78 +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 . - */ -#pragma once -#include - -static constexpr uintptr_t PmcBase = 0x7000E400ul; - -static constexpr size_t APBDEV_PMC_CNTRL = 0x0; -static constexpr u32 PMC_CNTRL_MAIN_RST = (1 << 4); -static constexpr size_t APBDEV_PMC_SEC_DISABLE = 0x4; -static constexpr size_t APBDEV_PMC_WAKE_MASK = 0xC; -static constexpr size_t APBDEV_PMC_WAKE_LVL = 0x10; -static constexpr size_t APBDEV_PMC_DPD_PADS_ORIDE = 0x1C; -static constexpr size_t APBDEV_PMC_PWRGATE_TOGGLE = 0x30; -static constexpr size_t APBDEV_PMC_PWRGATE_STATUS = 0x38; -static constexpr size_t APBDEV_PMC_BLINK_TIMER = 0x40; -static constexpr size_t APBDEV_PMC_NO_IOPOWER = 0x44; -static constexpr size_t APBDEV_PMC_SCRATCH0 = 0x50; -static constexpr size_t APBDEV_PMC_SCRATCH1 = 0x54; -static constexpr size_t APBDEV_PMC_SCRATCH20 = 0xA0; -static constexpr size_t APBDEV_PMC_AUTO_WAKE_LVL_MASK = 0xDC; -static constexpr size_t APBDEV_PMC_PWR_DET_VAL = 0xE4; -static constexpr u32 PMC_PWR_DET_SDMMC1_IO_EN = (1 << 12); -static constexpr size_t APBDEV_PMC_DDR_PWR = 0xE8; -static constexpr size_t APBDEV_PMC_CRYPTO_OP = 0xF4; -static constexpr u32 PMC_CRYPTO_OP_SE_ENABLE = 0; -static constexpr u32 PMC_CRYPTO_OP_SE_DISABLE = 1; -static constexpr size_t APBDEV_PMC_SCRATCH33 = 0x120; -static constexpr size_t APBDEV_PMC_SCRATCH40 = 0x13C; -static constexpr size_t APBDEV_PMC_WAKE2_MASK = 0x160; -static constexpr size_t APBDEV_PMC_WAKE2_LVL = 0x164; -static constexpr size_t APBDEV_PMC_AUTO_WAKE2_LVL_MASK = 0x170; -static constexpr size_t APBDEV_PMC_OSC_EDPD_OVER = 0x1A4; -static constexpr size_t APBDEV_PMC_CLK_OUT_CNTRL = 0x1A8; -static constexpr size_t APBDEV_PMC_RST_STATUS = 0x1B4; -static constexpr size_t APBDEV_PMC_IO_DPD_REQ = 0x1B8; -static constexpr size_t APBDEV_PMC_IO_DPD2_REQ = 0x1C0; -static constexpr size_t APBDEV_PMC_VDDP_SEL = 0x1CC; -static constexpr size_t APBDEV_PMC_DDR_CFG = 0x1D0; -static constexpr size_t APBDEV_PMC_SCRATCH45 = 0x234; -static constexpr size_t APBDEV_PMC_SCRATCH46 = 0x238; -static constexpr size_t APBDEV_PMC_SCRATCH49 = 0x244; -static constexpr size_t APBDEV_PMC_TSC_MULT = 0x2B4; -static constexpr size_t APBDEV_PMC_SEC_DISABLE2 = 0x2C4; -static constexpr size_t APBDEV_PMC_WEAK_BIAS = 0x2C8; -static constexpr size_t APBDEV_PMC_REG_SHORT = 0x2CC; -static constexpr size_t APBDEV_PMC_SEC_DISABLE3 = 0x2D8; -static constexpr size_t APBDEV_PMC_SECURE_SCRATCH21 = 0x334; -static constexpr size_t APBDEV_PMC_SECURE_SCRATCH32 = 0x360; -static constexpr size_t APBDEV_PMC_SECURE_SCRATCH49 = 0x3A4; -static constexpr size_t APBDEV_PMC_CNTRL2 = 0x440; -static constexpr size_t APBDEV_PMC_IO_DPD3_REQ = 0x45C; -static constexpr size_t APBDEV_PMC_IO_DPD4_REQ = 0x464; -static constexpr size_t APBDEV_PMC_UTMIP_PAD_CFG1 = 0x4C4; -static constexpr size_t APBDEV_PMC_UTMIP_PAD_CFG3 = 0x4CC; -static constexpr size_t APBDEV_PMC_WAKE_DEBOUNCE_EN = 0x4D8; -static constexpr size_t APBDEV_PMC_DDR_CNTRL = 0x4E4; -static constexpr size_t APBDEV_PMC_SEC_DISABLE4 = 0x5B0; -static constexpr size_t APBDEV_PMC_SEC_DISABLE5 = 0x5B4; -static constexpr size_t APBDEV_PMC_SEC_DISABLE6 = 0x5B8; -static constexpr size_t APBDEV_PMC_SEC_DISABLE7 = 0x5BC; -static constexpr size_t APBDEV_PMC_SEC_DISABLE8 = 0x5C0; -static constexpr size_t APBDEV_PMC_SCRATCH188 = 0x810; -static constexpr size_t APBDEV_PMC_SCRATCH190 = 0x818; -static constexpr size_t APBDEV_PMC_SCRATCH200 = 0x840; diff --git a/stratosphere/boot/source/boot_rtc_driver.cpp b/stratosphere/boot/source/boot_rtc_driver.cpp index 33fb6fd98..72f65c04e 100644 --- a/stratosphere/boot/source/boot_rtc_driver.cpp +++ b/stratosphere/boot/source/boot_rtc_driver.cpp @@ -22,7 +22,7 @@ namespace ams::boot { const u8 update_addr = 0x04; const u8 update_val = 0x10; R_TRY(WriteI2cRegister(this->i2c_session, &update_val, sizeof(update_val), &update_addr, sizeof(update_addr))); - svcSleepThread(16'000'000ul); + os::SleepThread(TimeSpan::FromMilliSeconds(16)); return ReadI2cRegister(this->i2c_session, out, sizeof(*out), &address, sizeof(address)); } diff --git a/stratosphere/boot/source/boot_rtc_driver.hpp b/stratosphere/boot/source/boot_rtc_driver.hpp index 7225d350e..d40bb5949 100644 --- a/stratosphere/boot/source/boot_rtc_driver.hpp +++ b/stratosphere/boot/source/boot_rtc_driver.hpp @@ -20,16 +20,14 @@ namespace ams::boot { class RtcDriver { private: - i2c::driver::Session i2c_session; + i2c::driver::I2cSession i2c_session; public: RtcDriver() { - i2c::driver::Initialize(); - i2c::driver::OpenSession(&this->i2c_session, I2cDevice_Max77620Rtc); + R_ABORT_UNLESS(i2c::driver::OpenSession(std::addressof(this->i2c_session), i2c::DeviceCode_Max77620Rtc)); } ~RtcDriver() { i2c::driver::CloseSession(this->i2c_session); - i2c::driver::Finalize(); } private: Result ReadRtcRegister(u8 *out, u8 address); diff --git a/stratosphere/boot/source/boot_splash_screen.cpp b/stratosphere/boot/source/boot_splash_screen.cpp index 01b45aed5..23fa3eb1b 100644 --- a/stratosphere/boot/source/boot_splash_screen.cpp +++ b/stratosphere/boot/source/boot_splash_screen.cpp @@ -29,8 +29,8 @@ namespace ams::boot { } void ShowSplashScreen() { - const u32 boot_reason = GetBootReason(); - if (boot_reason == 1 || boot_reason == 4) { + const auto boot_reason = GetBootReason(); + if (boot_reason == spl::BootReason_AcOk || boot_reason == spl::BootReason_RtcAlarm2) { return; } @@ -38,7 +38,7 @@ namespace ams::boot { { /* Splash screen is shown for 2 seconds. */ ShowDisplay(SplashScreenX, SplashScreenY, SplashScreenW, SplashScreenH, SplashScreen); - svcSleepThread(2'000'000'000ul); + os::SleepThread(TimeSpan::FromSeconds(2)); } FinalizeDisplay(); } diff --git a/stratosphere/boot/source/boot_wake_control_configs.inc b/stratosphere/boot/source/boot_wake_control_configs.inc deleted file mode 100644 index 8bf77fa9e..000000000 --- a/stratosphere/boot/source/boot_wake_control_configs.inc +++ /dev/null @@ -1,35 +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 . - */ - -struct WakeControlConfig { - u32 reg_offset; - u32 mask_val; - bool flag_val; -}; - -constexpr WakeControlConfig WakeControlConfigs[] = { - {APBDEV_PMC_CNTRL, 0x0800, true}, - {APBDEV_PMC_CNTRL, 0x0400, false}, - {APBDEV_PMC_CNTRL, 0x0200, true}, - {APBDEV_PMC_CNTRL, 0x0100, false}, - {APBDEV_PMC_CNTRL, 0x0040, false}, - {APBDEV_PMC_CNTRL, 0x0020, false}, - {APBDEV_PMC_CNTRL2, 0x4000, true}, - {APBDEV_PMC_CNTRL2, 0x0200, false}, - {APBDEV_PMC_CNTRL2, 0x0001, true}, -}; - -constexpr size_t NumWakeControlConfigs = util::size(WakeControlConfigs); diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_calcio.inc b/stratosphere/boot/source/boot_wake_pin_configuration_calcio.inc deleted file mode 100644 index abeb3e6c5..000000000 --- a/stratosphere/boot/source/boot_wake_pin_configuration_calcio.inc +++ /dev/null @@ -1,69 +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 . - */ - -static constexpr WakePinConfig WakePinConfigsCalcio[] = { - {0x00, false, 0x02}, - {0x01, false, 0x02}, - {0x02, false, 0x02}, - {0x03, false, 0x02}, - {0x04, false, 0x02}, - {0x05, false, 0x02}, - {0x06, false, 0x02}, - {0x07, false, 0x02}, - {0x08, true, 0x01}, - {0x0A, false, 0x02}, - {0x0B, false, 0x02}, - {0x0C, false, 0x02}, - {0x0D, false, 0x02}, - {0x0E, true, 0x00}, - {0x0F, false, 0x02}, - {0x11, false, 0x02}, - {0x12, false, 0x02}, - {0x13, false, 0x02}, - {0x14, false, 0x02}, - {0x15, false, 0x02}, - {0x16, false, 0x02}, - {0x17, false, 0x02}, - {0x18, false, 0x02}, - {0x19, false, 0x02}, - {0x1A, false, 0x02}, - {0x1B, false, 0x00}, - {0x1C, false, 0x02}, - {0x21, false, 0x02}, - {0x22, false, 0x00}, - {0x23, true, 0x02}, - {0x24, false, 0x02}, - {0x2D, false, 0x02}, - {0x2E, false, 0x02}, - {0x2F, false, 0x02}, - {0x30, false, 0x02}, - {0x31, false, 0x02}, - {0x32, false, 0x02}, - {0x33, true, 0x00}, - {0x34, true, 0x00}, - {0x35, false, 0x02}, - {0x36, false, 0x02}, - {0x37, false, 0x02}, - {0x38, false, 0x02}, - {0x39, false, 0x00}, - {0x3A, false, 0x02}, - {0x3B, false, 0x02}, - {0x3D, false, 0x02}, - {0x3E, false, 0x02}, - {0x3F, false, 0x02}, -}; - -static constexpr size_t NumWakePinConfigsCalcio = util::size(WakePinConfigsCalcio); diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_copper.inc b/stratosphere/boot/source/boot_wake_pin_configuration_copper.inc deleted file mode 100644 index 7105cb052..000000000 --- a/stratosphere/boot/source/boot_wake_pin_configuration_copper.inc +++ /dev/null @@ -1,69 +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 . - */ - -static constexpr WakePinConfig WakePinConfigsCopper[] = { - {0x00, true, 0x02}, - {0x01, false, 0x02}, - {0x02, false, 0x02}, - {0x03, true, 0x02}, - {0x04, false, 0x02}, - {0x05, true, 0x02}, - {0x06, false, 0x02}, - {0x07, false, 0x02}, - {0x08, true, 0x02}, - {0x0A, false, 0x02}, - {0x0B, false, 0x02}, - {0x0C, false, 0x02}, - {0x0D, false, 0x02}, - {0x0E, true, 0x00}, - {0x0F, false, 0x02}, - {0x11, false, 0x02}, - {0x12, false, 0x02}, - {0x13, false, 0x02}, - {0x14, false, 0x02}, - {0x15, false, 0x02}, - {0x16, false, 0x02}, - {0x17, false, 0x02}, - {0x18, true, 0x02}, - {0x19, false, 0x02}, - {0x1A, false, 0x02}, - {0x1B, false, 0x00}, - {0x1C, false, 0x02}, - {0x21, false, 0x02}, - {0x22, false, 0x00}, - {0x23, false, 0x02}, - {0x24, false, 0x02}, - {0x2D, false, 0x02}, - {0x2E, false, 0x02}, - {0x2F, true, 0x02}, - {0x30, true, 0x02}, - {0x31, false, 0x02}, - {0x32, true, 0x02}, - {0x33, true, 0x00}, - {0x34, true, 0x00}, - {0x35, false, 0x02}, - {0x36, false, 0x02}, - {0x37, false, 0x02}, - {0x38, false, 0x02}, - {0x39, false, 0x02}, - {0x3A, false, 0x02}, - {0x3B, false, 0x02}, - {0x3D, false, 0x02}, - {0x3E, false, 0x02}, - {0x3F, false, 0x02}, -}; - -static constexpr size_t NumWakePinConfigsCopper = util::size(WakePinConfigsCopper); diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_hoag.inc b/stratosphere/boot/source/boot_wake_pin_configuration_hoag.inc deleted file mode 100644 index c199fecd1..000000000 --- a/stratosphere/boot/source/boot_wake_pin_configuration_hoag.inc +++ /dev/null @@ -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 . - */ - -static constexpr WakePinConfig WakePinConfigsHoag[] = { - {0x00, false, 0x02}, - {0x01, false, 0x02}, - {0x02, false, 0x02}, - {0x03, false, 0x02}, - {0x04, true, 0x02}, - {0x05, true, 0x02}, - {0x06, false, 0x02}, - {0x07, true, 0x02}, - {0x08, true, 0x01}, - {0x0A, true, 0x02}, - {0x0B, false, 0x02}, - {0x0C, false, 0x02}, - {0x0D, false, 0x02}, - {0x0E, true, 0x00}, - {0x0F, false, 0x02}, - {0x11, false, 0x02}, - {0x12, false, 0x02}, - {0x13, false, 0x02}, - {0x14, false, 0x02}, - {0x15, false, 0x02}, - {0x16, false, 0x02}, - {0x17, false, 0x02}, - {0x18, false, 0x02}, - {0x19, false, 0x02}, - {0x1A, false, 0x02}, - {0x1B, true, 0x00}, - {0x1C, false, 0x02}, - {0x20, false, 0x02}, - {0x21, false, 0x02}, - {0x22, true, 0x00}, - {0x23, true, 0x02}, - {0x24, false, 0x02}, - {0x2D, false, 0x02}, - {0x2E, false, 0x02}, - {0x2F, false, 0x02}, - {0x30, true, 0x02}, - {0x31, false, 0x02}, - {0x32, false, 0x02}, - {0x33, true, 0x00}, - {0x34, true, 0x00}, - {0x35, false, 0x02}, - {0x36, false, 0x02}, - {0x37, false, 0x02}, - {0x38, false, 0x02}, - {0x39, true, 0x00}, - {0x3A, false, 0x02}, - {0x3B, false, 0x02}, - {0x3D, false, 0x02}, - {0x3E, false, 0x02}, - {0x3F, false, 0x02}, -}; - -static constexpr size_t NumWakePinConfigsHoag = util::size(WakePinConfigsHoag); diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_icosa.inc b/stratosphere/boot/source/boot_wake_pin_configuration_icosa.inc deleted file mode 100644 index ddfb16a6f..000000000 --- a/stratosphere/boot/source/boot_wake_pin_configuration_icosa.inc +++ /dev/null @@ -1,69 +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 . - */ - -static constexpr WakePinConfig WakePinConfigsIcosa[] = { - {0x00, false, 0x02}, - {0x01, false, 0x02}, - {0x02, false, 0x02}, - {0x03, false, 0x02}, - {0x04, true, 0x02}, - {0x05, false, 0x02}, - {0x06, true, 0x02}, - {0x07, true, 0x02}, - {0x08, true, 0x01}, - {0x0A, true, 0x02}, - {0x0B, false, 0x02}, - {0x0C, false, 0x02}, - {0x0D, false, 0x02}, - {0x0E, true, 0x00}, - {0x0F, false, 0x02}, - {0x11, false, 0x02}, - {0x12, false, 0x02}, - {0x13, false, 0x02}, - {0x14, false, 0x02}, - {0x15, false, 0x02}, - {0x16, false, 0x02}, - {0x17, false, 0x02}, - {0x18, false, 0x02}, - {0x19, false, 0x02}, - {0x1A, false, 0x02}, - {0x1B, true, 0x00}, - {0x1C, false, 0x02}, - {0x21, false, 0x02}, - {0x22, true, 0x00}, - {0x23, true, 0x02}, - {0x24, false, 0x02}, - {0x2D, false, 0x02}, - {0x2E, false, 0x02}, - {0x2F, false, 0x02}, - {0x30, true, 0x02}, - {0x31, false, 0x02}, - {0x32, false, 0x02}, - {0x33, true, 0x00}, - {0x34, true, 0x00}, - {0x35, false, 0x02}, - {0x36, false, 0x02}, - {0x37, false, 0x02}, - {0x38, false, 0x02}, - {0x39, false, 0x02}, - {0x3A, false, 0x02}, - {0x3B, false, 0x02}, - {0x3D, false, 0x02}, - {0x3E, false, 0x02}, - {0x3F, false, 0x02}, -}; - -static constexpr size_t NumWakePinConfigsIcosa = util::size(WakePinConfigsIcosa); diff --git a/stratosphere/boot/source/boot_wake_pin_configuration_iowa.inc b/stratosphere/boot/source/boot_wake_pin_configuration_iowa.inc deleted file mode 100644 index 53dbe0007..000000000 --- a/stratosphere/boot/source/boot_wake_pin_configuration_iowa.inc +++ /dev/null @@ -1,69 +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 . - */ - -static constexpr WakePinConfig WakePinConfigsIowa[] = { - {0x00, false, 0x02}, - {0x01, false, 0x02}, - {0x02, false, 0x02}, - {0x03, false, 0x02}, - {0x04, true, 0x02}, - {0x05, false, 0x02}, - {0x06, true, 0x02}, - {0x07, true, 0x02}, - {0x08, true, 0x01}, - {0x0A, true, 0x02}, - {0x0B, false, 0x02}, - {0x0C, false, 0x02}, - {0x0D, false, 0x02}, - {0x0E, true, 0x00}, - {0x0F, false, 0x02}, - {0x11, false, 0x02}, - {0x12, false, 0x02}, - {0x13, false, 0x02}, - {0x14, false, 0x02}, - {0x15, false, 0x02}, - {0x16, false, 0x02}, - {0x17, false, 0x02}, - {0x18, false, 0x02}, - {0x19, false, 0x02}, - {0x1A, false, 0x02}, - {0x1B, true, 0x00}, - {0x1C, false, 0x02}, - {0x21, false, 0x02}, - {0x22, true, 0x00}, - {0x23, true, 0x02}, - {0x24, false, 0x02}, - {0x2D, false, 0x02}, - {0x2E, false, 0x02}, - {0x2F, false, 0x02}, - {0x30, true, 0x02}, - {0x31, false, 0x02}, - {0x32, false, 0x02}, - {0x33, true, 0x00}, - {0x34, true, 0x00}, - {0x35, false, 0x02}, - {0x36, false, 0x02}, - {0x37, false, 0x02}, - {0x38, false, 0x02}, - {0x39, false, 0x02}, - {0x3A, false, 0x02}, - {0x3B, false, 0x02}, - {0x3D, false, 0x02}, - {0x3E, false, 0x02}, - {0x3F, false, 0x02}, -}; - -static constexpr size_t NumWakePinConfigsIowa = util::size(WakePinConfigsIowa); diff --git a/stratosphere/boot/source/boot_wake_pins.cpp b/stratosphere/boot/source/boot_wake_pins.cpp deleted file mode 100644 index ccfc94700..000000000 --- a/stratosphere/boot/source/boot_wake_pins.cpp +++ /dev/null @@ -1,132 +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 . - */ -#include -#include "boot_pmc_wrapper.hpp" -#include "boot_wake_pins.hpp" - -#include "boot_registers_pmc.hpp" - -namespace ams::boot { - - /* Include configuration into anonymous namespace. */ - namespace { - - struct WakePinConfig { - u32 index; - bool enabled; - u32 level; - }; - -#include "boot_wake_control_configs.inc" -#include "boot_wake_pin_configuration_icosa.inc" -#include "boot_wake_pin_configuration_copper.inc" -#include "boot_wake_pin_configuration_hoag.inc" -#include "boot_wake_pin_configuration_iowa.inc" -#include "boot_wake_pin_configuration_calcio.inc" - - } - - namespace { - - /* Helpers. */ - void UpdatePmcControlBit(const u32 reg_offset, const u32 mask_val, const bool flag) { - WritePmcRegister(PmcBase + reg_offset, flag ? UINT32_MAX : 0, mask_val); - ReadPmcRegister(PmcBase + reg_offset); - } - - void InitializePmcWakeConfiguration(const bool is_blink) { - /* Initialize APBDEV_PMC_WAKE_DEBOUNCE_EN, do a dummy read. */ - WritePmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN, 0); - ReadPmcRegister(PmcBase + APBDEV_PMC_WAKE_DEBOUNCE_EN); - - /* Initialize APBDEV_PMC_BLINK_TIMER, do a dummy read. */ - WritePmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER, 0x8008800); - ReadPmcRegister(PmcBase + APBDEV_PMC_BLINK_TIMER); - - /* Set control bits, do dummy reads. */ - for (size_t i = 0; i < NumWakeControlConfigs; i++) { - UpdatePmcControlBit(WakeControlConfigs[i].reg_offset, WakeControlConfigs[i].mask_val, WakeControlConfigs[i].flag_val); - } - - /* Set bit 0x80 in APBDEV_PMC_CNTRL based on is_blink, do dummy read. */ - UpdatePmcControlBit(APBDEV_PMC_CNTRL, 0x80, is_blink); - - /* Set bit 0x100000 in APBDEV_PMC_DPD_PADS_ORIDE based on is_blink, do dummy read. */ - UpdatePmcControlBit(APBDEV_PMC_DPD_PADS_ORIDE, 0x100000, is_blink); - } - - void SetWakeEventLevel(u32 index, u32 level) { - u32 pmc_wake_level_reg_offset = index <= 0x1F ? APBDEV_PMC_WAKE_LVL : APBDEV_PMC_WAKE2_LVL; - u32 pmc_wake_level_mask_reg_offset = index <= 0x1F ? APBDEV_PMC_AUTO_WAKE_LVL_MASK : APBDEV_PMC_AUTO_WAKE2_LVL_MASK; - if (level != 2) { - std::swap(pmc_wake_level_reg_offset, pmc_wake_level_mask_reg_offset); - } - - const u32 mask_val = (1 << (index & 0x1F)); - - /* Clear level reg bit. */ - UpdatePmcControlBit(pmc_wake_level_reg_offset, mask_val, false); - - /* Set or clear mask reg bit. */ - UpdatePmcControlBit(pmc_wake_level_mask_reg_offset, mask_val, level > 0); - } - - void SetWakeEventEnabled(u32 index, bool enabled) { - /* Set or clear enabled bit. */ - UpdatePmcControlBit(index <= 0x1F ? APBDEV_PMC_WAKE_MASK : APBDEV_PMC_WAKE2_MASK, (1 << (index & 0x1F)), enabled); - } - - } - - void SetInitialWakePinConfiguration() { - InitializePmcWakeConfiguration(false); - - /* Set wake event levels, wake event enables. */ - const WakePinConfig *configs = nullptr; - size_t num_configs = 0; - - switch (spl::GetHardwareType()) { - case spl::HardwareType::Icosa: - configs = WakePinConfigsIcosa; - num_configs = NumWakePinConfigsIcosa; - break; - case spl::HardwareType::Copper: - configs = WakePinConfigsCopper; - num_configs = NumWakePinConfigsCopper; - break; - case spl::HardwareType::Hoag: - configs = WakePinConfigsHoag; - num_configs = NumWakePinConfigsHoag; - break; - case spl::HardwareType::Iowa: - configs = WakePinConfigsIowa; - num_configs = NumWakePinConfigsIowa; - case spl::HardwareType::Calcio: - configs = WakePinConfigsCalcio; - num_configs = NumWakePinConfigsCalcio; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - AMS_ABORT_UNLESS(configs != nullptr); - - for (size_t i = 0; i < num_configs; i++) { - SetWakeEventLevel(configs[i].index, configs[i].level); - SetWakeEventEnabled(configs[i].index, configs[i].enabled); - } - } - -} diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp b/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp deleted file mode 100644 index 984c4423d..000000000 --- a/stratosphere/boot/source/gpio/gpio_initial_configuration.cpp +++ /dev/null @@ -1,102 +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 . - */ -#include -#include "gpio_initial_configuration.hpp" -#include "gpio_utils.hpp" - -namespace ams::gpio { - - namespace { - - struct InitialConfig { - u32 pad_name; - GpioDirection direction; - GpioValue value; - }; - - /* Include all initial configuration definitions. */ -#include "gpio_initial_configuration_icosa.inc" -#include "gpio_initial_configuration_copper.inc" -#include "gpio_initial_configuration_hoag.inc" -#include "gpio_initial_configuration_iowa.inc" -#include "gpio_initial_configuration_calcio.inc" - - } - - void SetInitialConfiguration() { - const InitialConfig *configs = nullptr; - size_t num_configs = 0; - const auto hw_type = spl::GetHardwareType(); - const auto hos_ver = hos::GetVersion(); - - /* Choose GPIO map. */ - if (hos_ver >= hos::Version_2_0_0) { - switch (hw_type) { - case spl::HardwareType::Icosa: - { - if (hos_ver >= hos::Version_4_0_0) { - configs = InitialConfigsIcosa4x; - num_configs = NumInitialConfigsIcosa4x; - } else { - configs = InitialConfigsIcosa; - num_configs = NumInitialConfigsIcosa; - } - } - break; - case spl::HardwareType::Copper: - configs = InitialConfigsCopper; - num_configs = NumInitialConfigsCopper; - break; - case spl::HardwareType::Hoag: - configs = InitialConfigsHoag; - num_configs = NumInitialConfigsHoag; - break; - case spl::HardwareType::Iowa: - configs = InitialConfigsIowa; - num_configs = NumInitialConfigsIowa; - break; - case spl::HardwareType::Calcio: - configs = InitialConfigsCalcio; - num_configs = NumInitialConfigsCalcio; - break; - /* Unknown hardware type, we can't proceed. */ - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } else { - /* Until 2.0.0, the GPIO map for Icosa was used for all hardware types. */ - configs = InitialConfigsIcosa; - num_configs = NumInitialConfigsIcosa; - } - - /* Ensure we found an appropriate config. */ - AMS_ABORT_UNLESS(configs != nullptr); - - for (size_t i = 0; i < num_configs; i++) { - /* Configure the GPIO. */ - Configure(configs[i].pad_name); - - /* Set the GPIO's direction. */ - SetDirection(configs[i].pad_name, configs[i].direction); - - if (configs[i].direction == GpioDirection_Output) { - /* Set the GPIO's value. */ - SetValue(configs[i].pad_name, configs[i].value); - } - } - } - -} - diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_calcio.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_calcio.inc deleted file mode 100644 index 0a2f72d5b..000000000 --- a/stratosphere/boot/source/gpio/gpio_initial_configuration_calcio.inc +++ /dev/null @@ -1,51 +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 . - */ - -constexpr InitialConfig InitialConfigsCalcio[] = { - {0x50, GpioDirection_Output, GpioValue_Low}, - {0x51, GpioDirection_Output, GpioValue_Low}, - {0x52, GpioDirection_Output, GpioValue_Low}, - {0x53, GpioDirection_Output, GpioValue_Low}, - {0x02, GpioDirection_Output, GpioValue_Low}, - {0x0B, GpioDirection_Input, GpioValue_Low}, - {0x14, GpioDirection_Input, GpioValue_High}, - {0x18, GpioDirection_Input, GpioValue_Low}, - {0x19, GpioDirection_Input, GpioValue_High}, - {0x1A, GpioDirection_Input, GpioValue_High}, - {0x1C, GpioDirection_Input, GpioValue_High}, - {0x20, GpioDirection_Output, GpioValue_Low}, - {0x38, GpioDirection_Input, GpioValue_High}, - {0x23, GpioDirection_Input, GpioValue_High}, - {0x25, GpioDirection_Input, GpioValue_Low}, - {0x26, GpioDirection_Input, GpioValue_Low}, - {0x27, GpioDirection_Input, GpioValue_Low}, - {0x28, GpioDirection_Input, GpioValue_High}, - {0x4F, GpioDirection_Input, GpioValue_High}, - {0x48, GpioDirection_Output, GpioValue_Low}, - {0x4C, GpioDirection_Input, GpioValue_High}, - {0x4A, GpioDirection_Output, GpioValue_Low}, - {0x2D, GpioDirection_Output, GpioValue_Low}, - {0x2E, GpioDirection_Output, GpioValue_Low}, - {0x37, GpioDirection_Input, GpioValue_Low}, - {0x2F, GpioDirection_Output, GpioValue_Low}, - {0x03, GpioDirection_Output, GpioValue_Low}, - {0x30, GpioDirection_Input, GpioValue_Low}, - {0x31, GpioDirection_Output, GpioValue_Low}, - {0x49, GpioDirection_Output, GpioValue_Low}, - {0x4E, GpioDirection_Input, GpioValue_Low}, -}; - -constexpr u32 NumInitialConfigsCalcio = util::size(InitialConfigsCalcio); diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_copper.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_copper.inc deleted file mode 100644 index 70771f22a..000000000 --- a/stratosphere/boot/source/gpio/gpio_initial_configuration_copper.inc +++ /dev/null @@ -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 . - */ - -constexpr InitialConfig InitialConfigsCopper[] = { - {0x40, GpioDirection_Output, GpioValue_Low}, - {0x05, GpioDirection_Output, GpioValue_Low}, - {0x41, GpioDirection_Input, GpioValue_High}, - {0x42, GpioDirection_Input, GpioValue_Low}, - {0x43, GpioDirection_Output, GpioValue_Low}, - {0x02, GpioDirection_Output, GpioValue_Low}, - {0x07, GpioDirection_Output, GpioValue_Low}, - {0x44, GpioDirection_Input, GpioValue_High}, - {0x45, GpioDirection_Input, GpioValue_High}, - {0x0F, GpioDirection_Input, GpioValue_High}, - {0x46, GpioDirection_Output, GpioValue_Low}, - {0x47, GpioDirection_Output, GpioValue_Low}, - {0x10, GpioDirection_Input, GpioValue_Low}, - {0x11, GpioDirection_Input, GpioValue_Low}, - {0x12, GpioDirection_Input, GpioValue_Low}, - {0x13, GpioDirection_Input, GpioValue_Low}, - {0x14, GpioDirection_Input, GpioValue_High}, - {0x18, GpioDirection_Input, GpioValue_Low}, - {0x19, GpioDirection_Input, GpioValue_High}, - {0x1A, GpioDirection_Input, GpioValue_High}, - {0x1C, GpioDirection_Input, GpioValue_High}, - {0x4D, GpioDirection_Output, GpioValue_Low}, - {0x20, GpioDirection_Output, GpioValue_Low}, - {0x38, GpioDirection_Input, GpioValue_High}, - {0x23, GpioDirection_Input, GpioValue_High}, - {0x25, GpioDirection_Input, GpioValue_Low}, - {0x26, GpioDirection_Input, GpioValue_Low}, - {0x27, GpioDirection_Input, GpioValue_Low}, - {0x28, GpioDirection_Input, GpioValue_High}, - {0x29, GpioDirection_Input, GpioValue_High}, - {0x2A, GpioDirection_Input, GpioValue_High}, - {0x48, GpioDirection_Output, GpioValue_Low}, - {0x49, GpioDirection_Output, GpioValue_Low}, - {0x4A, GpioDirection_Output, GpioValue_Low}, - {0x2D, GpioDirection_Output, GpioValue_Low}, - {0x2E, GpioDirection_Output, GpioValue_Low}, - {0x37, GpioDirection_Input, GpioValue_Low}, - {0x2F, GpioDirection_Output, GpioValue_Low}, - {0x03, GpioDirection_Output, GpioValue_Low}, - {0x30, GpioDirection_Input, GpioValue_Low}, - {0x31, GpioDirection_Output, GpioValue_Low}, - {0x4B, GpioDirection_Output, GpioValue_Low}, - {0x4C, GpioDirection_Input, GpioValue_High}, - {0x4E, GpioDirection_Input, GpioValue_Low}, -}; - -constexpr u32 NumInitialConfigsCopper = util::size(InitialConfigsCopper); diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_hoag.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_hoag.inc deleted file mode 100644 index 8e18daf4b..000000000 --- a/stratosphere/boot/source/gpio/gpio_initial_configuration_hoag.inc +++ /dev/null @@ -1,79 +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 . - */ - -constexpr InitialConfig InitialConfigsHoag[] = { - {0x05, GpioDirection_Output, GpioValue_Low}, - {0x06, GpioDirection_Input, GpioValue_Low}, - {0x50, GpioDirection_Output, GpioValue_Low}, - {0x51, GpioDirection_Output, GpioValue_Low}, - {0x52, GpioDirection_Output, GpioValue_Low}, - {0x53, GpioDirection_Output, GpioValue_Low}, - {0x02, GpioDirection_Output, GpioValue_Low}, - {0x3C, GpioDirection_Input, GpioValue_Low}, - {0x56, GpioDirection_Input, GpioValue_High}, - {0x0F, GpioDirection_Input, GpioValue_High}, - {0x09, GpioDirection_Input, GpioValue_Low}, - {0x0A, GpioDirection_Output, GpioValue_Low}, - {0x0B, GpioDirection_Input, GpioValue_Low}, - {0x57, GpioDirection_Output, GpioValue_Low}, - {0x58, GpioDirection_Output, GpioValue_Low}, - {0x0D, GpioDirection_Output, GpioValue_Low}, - {0x59, GpioDirection_Output, GpioValue_Low}, - {0x14, GpioDirection_Input, GpioValue_High}, - {0x16, GpioDirection_Input, GpioValue_Low}, - {0x15, GpioDirection_Input, GpioValue_Low}, - {0x17, GpioDirection_Input, GpioValue_High}, - {0x18, GpioDirection_Input, GpioValue_Low}, - {0x19, GpioDirection_Input, GpioValue_High}, - {0x1A, GpioDirection_Input, GpioValue_High}, - {0x1B, GpioDirection_Input, GpioValue_Low}, - {0x1C, GpioDirection_Input, GpioValue_High}, - {0x1D, GpioDirection_Output, GpioValue_Low}, - {0x1E, GpioDirection_Output, GpioValue_Low}, - {0x5B, GpioDirection_Input, GpioValue_High}, - {0x20, GpioDirection_Output, GpioValue_Low}, - {0x21, GpioDirection_Input, GpioValue_Low}, - {0x38, GpioDirection_Input, GpioValue_High}, - {0x23, GpioDirection_Input, GpioValue_High}, - {0x01, GpioDirection_Output, GpioValue_Low}, - {0x5C, GpioDirection_Output, GpioValue_Low}, - {0x54, GpioDirection_Input, GpioValue_Low}, - {0x24, GpioDirection_Output, GpioValue_Low}, - {0x25, GpioDirection_Input, GpioValue_Low}, - {0x26, GpioDirection_Input, GpioValue_Low}, - {0x27, GpioDirection_Input, GpioValue_Low}, - {0x28, GpioDirection_Input, GpioValue_High}, - {0x1F, GpioDirection_Output, GpioValue_Low}, - {0x4F, GpioDirection_Input, GpioValue_High}, - {0x55, GpioDirection_Output, GpioValue_Low}, - {0x5F, GpioDirection_Input, GpioValue_Low}, - {0x60, GpioDirection_Input, GpioValue_Low}, - {0x61, GpioDirection_Input, GpioValue_Low}, - {0x62, GpioDirection_Input, GpioValue_Low}, - {0x2D, GpioDirection_Output, GpioValue_Low}, - {0x2E, GpioDirection_Output, GpioValue_Low}, - {0x37, GpioDirection_Input, GpioValue_Low}, - {0x2F, GpioDirection_Output, GpioValue_Low}, - {0x03, GpioDirection_Output, GpioValue_Low}, - {0x30, GpioDirection_Input, GpioValue_Low}, - {0x3B, GpioDirection_Input, GpioValue_Low}, - {0x31, GpioDirection_Output, GpioValue_Low}, - {0x32, GpioDirection_Output, GpioValue_Low}, - {0x33, GpioDirection_Output, GpioValue_Low}, - {0x5A, GpioDirection_Output, GpioValue_Low}, -}; - -constexpr u32 NumInitialConfigsHoag = util::size(InitialConfigsHoag); diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_icosa.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_icosa.inc deleted file mode 100644 index 06a80f49e..000000000 --- a/stratosphere/boot/source/gpio/gpio_initial_configuration_icosa.inc +++ /dev/null @@ -1,145 +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 . - */ - -constexpr InitialConfig InitialConfigsIcosa[] = { - {0x04, GpioDirection_Input, GpioValue_High}, - {0x05, GpioDirection_Output, GpioValue_Low}, - {0x06, GpioDirection_Input, GpioValue_Low}, - {0x02, GpioDirection_Output, GpioValue_Low}, - {0x07, GpioDirection_Output, GpioValue_Low}, - {0x3C, GpioDirection_Input, GpioValue_Low}, - {0x0F, GpioDirection_Input, GpioValue_High}, - {0x08, GpioDirection_Input, GpioValue_Low}, - {0x09, GpioDirection_Input, GpioValue_Low}, - {0x0A, GpioDirection_Output, GpioValue_Low}, - {0x0B, GpioDirection_Input, GpioValue_High}, - {0x0D, GpioDirection_Output, GpioValue_Low}, - {0x0E, GpioDirection_Input, GpioValue_Low}, - {0x10, GpioDirection_Input, GpioValue_Low}, - {0x11, GpioDirection_Input, GpioValue_Low}, - {0x12, GpioDirection_Input, GpioValue_Low}, - {0x13, GpioDirection_Input, GpioValue_Low}, - {0x14, GpioDirection_Input, GpioValue_High}, - {0x16, GpioDirection_Input, GpioValue_Low}, - {0x15, GpioDirection_Input, GpioValue_Low}, - {0x17, GpioDirection_Input, GpioValue_High}, - {0x18, GpioDirection_Input, GpioValue_Low}, - {0x19, GpioDirection_Input, GpioValue_High}, - {0x1A, GpioDirection_Input, GpioValue_High}, - {0x1B, GpioDirection_Input, GpioValue_High}, - {0x1C, GpioDirection_Input, GpioValue_Low}, - {0x1D, GpioDirection_Output, GpioValue_Low}, - {0x1E, GpioDirection_Output, GpioValue_Low}, - {0x20, GpioDirection_Output, GpioValue_Low}, - {0x21, GpioDirection_Input, GpioValue_High}, - {0x38, GpioDirection_Input, GpioValue_High}, - {0x22, GpioDirection_Input, GpioValue_Low}, - {0x23, GpioDirection_Input, GpioValue_High}, - {0x01, GpioDirection_Output, GpioValue_Low}, - {0x39, GpioDirection_Output, GpioValue_Low}, - {0x24, GpioDirection_Output, GpioValue_Low}, - {0x34, GpioDirection_Input, GpioValue_Low}, - {0x25, GpioDirection_Input, GpioValue_Low}, - {0x26, GpioDirection_Input, GpioValue_Low}, - {0x27, GpioDirection_Input, GpioValue_Low}, - {0x2B, GpioDirection_Output, GpioValue_Low}, - {0x28, GpioDirection_Input, GpioValue_High}, - {0x1F, GpioDirection_Output, GpioValue_Low}, - {0x29, GpioDirection_Input, GpioValue_High}, - {0x2A, GpioDirection_Input, GpioValue_High}, - {0x3A, GpioDirection_Output, GpioValue_Low}, - {0x0C, GpioDirection_Input, GpioValue_Low}, - {0x2D, GpioDirection_Output, GpioValue_Low}, - {0x2E, GpioDirection_Output, GpioValue_Low}, - {0x37, GpioDirection_Input, GpioValue_Low}, - {0x2F, GpioDirection_Output, GpioValue_Low}, - {0x03, GpioDirection_Output, GpioValue_Low}, - {0x30, GpioDirection_Input, GpioValue_Low}, - {0x3B, GpioDirection_Input, GpioValue_Low}, - {0x31, GpioDirection_Output, GpioValue_Low}, - {0x32, GpioDirection_Output, GpioValue_Low}, - {0x33, GpioDirection_Output, GpioValue_Low}, - {0x35, GpioDirection_Input, GpioValue_High}, - {0x2C, GpioDirection_Output, GpioValue_Low}, - {0x36, GpioDirection_Output, GpioValue_Low}, -}; - -constexpr u32 NumInitialConfigsIcosa = util::size(InitialConfigsIcosa); - -constexpr InitialConfig InitialConfigsIcosa4x[] = { - {0x04, GpioDirection_Input, GpioValue_High}, - {0x05, GpioDirection_Output, GpioValue_Low}, - {0x06, GpioDirection_Input, GpioValue_Low}, - {0x02, GpioDirection_Output, GpioValue_Low}, - {0x07, GpioDirection_Output, GpioValue_Low}, - {0x3C, GpioDirection_Input, GpioValue_Low}, - {0x0F, GpioDirection_Input, GpioValue_High}, - {0x08, GpioDirection_Input, GpioValue_Low}, - {0x09, GpioDirection_Input, GpioValue_Low}, - {0x0A, GpioDirection_Output, GpioValue_Low}, - {0x0B, GpioDirection_Input, GpioValue_High}, - {0x0D, GpioDirection_Output, GpioValue_Low}, - {0x0E, GpioDirection_Input, GpioValue_Low}, - {0x10, GpioDirection_Input, GpioValue_Low}, - {0x11, GpioDirection_Input, GpioValue_Low}, - {0x12, GpioDirection_Input, GpioValue_Low}, - {0x13, GpioDirection_Input, GpioValue_Low}, - {0x14, GpioDirection_Input, GpioValue_High}, - {0x16, GpioDirection_Input, GpioValue_Low}, - {0x15, GpioDirection_Input, GpioValue_Low}, - {0x17, GpioDirection_Input, GpioValue_High}, - {0x18, GpioDirection_Input, GpioValue_High}, - {0x19, GpioDirection_Input, GpioValue_High}, - {0x1A, GpioDirection_Input, GpioValue_High}, - {0x1B, GpioDirection_Input, GpioValue_High}, - {0x1C, GpioDirection_Input, GpioValue_Low}, - {0x1D, GpioDirection_Output, GpioValue_Low}, - {0x1E, GpioDirection_Output, GpioValue_Low}, - {0x20, GpioDirection_Output, GpioValue_Low}, - {0x21, GpioDirection_Input, GpioValue_High}, - {0x38, GpioDirection_Input, GpioValue_High}, - {0x22, GpioDirection_Input, GpioValue_Low}, - {0x23, GpioDirection_Input, GpioValue_High}, - {0x01, GpioDirection_Output, GpioValue_Low}, - {0x39, GpioDirection_Output, GpioValue_Low}, - {0x24, GpioDirection_Output, GpioValue_Low}, - {0x34, GpioDirection_Input, GpioValue_Low}, - {0x25, GpioDirection_Input, GpioValue_Low}, - {0x26, GpioDirection_Input, GpioValue_Low}, - {0x27, GpioDirection_Input, GpioValue_Low}, - {0x2B, GpioDirection_Output, GpioValue_Low}, - {0x28, GpioDirection_Input, GpioValue_High}, - {0x1F, GpioDirection_Output, GpioValue_Low}, - {0x29, GpioDirection_Input, GpioValue_High}, - {0x2A, GpioDirection_Input, GpioValue_High}, - {0x3A, GpioDirection_Output, GpioValue_Low}, - {0x0C, GpioDirection_Input, GpioValue_Low}, - {0x2D, GpioDirection_Output, GpioValue_Low}, - {0x2E, GpioDirection_Output, GpioValue_Low}, - {0x37, GpioDirection_Input, GpioValue_Low}, - {0x2F, GpioDirection_Output, GpioValue_Low}, - {0x03, GpioDirection_Output, GpioValue_Low}, - {0x30, GpioDirection_Input, GpioValue_Low}, - {0x3B, GpioDirection_Input, GpioValue_Low}, - {0x31, GpioDirection_Output, GpioValue_Low}, - {0x32, GpioDirection_Output, GpioValue_Low}, - {0x33, GpioDirection_Output, GpioValue_Low}, - {0x35, GpioDirection_Input, GpioValue_High}, - {0x2C, GpioDirection_Output, GpioValue_Low}, - {0x36, GpioDirection_Output, GpioValue_Low}, -}; - -constexpr u32 NumInitialConfigsIcosa4x = util::size(InitialConfigsIcosa4x); diff --git a/stratosphere/boot/source/gpio/gpio_initial_configuration_iowa.inc b/stratosphere/boot/source/gpio/gpio_initial_configuration_iowa.inc deleted file mode 100644 index f0a017897..000000000 --- a/stratosphere/boot/source/gpio/gpio_initial_configuration_iowa.inc +++ /dev/null @@ -1,79 +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 . - */ - -constexpr InitialConfig InitialConfigsIowa[] = { - {0x04, GpioDirection_Input, GpioValue_High}, - {0x05, GpioDirection_Output, GpioValue_Low}, - {0x06, GpioDirection_Input, GpioValue_Low}, - {0x02, GpioDirection_Output, GpioValue_Low}, - {0x3C, GpioDirection_Input, GpioValue_Low}, - {0x0F, GpioDirection_Input, GpioValue_High}, - {0x08, GpioDirection_Input, GpioValue_Low}, - {0x09, GpioDirection_Input, GpioValue_Low}, - {0x0A, GpioDirection_Output, GpioValue_Low}, - {0x0B, GpioDirection_Input, GpioValue_Low}, - {0x0D, GpioDirection_Output, GpioValue_Low}, - {0x0E, GpioDirection_Input, GpioValue_Low}, - {0x10, GpioDirection_Input, GpioValue_Low}, - {0x11, GpioDirection_Input, GpioValue_Low}, - {0x12, GpioDirection_Input, GpioValue_Low}, - {0x13, GpioDirection_Input, GpioValue_Low}, - {0x59, GpioDirection_Output, GpioValue_Low}, - {0x14, GpioDirection_Input, GpioValue_High}, - {0x16, GpioDirection_Input, GpioValue_Low}, - {0x15, GpioDirection_Input, GpioValue_Low}, - {0x17, GpioDirection_Input, GpioValue_High}, - {0x18, GpioDirection_Input, GpioValue_Low}, - {0x19, GpioDirection_Input, GpioValue_High}, - {0x1A, GpioDirection_Input, GpioValue_High}, - {0x1B, GpioDirection_Input, GpioValue_Low}, - {0x1C, GpioDirection_Input, GpioValue_Low}, - {0x1D, GpioDirection_Output, GpioValue_Low}, - {0x1E, GpioDirection_Output, GpioValue_Low}, - {0x20, GpioDirection_Output, GpioValue_Low}, - {0x21, GpioDirection_Input, GpioValue_Low}, - {0x38, GpioDirection_Input, GpioValue_High}, - {0x22, GpioDirection_Input, GpioValue_Low}, - {0x23, GpioDirection_Input, GpioValue_High}, - {0x01, GpioDirection_Output, GpioValue_Low}, - {0x39, GpioDirection_Output, GpioValue_Low}, - {0x24, GpioDirection_Output, GpioValue_Low}, - {0x34, GpioDirection_Input, GpioValue_Low}, - {0x25, GpioDirection_Input, GpioValue_Low}, - {0x26, GpioDirection_Input, GpioValue_Low}, - {0x27, GpioDirection_Input, GpioValue_Low}, - {0x2B, GpioDirection_Output, GpioValue_Low}, - {0x28, GpioDirection_Input, GpioValue_High}, - {0x1F, GpioDirection_Output, GpioValue_Low}, - {0x4F, GpioDirection_Input, GpioValue_High}, - {0x3A, GpioDirection_Output, GpioValue_Low}, - {0x0C, GpioDirection_Input, GpioValue_Low}, - {0x2D, GpioDirection_Output, GpioValue_Low}, - {0x2E, GpioDirection_Output, GpioValue_Low}, - {0x37, GpioDirection_Input, GpioValue_Low}, - {0x2F, GpioDirection_Output, GpioValue_Low}, - {0x03, GpioDirection_Output, GpioValue_Low}, - {0x30, GpioDirection_Input, GpioValue_Low}, - {0x3B, GpioDirection_Input, GpioValue_Low}, - {0x31, GpioDirection_Output, GpioValue_Low}, - {0x32, GpioDirection_Output, GpioValue_Low}, - {0x33, GpioDirection_Output, GpioValue_Low}, - {0x35, GpioDirection_Input, GpioValue_High}, - {0x2C, GpioDirection_Output, GpioValue_Low}, - {0x36, GpioDirection_Output, GpioValue_Low}, -}; - -constexpr u32 NumInitialConfigsIowa = util::size(InitialConfigsIowa); diff --git a/stratosphere/boot/source/gpio/gpio_map.inc b/stratosphere/boot/source/gpio/gpio_map.inc deleted file mode 100644 index 7a76dcc5e..000000000 --- a/stratosphere/boot/source/gpio/gpio_map.inc +++ /dev/null @@ -1,132 +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 . - */ - -constexpr u32 InvalidPadName = UINT32_MAX; - -constexpr u32 Map[] = { - InvalidPadName, /* Invalid */ - 0x000000CC, /* Port Z, Pin 4 */ - 0x00000024, /* Port E, Pin 4 */ - 0x0000003C, /* Port H, Pin 4 */ - 0x000000DA, /* Port BB, Pin 2 */ - 0x000000DB, /* Port BB, Pin 3 */ - 0x000000DC, /* Port BB, Pin 4 */ - 0x00000025, /* Port E, Pin 5 */ - 0x00000090, /* Port S, Pin 0 */ - 0x00000091, /* Port S, Pin 1 */ - 0x00000096, /* Port S, Pin 6 */ - 0x00000097, /* Port S, Pin 7 */ - 0x00000026, /* Port E, Pin 6 */ - 0x00000005, /* Port A, Pin 5 */ - 0x00000078, /* Port P, Pin 0 */ - 0x00000093, /* Port S, Pin 3 */ - 0x0000007D, /* Port P, Pin 5 */ - 0x0000007C, /* Port P, Pin 4 */ - 0x0000007B, /* Port P, Pin 3 */ - 0x0000007A, /* Port P, Pin 2 */ - 0x000000BC, /* Port X, Pin 4 */ - 0x000000AE, /* Port V, Pin 6 */ - 0x000000BA, /* Port X, Pin 2 */ - 0x000000B9, /* Port X, Pin 1 */ - 0x000000BD, /* Port X, Pin 5 */ - 0x000000BE, /* Port X, Pin 6 */ - 0x000000BF, /* Port X, Pin 7 */ - 0x000000C0, /* Port Y, Pin 0 */ - 0x000000C1, /* Port Y, Pin 1 */ - 0x000000A9, /* Port V, Pin 1 */ - 0x000000AA, /* Port V, Pin 2 */ - 0x00000055, /* Port K, Pin 5 */ - 0x000000AD, /* Port V, Pin 5 */ - 0x000000C8, /* Port Z, Pin 0 */ - 0x000000CA, /* Port Z, Pin 2 */ - 0x000000CB, /* Port Z, Pin 3 */ - 0x0000004F, /* Port J, Pin 7 */ - 0x00000050, /* Port K, Pin 0 */ - 0x00000051, /* Port K, Pin 1 */ - 0x00000052, /* Port K, Pin 2 */ - 0x00000054, /* Port K, Pin 4 */ - 0x00000056, /* Port K, Pin 6 */ - 0x00000057, /* Port K, Pin 7 */ - 0x00000053, /* Port K, Pin 3 */ - 0x000000E3, /* Port CC, Pin 3 */ - 0x00000038, /* Port H, Pin 0 */ - 0x00000039, /* Port H, Pin 1 */ - 0x0000003B, /* Port H, Pin 3 */ - 0x0000003D, /* Port H, Pin 5 */ - 0x0000003F, /* Port H, Pin 7 */ - 0x00000040, /* Port I, Pin 0 */ - 0x00000041, /* Port I, Pin 1 */ - 0x0000003E, /* Port H, Pin 6 */ - 0x000000E2, /* Port CC, Pin 2 */ - 0x000000E4, /* Port CC, Pin 4 */ - 0x0000003A, /* Port H, Pin 2 */ - 0x000000C9, /* Port Z, Pin 1 */ - 0x0000004D, /* Port J, Pin 5 */ - 0x00000058, /* Port L, Pin 0 */ - 0x0000003E, /* Port H, Pin 6 */ - 0x00000026, /* Port E, Pin 6 */ - - /* Copper only */ - InvalidPadName, /* Invalid */ - 0x00000033, /* Port G, Pin 3 */ - 0x0000001C, /* Port D, Pin 4 */ - 0x000000D9, /* Port BB, Pin 1 */ - 0x0000000C, /* Port B, Pin 4 */ - 0x0000000D, /* Port B, Pin 5 */ - 0x00000021, /* Port E, Pin 1 */ - 0x00000027, /* Port E, Pin 7 */ - 0x00000092, /* Port S, Pin 2 */ - 0x00000095, /* Port S, Pin 5 */ - 0x00000098, /* Port T, Pin 0 */ - 0x00000010, /* Port C, Pin 0 */ - 0x00000011, /* Port C, Pin 1 */ - 0x00000012, /* Port C, Pin 2 */ - 0x00000042, /* Port I, Pin 2 */ - 0x000000E6, /* Port CC, Pin 6 */ - - /* 2.0.0+ Copper only */ - 0x000000AC, /* Port V, Pin 4 */ - 0x000000E1, /* Port CC, Pin 1 */ - - /* 5.0.0+ Copper only (unused) */ - 0x00000056, /* Port K, Pin 6 */ - - /* 6.0.0+ */ - 0x00000020, /* Port E, Pin 0 */ - 0x00000021, /* Port E, Pin 1 */ - 0x00000022, /* Port E, Pin 2 */ - 0x00000023, /* Port E, Pin 3 */ - 0x0000004C, /* Port J, Pin 4 */ - 0x00000057, /* Port K, Pin 7 */ - 0x00000027, /* Port S, Pin 4 */ - 0x00000098, /* Port T, Pin 0 */ - 0x00000099, /* Port T, Pin 1 */ - 0x000000BB, /* Port X, Pin 3 */ - 0x000000E5, /* Port CC, Pin 5 */ - 0x000000AB, /* Port V, Pin 3 */ - 0x0000004E, /* Port J, Pin 6 */ - - /* 7.0.0+ */ - 0x00000032, /* Port G, Pin 2 */ - 0x0000001B, /* Port D, Pin 3 */ - 0x00000017, /* Port C, Pin 7 */ - 0x00000018, /* Port D, Pin 0 */ - 0x00000015, /* Port C, Pin 5 */ - 0x00000016, /* Port C, Pin 6 */ - -}; - -static constexpr u32 PadNameMax = util::size(Map); diff --git a/stratosphere/boot/source/gpio/gpio_utils.cpp b/stratosphere/boot/source/gpio/gpio_utils.cpp deleted file mode 100644 index 9f933e365..000000000 --- a/stratosphere/boot/source/gpio/gpio_utils.cpp +++ /dev/null @@ -1,127 +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 . - */ -#include -#include "gpio_utils.hpp" - -namespace ams::gpio { - - namespace { - - /* Pull in GPIO map definitions. */ -#include "gpio_map.inc" - - constexpr u32 PhysicalBase = 0x6000D000; - - /* Globals. */ - bool g_initialized_gpio_vaddr = false; - uintptr_t g_gpio_vaddr = 0; - - /* Helpers. */ - inline u32 GetPadDescriptor(u32 gpio_pad_name) { - AMS_ABORT_UNLESS(gpio_pad_name < PadNameMax); - return Map[gpio_pad_name]; - } - - uintptr_t GetBaseAddress() { - if (!g_initialized_gpio_vaddr) { - g_gpio_vaddr = dd::GetIoMapping(PhysicalBase, os::MemoryPageSize); - g_initialized_gpio_vaddr = true; - } - return g_gpio_vaddr; - } - - - } - - u32 Configure(u32 gpio_pad_name) { - uintptr_t gpio_base_vaddr = GetBaseAddress(); - - /* Fetch this GPIO's pad descriptor */ - const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name); - - /* Discard invalid GPIOs */ - if (gpio_pad_desc == InvalidPadName) { - return InvalidPadName; - } - - /* Convert the GPIO pad descriptor into its register offset */ - u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C)); - - /* Extract the bit and lock values from the GPIO pad descriptor */ - u32 gpio_cnf_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (0x01 << (gpio_pad_desc & 0x07))); - - /* Write to the appropriate GPIO_CNF_x register (upper offset) */ - reg::Write(gpio_base_vaddr + gpio_reg_offset + 0x80, gpio_cnf_val); - - /* Do a dummy read from GPIO_CNF_x register (lower offset) */ - gpio_cnf_val = reg::Read(gpio_base_vaddr + gpio_reg_offset + 0x00); - - return gpio_cnf_val; - } - - u32 SetDirection(u32 gpio_pad_name, GpioDirection dir) { - uintptr_t gpio_base_vaddr = GetBaseAddress(); - - /* Fetch this GPIO's pad descriptor */ - const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name); - - /* Discard invalid GPIOs */ - if (gpio_pad_desc == InvalidPadName) { - return InvalidPadName; - } - - /* Convert the GPIO pad descriptor into its register offset */ - u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C)); - - /* Set the direction bit and lock values */ - u32 gpio_oe_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (static_cast(dir) << (gpio_pad_desc & 0x07))); - - /* Write to the appropriate GPIO_OE_x register (upper offset) */ - reg::Write(gpio_base_vaddr + gpio_reg_offset + 0x90, gpio_oe_val); - - /* Do a dummy read from GPIO_OE_x register (lower offset) */ - gpio_oe_val = reg::Read(gpio_base_vaddr + gpio_reg_offset + 0x10); - - return gpio_oe_val; - } - - u32 SetValue(u32 gpio_pad_name, GpioValue val) { - uintptr_t gpio_base_vaddr = GetBaseAddress(); - - /* Fetch this GPIO's pad descriptor */ - const u32 gpio_pad_desc = GetPadDescriptor(gpio_pad_name); - - /* Discard invalid GPIOs */ - if (gpio_pad_desc == InvalidPadName) { - return InvalidPadName; - } - - /* Convert the GPIO pad descriptor into its register offset */ - u32 gpio_reg_offset = (((gpio_pad_desc << 0x03) & 0xFFFFFF00) | ((gpio_pad_desc >> 0x01) & 0x0C)); - - /* Set the output bit and lock values */ - u32 gpio_out_val = ((0x01 << ((gpio_pad_desc & 0x07) | 0x08)) | (static_cast(val) << (gpio_pad_desc & 0x07))); - - /* Write to the appropriate GPIO_OUT_x register (upper offset) */ - reg::Write(gpio_base_vaddr + gpio_reg_offset + 0xA0, gpio_out_val); - - /* Do a dummy read from GPIO_OUT_x register (lower offset) */ - gpio_out_val = reg::Read(gpio_base_vaddr + gpio_reg_offset + 0x20); - - return gpio_out_val; - } - -} diff --git a/stratosphere/boot/source/i2c/driver/i2c_api.cpp b/stratosphere/boot/source/i2c/driver/i2c_api.cpp deleted file mode 100644 index df9a1bda4..000000000 --- a/stratosphere/boot/source/i2c/driver/i2c_api.cpp +++ /dev/null @@ -1,175 +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 . - */ -#include -#include "i2c_api.hpp" -#include "impl/i2c_resource_manager.hpp" - -namespace ams::i2c::driver { - - namespace { - - /* For convenience. */ - using CommandHandler = Result (*)(const u8 **cur_cmd, u8 **cur_dst, Session& session); - - /* Command handlers. */ - Result SendHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { - I2cTransactionOption option = static_cast( - (((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0) - ); - (*cur_cmd)++; - - size_t num_bytes = (**cur_cmd); - (*cur_cmd)++; - - R_TRY(Send(session, *cur_cmd, num_bytes, option)); - (*cur_cmd) += num_bytes; - - return ResultSuccess(); - } - - Result ReceiveHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { - I2cTransactionOption option = static_cast( - (((**cur_cmd) & (1 << 6)) ? I2cTransactionOption_Start : 0) | (((**cur_cmd) & (1 << 7)) ? I2cTransactionOption_Stop : 0) - ); - (*cur_cmd)++; - - size_t num_bytes = (**cur_cmd); - (*cur_cmd)++; - - R_TRY(Receive(session, *cur_dst, num_bytes, option)); - (*cur_dst) += num_bytes; - - return ResultSuccess(); - } - - Result SubCommandHandler(const u8 **cur_cmd, u8 **cur_dst, Session& session) { - const SubCommand sub_cmd = static_cast((**cur_cmd) >> 2); - (*cur_cmd)++; - - switch (sub_cmd) { - case SubCommand::Sleep: - { - const size_t us = (**cur_cmd); - (*cur_cmd)++; - svcSleepThread(us * 1'000ul); - } - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - return ResultSuccess(); - } - - /* Command handler list. */ - constexpr CommandHandler g_cmd_handlers[static_cast(Command::Count)] = { - SendHandler, - ReceiveHandler, - SubCommandHandler, - }; - - inline impl::ResourceManager &GetResourceManager() { - return impl::ResourceManager::GetInstance(); - } - - inline void CheckInitialized() { - AMS_ABORT_UNLESS(GetResourceManager().IsInitialized()); - } - - } - - /* Initialization. */ - void Initialize() { - GetResourceManager().Initialize(); - } - - void Finalize() { - GetResourceManager().Finalize(); - } - - /* Session management. */ - void OpenSession(Session *out_session, I2cDevice device) { - CheckInitialized(); - AMS_ABORT_UNLESS(impl::IsDeviceSupported(device)); - - const auto bus = impl::GetDeviceBus(device); - const auto slave_address = impl::GetDeviceSlaveAddress(device); - const auto addressing_mode = impl::GetDeviceAddressingMode(device); - const auto speed_mode = impl::GetDeviceSpeedMode(device); - const auto max_retries = impl::GetDeviceMaxRetries(device); - const auto retry_wait_time = impl::GetDeviceRetryWaitTime(device); - GetResourceManager().OpenSession(out_session, bus, slave_address, addressing_mode, speed_mode, max_retries, retry_wait_time); - } - - void CloseSession(Session &session) { - CheckInitialized(); - GetResourceManager().CloseSession(session); - } - - /* Communication. */ - Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option) { - CheckInitialized(); - AMS_ABORT_UNLESS(src != nullptr); - AMS_ABORT_UNLESS(size > 0); - - std::scoped_lock lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx))); - return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(nullptr, src, size, option, impl::Command::Send); - } - - Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option) { - CheckInitialized(); - AMS_ABORT_UNLESS(dst != nullptr); - AMS_ABORT_UNLESS(size > 0); - - std::scoped_lock lk(GetResourceManager().GetTransactionMutex(impl::ConvertFromIndex(session.bus_idx))); - return GetResourceManager().GetSession(session.session_id).DoTransactionWithRetry(dst, nullptr, size, option, impl::Command::Receive); - } - - Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size) { - CheckInitialized(); - AMS_ABORT_UNLESS(dst != nullptr && size > 0); - AMS_ABORT_UNLESS(cmd_list != nullptr && cmd_list_size > 0); - - u8 *cur_dst = static_cast(dst); - const u8 *cur_cmd = static_cast(cmd_list); - const u8 *cmd_list_end = cur_cmd + cmd_list_size; - - while (cur_cmd < cmd_list_end) { - Command cmd = static_cast((*cur_cmd) & 3); - AMS_ABORT_UNLESS(cmd < Command::Count); - - R_TRY(g_cmd_handlers[static_cast(cmd)](&cur_cmd, &cur_dst, session)); - } - - return ResultSuccess(); - } - - /* Power management. */ - void SuspendBuses() { - GetResourceManager().SuspendBuses(); - } - - void ResumeBuses() { - GetResourceManager().ResumeBuses(); - } - - void SuspendPowerBus() { - GetResourceManager().SuspendPowerBus(); - } - - void ResumePowerBus() { - GetResourceManager().ResumePowerBus(); - } - -} diff --git a/stratosphere/boot/source/i2c/driver/i2c_api.hpp b/stratosphere/boot/source/i2c/driver/i2c_api.hpp deleted file mode 100644 index 899d7d75e..000000000 --- a/stratosphere/boot/source/i2c/driver/i2c_api.hpp +++ /dev/null @@ -1,46 +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 . - */ -#pragma once -#include "../i2c_types.hpp" -#include "../i2c_command_list.hpp" - -namespace ams::i2c::driver { - - struct Session { - size_t bus_idx; - size_t session_id; - }; - - /* Initialization. */ - void Initialize(); - void Finalize(); - - /* Session management. */ - void OpenSession(Session *out_session, I2cDevice device); - void CloseSession(Session &session); - - /* Communication. */ - Result Send(Session &session, const void *src, size_t size, I2cTransactionOption option); - Result Receive(Session &session, void *dst, size_t size, I2cTransactionOption option); - Result ExecuteCommandList(Session &session, void *dst, size_t size, const void *cmd_list, size_t cmd_list_size); - - /* Power management. */ - void SuspendBuses(); - void ResumeBuses(); - void SuspendPowerBus(); - void ResumePowerBus(); - -} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp deleted file mode 100644 index fb419af32..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.cpp +++ /dev/null @@ -1,450 +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 . - */ -#include -#include "i2c_pcv.hpp" -#include "i2c_bus_accessor.hpp" - -namespace ams::i2c::driver::impl { - - void BusAccessor::Open(Bus bus, SpeedMode speed_mode) { - std::scoped_lock lk(this->open_mutex); - /* Open new session. */ - this->open_sessions++; - - /* Ensure we're good if this isn't our first session. */ - if (this->open_sessions > 1) { - AMS_ABORT_UNLESS(this->speed_mode == speed_mode); - return; - } - - /* Set all members for chosen bus. */ - { - std::scoped_lock lk(this->register_mutex); - /* Set bus/registers. */ - this->SetBus(bus); - /* Set pcv module. */ - this->pcv_module = ConvertToPcvModule(bus); - /* Set speed mode. */ - this->speed_mode = speed_mode; - /* Setup interrupt event. */ - this->CreateInterruptEvent(bus); - } - } - - void BusAccessor::Close() { - std::scoped_lock lk(this->open_mutex); - /* Close current session. */ - this->open_sessions--; - if (this->open_sessions > 0) { - return; - } - - /* Close interrupt event. */ - os::FinalizeInterruptEvent(std::addressof(this->interrupt_event)); - - /* Close PCV. */ - pcv::Finalize(); - - this->suspended = false; - } - - void BusAccessor::Suspend() { - std::scoped_lock lk(this->open_mutex); - std::scoped_lock lk_reg(this->register_mutex); - - if (!this->suspended) { - this->suspended = true; - - if (this->pcv_module != PcvModule_I2C5) { - this->DisableClock(); - } - } - } - - void BusAccessor::Resume() { - if (this->suspended) { - this->DoInitialConfig(); - this->suspended = false; - } - } - - void BusAccessor::DoInitialConfig() { - std::scoped_lock lk(this->register_mutex); - - if (this->pcv_module != PcvModule_I2C5) { - pcv::Initialize(); - } - - this->ResetController(); - this->SetClock(this->speed_mode); - this->SetPacketMode(); - this->FlushFifos(); - } - - size_t BusAccessor::GetOpenSessions() const { - return this->open_sessions; - } - - bool BusAccessor::GetBusy() const { - /* Nintendo has a loop here that calls a member function to check if busy, retrying a few times. */ - /* This member function does "return false". */ - /* We will not bother with the loop. */ - return false; - } - - void BusAccessor::OnStartTransaction() const { - /* Nothing actually happens here. */ - } - - void BusAccessor::OnStopTransaction() const { - /* Nothing actually happens here. */ - } - - Result BusAccessor::StartTransaction(Command command, AddressingMode addressing_mode, u32 slave_address) { - /* Nothing actually happens here... */ - return ResultSuccess(); - } - - Result BusAccessor::Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) { - std::scoped_lock lk(this->register_mutex); - const u8 *cur_src = data; - size_t remaining = num_bytes; - - /* Set interrupt enable, clear interrupt status. */ - reg::Write(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8E); - reg::Write(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC); - - ON_SCOPE_EXIT { this->ClearInterruptMask(); }; - - /* Send header. */ - this->WriteTransferHeader(TransferMode::Send, option, addressing_mode, slave_address, num_bytes); - - /* Send bytes. */ - while (true) { - const u32 fifo_status = reg::Read(&this->i2c_registers->I2C_FIFO_STATUS_0); - const size_t fifo_cnt = (fifo_status >> 4); - - for (size_t fifo_idx = 0; remaining > 0 && fifo_idx < fifo_cnt; fifo_idx++) { - const size_t cur_bytes = std::min(remaining, sizeof(u32)); - u32 val = 0; - for (size_t i = 0; i < cur_bytes; i++) { - val |= cur_src[i] << (8 * i); - } - reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, val); - - cur_src += cur_bytes; - remaining -= cur_bytes; - } - - if (remaining == 0) { - break; - } - - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), InterruptTimeout)) { - this->HandleTransactionResult(i2c::ResultBusBusy()); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - return i2c::ResultTimedOut(); - } - - R_TRY(this->GetAndHandleTransactionResult()); - } - - reg::Write(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8C); - - /* Wait for successful completion. */ - while (true) { - R_TRY(this->GetAndHandleTransactionResult()); - - /* Check PACKET_XFER_COMPLETE */ - const u32 interrupt_status = reg::Read(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0); - if (interrupt_status & 0x80) { - R_TRY(this->GetAndHandleTransactionResult()); - break; - } - - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), InterruptTimeout)) { - this->HandleTransactionResult(i2c::ResultBusBusy()); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - return i2c::ResultTimedOut(); - } - } - - return ResultSuccess(); - } - - Result BusAccessor::Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address) { - std::scoped_lock lk(this->register_mutex); - u8 *cur_dst = out_data; - size_t remaining = num_bytes; - - /* Set interrupt enable, clear interrupt status. */ - reg::Write(&this->i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0x8D); - reg::Write(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0, 0xFC); - - /* Send header. */ - this->WriteTransferHeader(TransferMode::Receive, option, addressing_mode, slave_address, num_bytes); - - /* Receive bytes. */ - while (remaining > 0) { - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - if (!os::TimedWaitInterruptEvent(std::addressof(this->interrupt_event), InterruptTimeout)) { - this->HandleTransactionResult(i2c::ResultBusBusy()); - this->ClearInterruptMask(); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - return i2c::ResultTimedOut(); - } - - R_TRY(this->GetAndHandleTransactionResult()); - - const u32 fifo_status = reg::Read(&this->i2c_registers->I2C_FIFO_STATUS_0); - const size_t fifo_cnt = std::min((remaining + 3) >> 2, static_cast(fifo_status & 0xF)); - - for (size_t fifo_idx = 0; remaining > 0 && fifo_idx < fifo_cnt; fifo_idx++) { - const u32 val = reg::Read(&this->i2c_registers->I2C_I2C_RX_FIFO_0); - const size_t cur_bytes = std::min(remaining, sizeof(u32)); - for (size_t i = 0; i < cur_bytes; i++) { - cur_dst[i] = static_cast((val >> (8 * i)) & 0xFF); - } - - cur_dst += cur_bytes; - remaining -= cur_bytes; - } - } - - /* N doesn't do ClearInterruptMask. */ - return ResultSuccess(); - } - - void BusAccessor::SetBus(Bus bus) { - this->bus = bus; - this->i2c_registers = GetRegisters(bus); - this->clkrst_registers.SetBus(bus); - } - - void BusAccessor::CreateInterruptEvent(Bus bus) { - static constexpr u64 s_interrupts[] = { - 0x46, 0x74, 0x7C, 0x98, 0x55, 0x5F - }; - const auto index = ConvertToIndex(bus); - AMS_ABORT_UNLESS(index < util::size(s_interrupts)); - os::InitializeInterruptEvent(std::addressof(this->interrupt_event), s_interrupts[index], os::EventClearMode_ManualClear); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - } - - void BusAccessor::SetClock(SpeedMode speed_mode) { - u32 t_high, t_low; - u32 clk_div, src_div; - u32 debounce; - - switch (speed_mode) { - case SpeedMode::Normal: - t_high = 2; - t_low = 4; - clk_div = 0x19; - src_div = 0x13; - debounce = 2; - break; - case SpeedMode::Fast: - t_high = 2; - t_low = 4; - clk_div = 0x19; - src_div = 0x04; - debounce = 2; - break; - case SpeedMode::FastPlus: - t_high = 2; - t_low = 4; - clk_div = 0x10; - src_div = 0x02; - debounce = 0; - break; - case SpeedMode::HighSpeed: - t_high = 3; - t_low = 8; - clk_div = 0x02; - src_div = 0x02; - debounce = 0; - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - if (speed_mode == SpeedMode::HighSpeed) { - reg::Write(&this->i2c_registers->I2C_I2C_HS_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low)); - reg::Write(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, clk_div); - } else { - reg::Write(&this->i2c_registers->I2C_I2C_INTERFACE_TIMING_0_0, (t_high << 8) | (t_low)); - reg::Write(&this->i2c_registers->I2C_I2C_CLK_DIVISOR_REGISTER_0, (clk_div << 16)); - } - - reg::Write(&this->i2c_registers->I2C_I2C_CNFG_0, debounce); - reg::Read(&this->i2c_registers->I2C_I2C_CNFG_0); - - if (this->pcv_module != PcvModule_I2C5) { - R_ABORT_UNLESS(pcv::SetReset(this->pcv_module, true)); - R_ABORT_UNLESS(pcv::SetClockRate(this->pcv_module, (408'000'000) / (src_div + 1))); - R_ABORT_UNLESS(pcv::SetReset(this->pcv_module, false)); - } - } - - void BusAccessor::ResetController() const { - if (this->pcv_module != PcvModule_I2C5) { - R_ABORT_UNLESS(pcv::SetReset(this->pcv_module, true)); - R_ABORT_UNLESS(pcv::SetClockRate(this->pcv_module, 81'600'000)); - R_ABORT_UNLESS(pcv::SetReset(this->pcv_module, false)); - } - } - - void BusAccessor::ClearBus() const { - bool success = false; - for (size_t i = 0; i < 3 && !success; i++) { - success = true; - - this->ResetController(); - - reg::Write(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x90000); - reg::SetBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x4); - reg::SetBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x2); - - reg::SetBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1); - { - u64 start_tick = armGetSystemTick(); - while (reg::Read(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0) & 1) { - if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { - success = false; - break; - } - } - } - if (!success) { - continue; - } - - reg::SetBits(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0, 0x1); - { - u64 start_tick = armGetSystemTick(); - while (reg::Read(&this->i2c_registers->I2C_I2C_BUS_CLEAR_CONFIG_0) & 1) { - if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { - success = false; - break; - } - } - } - if (!success) { - continue; - } - - { - u64 start_tick = armGetSystemTick(); - while (reg::Read(&this->i2c_registers->I2C_I2C_BUS_CLEAR_STATUS_0) & 1) { - if (armTicksToNs(armGetSystemTick() - start_tick) > 1'000'000) { - success = false; - break; - } - } - } - if (!success) { - continue; - } - } - } - - void BusAccessor::DisableClock() { - R_ABORT_UNLESS(pcv::SetClockEnabled(this->pcv_module, false)); - } - - void BusAccessor::SetPacketMode() { - /* Set PACKET_MODE_EN, MSTR_CONFIG_LOAD */ - reg::SetBits(&this->i2c_registers->I2C_I2C_CNFG_0, 0x400); - reg::SetBits(&this->i2c_registers->I2C_I2C_CONFIG_LOAD_0, 0x1); - - /* Set TX_FIFO_TRIGGER, RX_FIFO_TRIGGER */ - reg::Write(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFC); - } - - Result BusAccessor::FlushFifos() { - reg::Write(&this->i2c_registers->I2C_FIFO_CONTROL_0, 0xFF); - - /* Wait for flush to finish, check every ms for 5 ms. */ - for (size_t i = 0; i < 5; i++) { - const bool flush_done = (reg::Read(&this->i2c_registers->I2C_FIFO_CONTROL_0) & 3) == 0; - R_SUCCEED_IF(flush_done); - svcSleepThread(1'000'000ul); - } - - return i2c::ResultBusBusy(); - } - - Result BusAccessor::GetTransactionResult() const { - const u32 packet_status = reg::Read(&this->i2c_registers->I2C_PACKET_TRANSFER_STATUS_0); - const u32 interrupt_status = reg::Read(&this->i2c_registers->I2C_INTERRUPT_STATUS_REGISTER_0); - - /* Check for no ack. */ - R_UNLESS(!(packet_status & 0xC), i2c::ResultNoAck()); - R_UNLESS(!(interrupt_status & 0x8), i2c::ResultNoAck()); - - /* Check for arb lost. */ - { - auto bus_guard = SCOPE_GUARD { this->ClearBus(); }; - R_UNLESS(!(packet_status & 0x2), i2c::ResultBusBusy()); - R_UNLESS(!(interrupt_status & 0x4), i2c::ResultBusBusy()); - bus_guard.Cancel(); - } - - return ResultSuccess(); - } - - void BusAccessor::HandleTransactionResult(Result result) { - R_TRY_CATCH(result) { - R_CATCH(i2c::ResultNoAck, i2c::ResultBusBusy) { - this->ResetController(); - this->SetClock(this->speed_mode); - this->SetPacketMode(); - this->FlushFifos(); - } - } R_END_TRY_CATCH_WITH_ABORT_UNLESS; - } - - Result BusAccessor::GetAndHandleTransactionResult() { - const auto transaction_result = this->GetTransactionResult(); - R_SUCCEED_IF(R_SUCCEEDED(transaction_result)); - - this->HandleTransactionResult(transaction_result); - this->ClearInterruptMask(); - os::ClearInterruptEvent(std::addressof(this->interrupt_event)); - return transaction_result; - } - - void BusAccessor::WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes) { - this->FlushFifos(); - - reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, 0x10); - reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, static_cast(num_bytes - 1) & 0xFFF); - - const u32 slave_addr_val = ((transfer_mode == TransferMode::Receive) & 1) | ((slave_address & 0x7F) << 1); - u32 hdr_val = 0; - hdr_val |= ((this->speed_mode == SpeedMode::HighSpeed) & 1) << 22; - hdr_val |= ((transfer_mode == TransferMode::Receive) & 1) << 19; - hdr_val |= ((addressing_mode != AddressingMode::SevenBit) & 1) << 18; - hdr_val |= (1 << 17); - hdr_val |= (((option & I2cTransactionOption_Stop) == 0) & 1) << 16; - hdr_val |= slave_addr_val; - - reg::Write(&this->i2c_registers->I2C_I2C_TX_PACKET_FIFO_0, hdr_val); - } - -} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp deleted file mode 100644 index 8fa682627..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_bus_accessor.hpp +++ /dev/null @@ -1,81 +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 . - */ -#pragma once -#include "i2c_driver_types.hpp" -#include "i2c_registers.hpp" - -namespace ams::i2c::driver::impl { - - class BusAccessor { - private: - enum class TransferMode { - Send = 0, - Receive = 1, - }; - static constexpr TimeSpan InterruptTimeout = TimeSpan::FromMilliSeconds(100); - private: - os::InterruptEventType interrupt_event; - os::Mutex open_mutex; - os::Mutex register_mutex; - Registers *i2c_registers = nullptr; - ClkRstRegisters clkrst_registers; - SpeedMode speed_mode = SpeedMode::Fast; - size_t open_sessions = 0; - Bus bus = Bus::I2C1; - PcvModule pcv_module = PcvModule_I2C1; - bool suspended = false; - public: - BusAccessor() : open_mutex(false), register_mutex(false) { /* ... */ } - private: - inline void ClearInterruptMask() const { - reg::Write(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0, 0); - reg::Read(&i2c_registers->I2C_INTERRUPT_MASK_REGISTER_0); - } - - void SetBus(Bus bus); - void CreateInterruptEvent(Bus bus); - void SetClock(SpeedMode speed_mode); - - void ResetController() const; - void ClearBus() const; - void DisableClock(); - void SetPacketMode(); - Result FlushFifos(); - - Result GetTransactionResult() const; - void HandleTransactionResult(Result result); - Result GetAndHandleTransactionResult(); - - void WriteTransferHeader(TransferMode transfer_mode, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address, size_t num_bytes); - public: - void Open(Bus bus, SpeedMode speed_mode); - void Close(); - void Suspend(); - void Resume(); - void DoInitialConfig(); - - size_t GetOpenSessions() const; - bool GetBusy() const; - - void OnStartTransaction() const; - Result StartTransaction(Command command, AddressingMode addressing_mode, u32 slave_address); - Result Send(const u8 *data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address); - Result Receive(u8 *out_data, size_t num_bytes, I2cTransactionOption option, AddressingMode addressing_mode, u32 slave_address); - void OnStopTransaction() const; - }; - - -} \ No newline at end of file diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp deleted file mode 100644 index d6b3f1a5f..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_device_config.cpp +++ /dev/null @@ -1,122 +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 . - */ -#include -#include "i2c_driver_types.hpp" - -namespace ams::i2c::driver::impl { - - namespace { - - struct DeviceConfig { - I2cDevice device; - Bus bus; - u32 slave_address; - AddressingMode addressing_mode; - SpeedMode speed_mode; - u32 max_retries; - u64 retry_wait_time; - }; - - constexpr DeviceConfig g_device_configs[I2cDevice_Count] = { - {I2cDevice_DebugPad, Bus::I2C1, 0x52, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, - {I2cDevice_TouchPanel, Bus::I2C3, 0x49, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0}, - {I2cDevice_Tmp451, Bus::I2C1, 0x4c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, - {I2cDevice_Nct72, Bus::I2C1, 0x4c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, - {I2cDevice_Alc5639, Bus::I2C1, 0x1c, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, - {I2cDevice_Max77620Rtc, Bus::I2C5, 0x68, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Max77620Pmic, Bus::I2C5, 0x3c, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Max77621Cpu, Bus::I2C5, 0x1b, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Max77621Gpu, Bus::I2C5, 0x1c, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Bq24193, Bus::I2C1, 0x6b, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000}, - {I2cDevice_Max17050, Bus::I2C1, 0x36, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000}, - {I2cDevice_Bm92t30mwv, Bus::I2C1, 0x18, AddressingMode::SevenBit, SpeedMode::Normal, 3, 5'000'000}, - {I2cDevice_Ina226Vdd15v0Hb, Bus::I2C2, 0x40, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysCpuDs, Bus::I2C2, 0x41, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysGpuDs, Bus::I2C2, 0x44, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysDdrDs, Bus::I2C2, 0x45, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysAp, Bus::I2C2, 0x46, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysBlDs, Bus::I2C2, 0x47, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Bh1730, Bus::I2C2, 0x29, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226VsysCore, Bus::I2C2, 0x48, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226Soc1V8, Bus::I2C2, 0x49, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226Lpddr1V8, Bus::I2C2, 0x4a, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226Reg1V32, Bus::I2C2, 0x4b, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_Ina226Vdd3V3Sys, Bus::I2C2, 0x4d, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - {I2cDevice_HdmiDdc, Bus::I2C4, 0x50, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, - {I2cDevice_HdmiScdc, Bus::I2C4, 0x54, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, - {I2cDevice_HdmiHdcp, Bus::I2C4, 0x3a, AddressingMode::SevenBit, SpeedMode::Normal, 0, 0}, - {I2cDevice_Fan53528, Bus::I2C5, 0xa4, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0}, - {I2cDevice_Max77812_3, Bus::I2C5, 0x31, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0}, - {I2cDevice_Max77812_2, Bus::I2C5, 0x33, AddressingMode::SevenBit, SpeedMode::Fast, 0, 0}, - {I2cDevice_Ina226VddDdr0V6, Bus::I2C2, 0x4e, AddressingMode::SevenBit, SpeedMode::Fast, 3, 5'000'000}, - }; - - constexpr size_t NumDeviceConfigs = util::size(g_device_configs); - - constexpr size_t DeviceInvalidIndex = static_cast(-1); - - size_t GetDeviceIndex(I2cDevice dev) { - for (size_t i = 0; i < NumDeviceConfigs; i++) { - if (g_device_configs[i].device == dev) { - return i; - } - } - return DeviceInvalidIndex; - } - - } - - bool IsDeviceSupported(I2cDevice dev) { - return GetDeviceIndex(dev) != DeviceInvalidIndex; - } - - Bus GetDeviceBus(I2cDevice dev) { - const size_t dev_idx = GetDeviceIndex(dev); - AMS_ABORT_UNLESS(dev_idx != DeviceInvalidIndex); - return g_device_configs[dev_idx].bus; - } - - u32 GetDeviceSlaveAddress(I2cDevice dev) { - const size_t dev_idx = GetDeviceIndex(dev); - AMS_ABORT_UNLESS(dev_idx != DeviceInvalidIndex); - return g_device_configs[dev_idx].slave_address; - } - - AddressingMode GetDeviceAddressingMode(I2cDevice dev) { - const size_t dev_idx = GetDeviceIndex(dev); - AMS_ABORT_UNLESS(dev_idx != DeviceInvalidIndex); - return g_device_configs[dev_idx].addressing_mode; - } - - SpeedMode GetDeviceSpeedMode(I2cDevice dev) { - const size_t dev_idx = GetDeviceIndex(dev); - AMS_ABORT_UNLESS(dev_idx != DeviceInvalidIndex); - return g_device_configs[dev_idx].speed_mode; - } - - u32 GetDeviceMaxRetries(I2cDevice dev) { - const size_t dev_idx = GetDeviceIndex(dev); - AMS_ABORT_UNLESS(dev_idx != DeviceInvalidIndex); - return g_device_configs[dev_idx].max_retries; - } - - u64 GetDeviceRetryWaitTime(I2cDevice dev) { - const size_t dev_idx = GetDeviceIndex(dev); - AMS_ABORT_UNLESS(dev_idx != DeviceInvalidIndex); - return g_device_configs[dev_idx].retry_wait_time; - } - -} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_driver_types.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_driver_types.hpp deleted file mode 100644 index 43d59cb20..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_driver_types.hpp +++ /dev/null @@ -1,91 +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 . - */ -#pragma once -#include "../../i2c_types.hpp" - -namespace ams::i2c::driver::impl { - - enum class Command { - Send = 0, - Receive = 1, - }; - - enum class Bus { - I2C1 = 0, - I2C2 = 1, - I2C3 = 2, - I2C4 = 3, - I2C5 = 4, - I2C6 = 5, - Count, - }; - - /* Bus helpers. */ - constexpr inline size_t ConvertToIndex(Bus bus) { - return static_cast(bus); - } - - constexpr inline Bus ConvertFromIndex(size_t idx) { - AMS_ABORT_UNLESS(idx < static_cast(Bus::Count)); - return static_cast(idx); - } - - constexpr inline PcvModule ConvertToPcvModule(Bus bus) { - switch (bus) { - case Bus::I2C1: - return PcvModule_I2C1; - case Bus::I2C2: - return PcvModule_I2C2; - case Bus::I2C3: - return PcvModule_I2C3; - case Bus::I2C4: - return PcvModule_I2C4; - case Bus::I2C5: - return PcvModule_I2C5; - case Bus::I2C6: - return PcvModule_I2C6; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - constexpr inline Bus ConvertFromPcvModule(PcvModule module) { - switch (module) { - case PcvModule_I2C1: - return Bus::I2C1; - case PcvModule_I2C2: - return Bus::I2C2; - case PcvModule_I2C3: - return Bus::I2C3; - case PcvModule_I2C4: - return Bus::I2C4; - case PcvModule_I2C5: - return Bus::I2C5; - case PcvModule_I2C6: - return Bus::I2C6; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - } - - /* Global type functions. */ - bool IsDeviceSupported(I2cDevice dev); - Bus GetDeviceBus(I2cDevice dev); - u32 GetDeviceSlaveAddress(I2cDevice dev); - AddressingMode GetDeviceAddressingMode(I2cDevice dev); - SpeedMode GetDeviceSpeedMode(I2cDevice dev); - u32 GetDeviceMaxRetries(I2cDevice dev); - u64 GetDeviceRetryWaitTime(I2cDevice dev); - -} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp deleted file mode 100644 index 4af551d41..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_pcv.hpp +++ /dev/null @@ -1,31 +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 . - */ -#pragma once -#include - -/* This forward declares the functionality from pcv that i2c::driver uses. */ -/* This allows for overriding at compile-time (e.g., for boot sysmodule). */ -namespace ams::pcv { - - void Initialize(); - void Finalize(); - Result SetClockRate(PcvModule module, u32 hz); - Result SetClockEnabled(PcvModule module, bool enabled); - Result SetVoltageEnabled(u32 domain, bool enabled); - Result SetVoltageValue(u32 domain, u32 voltage); - Result SetReset(PcvModule module, bool reset); - -} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp deleted file mode 100644 index 4fd094a3f..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_registers.hpp +++ /dev/null @@ -1,104 +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 . - */ -#pragma once -#include "i2c_driver_types.hpp" - -namespace ams::i2c::driver::impl { - - struct Registers { - volatile u32 I2C_I2C_CNFG_0; - volatile u32 I2C_I2C_CMD_ADDR0_0; - volatile u32 I2C_I2C_CMD_ADDR1_0; - volatile u32 I2C_I2C_CMD_DATA1_0; - volatile u32 I2C_I2C_CMD_DATA2_0; - volatile u32 _0x14; - volatile u32 _0x18; - volatile u32 I2C_I2C_STATUS_0; - volatile u32 I2C_I2C_SL_CNFG_0; - volatile u32 I2C_I2C_SL_RCVD_0; - volatile u32 I2C_I2C_SL_STATUS_0; - volatile u32 I2C_I2C_SL_ADDR1_0; - volatile u32 I2C_I2C_SL_ADDR2_0; - volatile u32 I2C_I2C_TLOW_SEXT_0; - volatile u32 _0x38; - volatile u32 I2C_I2C_SL_DELAY_COUNT_0; - volatile u32 I2C_I2C_SL_INT_MASK_0; - volatile u32 I2C_I2C_SL_INT_SOURCE_0; - volatile u32 I2C_I2C_SL_INT_SET_0; - volatile u32 _0x4C; - volatile u32 I2C_I2C_TX_PACKET_FIFO_0; - volatile u32 I2C_I2C_RX_FIFO_0; - volatile u32 I2C_PACKET_TRANSFER_STATUS_0; - volatile u32 I2C_FIFO_CONTROL_0; - volatile u32 I2C_FIFO_STATUS_0; - volatile u32 I2C_INTERRUPT_MASK_REGISTER_0; - volatile u32 I2C_INTERRUPT_STATUS_REGISTER_0; - volatile u32 I2C_I2C_CLK_DIVISOR_REGISTER_0; - volatile u32 I2C_I2C_INTERRUPT_SOURCE_REGISTER_0; - volatile u32 I2C_I2C_INTERRUPT_SET_REGISTER_0; - volatile u32 I2C_I2C_SLV_TX_PACKET_FIFO_0; - volatile u32 I2C_I2C_SLV_RX_FIFO_0; - volatile u32 I2C_I2C_SLV_PACKET_STATUS_0; - volatile u32 I2C_I2C_BUS_CLEAR_CONFIG_0; - volatile u32 I2C_I2C_BUS_CLEAR_STATUS_0; - volatile u32 I2C_I2C_CONFIG_LOAD_0; - volatile u32 _0x90; - volatile u32 I2C_I2C_INTERFACE_TIMING_0_0; - volatile u32 I2C_I2C_INTERFACE_TIMING_1_0; - volatile u32 I2C_I2C_HS_INTERFACE_TIMING_0_0; - volatile u32 I2C_I2C_HS_INTERFACE_TIMING_1_0; - }; - - struct ClkRstRegisters { - public: - uintptr_t clk_src_reg; - uintptr_t clk_en_reg; - uintptr_t rst_reg; - u32 mask; - public: - void SetBus(Bus bus) { - static constexpr uintptr_t s_clk_src_offsets[ConvertToIndex(Bus::Count)] = { - 0x124, 0x198, 0x1b8, 0x3c4, 0x128, 0x65c - }; - static constexpr uintptr_t s_clk_en_offsets[ConvertToIndex(Bus::Count)] = { - 0x010, 0x014, 0x018, 0x360, 0x014, 0x280 - }; - static constexpr uintptr_t s_rst_offsets[ConvertToIndex(Bus::Count)] = { - 0x004, 0x008, 0x00c, 0x358, 0x008, 0x28c - }; - static constexpr size_t s_bit_shifts[ConvertToIndex(Bus::Count)] = { - 12, 22, 3, 7, 15, 6 - }; - - const uintptr_t registers = dd::GetIoMapping(0x60006000ul, os::MemoryPageSize); - const size_t idx = ConvertToIndex(bus); - this->clk_src_reg = registers + s_clk_src_offsets[idx]; - this->clk_en_reg = registers + s_clk_en_offsets[idx]; - this->rst_reg = registers + s_rst_offsets[idx]; - this->mask = (1u << s_bit_shifts[idx]); - } - }; - - inline Registers *GetRegisters(Bus bus) { - static constexpr uintptr_t s_offsets[ConvertToIndex(Bus::Count)] = { - 0x0000, 0x0400, 0x0500, 0x0700, 0x1000, 0x1100 - }; - - const uintptr_t registers = dd::GetIoMapping(0x7000c000ul, 2 * os::MemoryPageSize) + s_offsets[ConvertToIndex(bus)]; - return reinterpret_cast(registers); - } - -} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp deleted file mode 100644 index 627ab3c26..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.cpp +++ /dev/null @@ -1,175 +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 . - */ -#include -#include "i2c_pcv.hpp" -#include "i2c_resource_manager.hpp" - -namespace ams::i2c::driver::impl { - - void ResourceManager::Initialize() { - std::scoped_lock lk(this->initialize_mutex); - this->ref_cnt++; - } - - void ResourceManager::Finalize() { - std::scoped_lock lk(this->initialize_mutex); - AMS_ABORT_UNLESS(this->ref_cnt > 0); - this->ref_cnt--; - if (this->ref_cnt > 0) { - return; - } - - { - std::scoped_lock sess_lk(this->session_open_mutex); - for (size_t i = 0; i < MaxDriverSessions; i++) { - this->sessions[i].Close(); - } - } - } - - size_t ResourceManager::GetFreeSessionId() const { - for (size_t i = 0; i < MaxDriverSessions; i++) { - if (!this->sessions[i].IsOpen()) { - return i; - } - } - - return InvalidSessionId; - } - - void ResourceManager::OpenSession(driver::Session *out_session, Bus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time) { - bool need_enable_ldo6 = false; - size_t session_id = InvalidSessionId; - /* Get, open session. */ - { - std::scoped_lock lk(this->session_open_mutex); - AMS_ABORT_UNLESS(out_session != nullptr); - AMS_ABORT_UNLESS(bus < Bus::Count); - - session_id = GetFreeSessionId(); - AMS_ABORT_UNLESS(session_id != InvalidSessionId); - - - if ((bus == Bus::I2C2 || bus == Bus::I2C3) && (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() == 0 && this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() == 0)) { - need_enable_ldo6 = true; - } - - out_session->session_id = session_id; - out_session->bus_idx = ConvertToIndex(bus); - this->sessions[session_id].Open(bus, slave_address, addressing_mode, speed_mode, &this->bus_accessors[ConvertToIndex(bus)], max_retries, retry_wait_time); - } - - this->sessions[session_id].Start(); - if (need_enable_ldo6) { - pcv::Initialize(); - R_ABORT_UNLESS(pcv::SetVoltageValue(10, 2'900'000)); - R_ABORT_UNLESS(pcv::SetVoltageEnabled(10, true)); - pcv::Finalize(); - svcSleepThread(560'000ul); - } - } - - void ResourceManager::CloseSession(const driver::Session &session) { - bool need_disable_ldo6 = false; - /* Get, open session. */ - { - std::scoped_lock lk(this->session_open_mutex); - AMS_ABORT_UNLESS(this->sessions[session.session_id].IsOpen()); - - this->sessions[session.session_id].Close(); - - if ((ConvertFromIndex(session.bus_idx) == Bus::I2C2 || ConvertFromIndex(session.bus_idx) == Bus::I2C3) && - (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() == 0 && this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() == 0)) { - need_disable_ldo6 = true; - } - } - - if (need_disable_ldo6) { - pcv::Initialize(); - R_ABORT_UNLESS(pcv::SetVoltageEnabled(10, false)); - pcv::Finalize(); - } - - } - - void ResourceManager::SuspendBuses() { - AMS_ABORT_UNLESS(this->ref_cnt > 0); - - if (!this->suspended) { - { - std::scoped_lock lk(this->session_open_mutex); - this->suspended = true; - for (size_t i = 0; i < ConvertToIndex(Bus::Count); i++) { - if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) { - this->bus_accessors[i].Suspend(); - } - } - } - pcv::Initialize(); - R_ABORT_UNLESS(pcv::SetVoltageEnabled(10, false)); - pcv::Finalize(); - } - } - - void ResourceManager::ResumeBuses() { - AMS_ABORT_UNLESS(this->ref_cnt > 0); - - if (this->suspended) { - if (this->bus_accessors[ConvertToIndex(Bus::I2C2)].GetOpenSessions() > 0 || this->bus_accessors[ConvertToIndex(Bus::I2C3)].GetOpenSessions() > 0) { - pcv::Initialize(); - R_ABORT_UNLESS(pcv::SetVoltageValue(10, 2'900'000)); - R_ABORT_UNLESS(pcv::SetVoltageEnabled(10, true)); - pcv::Finalize(); - svcSleepThread(1'560'000ul); - } - { - std::scoped_lock lk(this->session_open_mutex); - for (size_t i = 0; i < ConvertToIndex(Bus::Count); i++) { - if (i != PowerBusId && this->bus_accessors[i].GetOpenSessions() > 0) { - this->bus_accessors[i].Resume(); - } - } - } - this->suspended = false; - } - } - - void ResourceManager::SuspendPowerBus() { - AMS_ABORT_UNLESS(this->ref_cnt > 0); - std::scoped_lock lk(this->session_open_mutex); - - if (!this->power_bus_suspended) { - this->power_bus_suspended = true; - if (this->bus_accessors[PowerBusId].GetOpenSessions() > 0) { - this->bus_accessors[PowerBusId].Suspend(); - } - } - } - - void ResourceManager::ResumePowerBus() { - AMS_ABORT_UNLESS(this->ref_cnt > 0); - std::scoped_lock lk(this->session_open_mutex); - - if (this->power_bus_suspended) { - if (this->bus_accessors[PowerBusId].GetOpenSessions() > 0) { - this->bus_accessors[PowerBusId].Resume(); - } - this->power_bus_suspended = false; - } - } - -} - diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp deleted file mode 100644 index 27465a2fd..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_resource_manager.hpp +++ /dev/null @@ -1,82 +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 . - */ -#pragma once -#include "../i2c_api.hpp" -#include "i2c_driver_types.hpp" -#include "i2c_bus_accessor.hpp" -#include "i2c_session.hpp" - -namespace ams::i2c::driver::impl { - - class ResourceManager { - public: - static constexpr size_t MaxDriverSessions = 40; - static constexpr size_t PowerBusId = ConvertToIndex(Bus::I2C5); - static constexpr size_t InvalidSessionId = static_cast(-1); - private: - os::Mutex initialize_mutex; - os::Mutex session_open_mutex; - size_t ref_cnt = 0; - bool suspended = false; - bool power_bus_suspended = false; - Session sessions[MaxDriverSessions]; - BusAccessor bus_accessors[ConvertToIndex(Bus::Count)]; - TYPED_STORAGE(os::Mutex) transaction_mutexes[ConvertToIndex(Bus::Count)]; - public: - ResourceManager() : initialize_mutex(false), session_open_mutex(false) { - for (size_t i = 0; i < util::size(this->transaction_mutexes); i++) { - new (GetPointer(this->transaction_mutexes[i])) os::Mutex(false); - } - } - - ~ResourceManager() { - for (size_t i = 0; i < util::size(this->transaction_mutexes); i++) { - GetReference(this->transaction_mutexes[i]).~Mutex(); - } - } - private: - size_t GetFreeSessionId() const; - public: - /* N uses a singleton here, we'll oblige. */ - static ResourceManager &GetInstance() { - static ResourceManager s_instance; - return s_instance; - } - - bool IsInitialized() const { - return this->ref_cnt > 0; - } - - Session& GetSession(size_t id) { - return this->sessions[id]; - } - - os::Mutex& GetTransactionMutex(Bus bus) { - return GetReference(this->transaction_mutexes[ConvertToIndex(bus)]); - } - - void Initialize(); - void Finalize(); - - void OpenSession(driver::Session *out_session, Bus bus, u32 slave_address, AddressingMode addressing_mode, SpeedMode speed_mode, u32 max_retries, u64 retry_wait_time); - void CloseSession(const driver::Session &session); - void SuspendBuses(); - void ResumeBuses(); - void SuspendPowerBus(); - void ResumePowerBus(); - }; - -} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp b/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp deleted file mode 100644 index 8e982ec6e..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_session.cpp +++ /dev/null @@ -1,96 +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 . - */ -#include -#include "i2c_session.hpp" - -namespace ams::i2c::driver::impl { - - void Session::Open(Bus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, BusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time) { - std::scoped_lock lk(this->bus_accessor_mutex); - if (!this->open) { - this->bus_accessor = bus_accessor; - this->bus = bus; - this->slave_address = slave_address; - this->addressing_mode = addr_mode; - this->max_retries = max_retries; - this->retry_wait_time = retry_wait_time; - this->bus_accessor->Open(this->bus, speed_mode); - this->open = true; - } - } - - void Session::Start() { - std::scoped_lock lk(this->bus_accessor_mutex); - if (this->open) { - if (this->bus_accessor->GetOpenSessions() == 1) { - this->bus_accessor->DoInitialConfig(); - } - } - } - - void Session::Close() { - std::scoped_lock lk(this->bus_accessor_mutex); - if (this->open) { - this->bus_accessor->Close(); - this->bus_accessor = nullptr; - this->open = false; - } - } - - bool Session::IsOpen() const { - return this->open; - } - - Result Session::DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command) { - std::scoped_lock lk(this->bus_accessor_mutex); - - R_UNLESS(!this->bus_accessor->GetBusy(), i2c::ResultBusBusy()); - - this->bus_accessor->OnStartTransaction(); - ON_SCOPE_EXIT { this->bus_accessor->OnStopTransaction(); }; - - R_TRY(this->bus_accessor->StartTransaction(command, this->addressing_mode, this->slave_address)); - - switch (command) { - case Command::Send: - R_TRY(this->bus_accessor->Send(reinterpret_cast(src), num_bytes, option, this->addressing_mode, this->slave_address)); - break; - case Command::Receive: - R_TRY(this->bus_accessor->Receive(reinterpret_cast(dst), num_bytes, option, this->addressing_mode, this->slave_address)); - break; - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - return ResultSuccess(); - } - - Result Session::DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command) { - size_t i = 0; - while (true) { - R_TRY_CATCH(this->DoTransaction(dst, src, num_bytes, option, command)) { - R_CATCH(i2c::ResultTimedOut) { - if ((++i) <= this->max_retries) { - svcSleepThread(this->retry_wait_time); - continue; - } - return i2c::ResultBusBusy(); - } - } R_END_TRY_CATCH; - return ResultSuccess(); - } - } - -} diff --git a/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp b/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp deleted file mode 100644 index 010bd41c6..000000000 --- a/stratosphere/boot/source/i2c/driver/impl/i2c_session.hpp +++ /dev/null @@ -1,47 +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 . - */ -#pragma once -#include "i2c_driver_types.hpp" -#include "i2c_bus_accessor.hpp" - -namespace ams::i2c::driver::impl { - - class Session { - private: - os::Mutex bus_accessor_mutex; - BusAccessor *bus_accessor = nullptr; - Bus bus = Bus::I2C1; - u32 slave_address = 0; - AddressingMode addressing_mode = AddressingMode::SevenBit; - u32 max_retries = 0; - u64 retry_wait_time = 0; - bool open = false; - public: - Session() : bus_accessor_mutex(false) { /* ... */ } - public: - void Open(Bus bus, u32 slave_address, AddressingMode addr_mode, SpeedMode speed_mode, BusAccessor *bus_accessor, u32 max_retries, u64 retry_wait_time); - void Start(); - void Close(); - - bool IsOpen() const; - - Result DoTransaction(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command); - Result DoTransactionWithRetry(void *dst, const void *src, size_t num_bytes, I2cTransactionOption option, Command command); - }; - -} - - diff --git a/stratosphere/boot/source/i2c/i2c_command_list.cpp b/stratosphere/boot/source/i2c/i2c_command_list.cpp deleted file mode 100644 index 9b44658b4..000000000 --- a/stratosphere/boot/source/i2c/i2c_command_list.cpp +++ /dev/null @@ -1,78 +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 . - */ -#include -#include "i2c_types.hpp" -#include "i2c_command_list.hpp" - -namespace ams::i2c { - - namespace { - - /* Useful definitions. */ - constexpr size_t SendCommandSize = 2; - constexpr size_t ReceiveCommandSize = 2; - constexpr size_t SleepCommandSize = 2; - - } - - Result CommandListFormatter::CanEnqueue(size_t size) const { - R_UNLESS(this->cmd_list_size - this->cur_index >= size, ResultFullCommandList()); - return ResultSuccess(); - } - - Result CommandListFormatter::EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size) { - R_TRY(this->CanEnqueue(SendCommandSize + size)); - - this->cmd_list[this->cur_index] = static_cast(Command::Send); - this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6; - this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7; - this->cur_index++; - - this->cmd_list[this->cur_index++] = size; - - const u8 *src_u8 = reinterpret_cast(src); - for (size_t i = 0; i < size; i++) { - this->cmd_list[this->cur_index++] = src_u8[i]; - } - return ResultSuccess(); - } - - Result CommandListFormatter::EnqueueReceiveCommand(I2cTransactionOption option, size_t size) { - R_TRY(this->CanEnqueue(ReceiveCommandSize)); - - this->cmd_list[this->cur_index] = static_cast(Command::Receive); - this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Start) != 0) << 6; - this->cmd_list[this->cur_index] |= ((option & I2cTransactionOption_Stop) != 0) << 7; - this->cur_index++; - - this->cmd_list[this->cur_index++] = size; - return ResultSuccess(); - } - - Result CommandListFormatter::EnqueueSleepCommand(size_t us) { - R_TRY(this->CanEnqueue(SleepCommandSize)); - - this->cmd_list[this->cur_index] = static_cast(Command::SubCommand); - this->cmd_list[this->cur_index] |= static_cast(SubCommand::Sleep) << 2; - this->cur_index++; - - this->cmd_list[this->cur_index++] = us; - return ResultSuccess(); - } - -} - - diff --git a/stratosphere/boot/source/i2c/i2c_command_list.hpp b/stratosphere/boot/source/i2c/i2c_command_list.hpp deleted file mode 100644 index cf904b5b7..000000000 --- a/stratosphere/boot/source/i2c/i2c_command_list.hpp +++ /dev/null @@ -1,60 +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 . - */ -#pragma once -#include "i2c_types.hpp" - -namespace ams::i2c { - - enum class Command { - Send = 0, - Receive = 1, - SubCommand = 2, - Count, - }; - - enum class SubCommand { - Sleep = 0, - Count, - }; - - class CommandListFormatter { - public: - static constexpr size_t MaxCommandListSize = 0x100; - private: - u8 *cmd_list = nullptr; - size_t cmd_list_size = 0; - size_t cur_index = 0; - public: - CommandListFormatter(void *cmd_list, size_t cmd_list_size) : cmd_list(static_cast(cmd_list)), cmd_list_size(cmd_list_size) { - AMS_ABORT_UNLESS(cmd_list_size <= MaxCommandListSize); - } - ~CommandListFormatter() { - this->cmd_list = nullptr; - } - - private: - Result CanEnqueue(size_t size) const; - public: - size_t GetCurrentSize() const { - return this->cur_index; - } - - Result EnqueueSendCommand(I2cTransactionOption option, const void *src, size_t size); - Result EnqueueReceiveCommand(I2cTransactionOption option, size_t size); - Result EnqueueSleepCommand(size_t us); - }; - -} diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp b/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp deleted file mode 100644 index 521dbe835..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration.cpp +++ /dev/null @@ -1,135 +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 . - */ -#include -#include "pinmux_initial_configuration.hpp" -#include "pinmux_utils.hpp" - -namespace ams::pinmux { - - namespace { - - struct InitialConfig { - u32 name; - u32 val; - u32 mask; - }; - - /* Include all initial configuration definitions. */ -#include "pinmux_initial_configuration_icosa.inc" -#include "pinmux_initial_configuration_copper.inc" -#include "pinmux_initial_configuration_hoag.inc" -#include "pinmux_initial_configuration_iowa.inc" -#include "pinmux_initial_configuration_calcio.inc" - -#include "pinmux_initial_drive_pad_configuration.inc" -#include "pinmux_initial_drive_pad_configuration_hoag.inc" - - - /* Configuration helpers. */ - void ConfigureInitialPads() { - const InitialConfig *configs = nullptr; - size_t num_configs = 0; - const auto hw_type = spl::GetHardwareType(); - - switch (hw_type) { - case spl::HardwareType::Icosa: - configs = InitialConfigsIcosa; - num_configs = NumInitialConfigsIcosa; - break; - case spl::HardwareType::Copper: - configs = InitialConfigsCopper; - num_configs = NumInitialConfigsCopper; - break; - case spl::HardwareType::Hoag: - configs = InitialConfigsHoag; - num_configs = NumInitialConfigsHoag; - break; - case spl::HardwareType::Iowa: - configs = InitialConfigsIowa; - num_configs = NumInitialConfigsIowa; - break; - case spl::HardwareType::Calcio: - configs = InitialConfigsCalcio; - num_configs = NumInitialConfigsCalcio; - break; - /* Unknown hardware type, we can't proceed. */ - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Ensure we found an appropriate config. */ - AMS_ABORT_UNLESS(configs != nullptr); - - for (size_t i = 0; i < num_configs; i++) { - UpdatePad(configs[i].name, configs[i].val, configs[i].mask); - } - - /* Extra configs for mariko only. */ - if (hw_type == spl::HardwareType::Hoag || hw_type == spl::HardwareType::Iowa || hw_type == spl::HardwareType::Calcio) { - static constexpr u32 ExtraMarikoPadNames[] = { - 0xAA, 0xAC, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9 - }; - for (size_t i = 0; i < util::size(ExtraMarikoPadNames); i++) { - UpdatePad(ExtraMarikoPadNames[i], 0x2000, 0x2000); - } - } - } - - void ConfigureInitialDrivePads() { - const InitialConfig *configs = nullptr; - size_t num_configs = 0; - const auto hw_type = spl::GetHardwareType(); - - switch (hw_type) { - case spl::HardwareType::Icosa: - case spl::HardwareType::Copper: - case spl::HardwareType::Iowa: - case spl::HardwareType::Calcio: - configs = InitialDrivePadConfigs; - num_configs = NumInitialDrivePadConfigs; - break; - case spl::HardwareType::Hoag: - configs = InitialDrivePadConfigsHoag; - num_configs = NumInitialDrivePadConfigsHoag; - break; - /* Unknown hardware type, we can't proceed. */ - AMS_UNREACHABLE_DEFAULT_CASE(); - } - - /* Ensure we found an appropriate config. */ - AMS_ABORT_UNLESS(configs != nullptr); - - for (size_t i = 0; i < num_configs; i++) { - UpdateDrivePad(configs[i].name, configs[i].val, configs[i].mask); - } - } - - } - - void SetInitialConfiguration() { - /* Update all parks. */ - UpdateAllParks(); - - /* Dummy read all drive pads. */ - DummyReadAllDrivePads(); - - /* Set initial pad configs. */ - ConfigureInitialPads(); - - /* Set initial drive pad configs. */ - ConfigureInitialDrivePads(); - } - -} diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_calcio.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_calcio.inc deleted file mode 100644 index 3dcb28611..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_calcio.inc +++ /dev/null @@ -1,194 +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 . - */ - -constexpr InitialConfig InitialConfigsCalcio[] = { - {0x0D, 0x20, 0x27F}, - {0x0C, 0x00, 0x27F}, - {0x10, 0x40, 0x27F}, - {0x0F, 0x00, 0x27F}, - {0x0E, 0x20, 0x27F}, - {0x00, 0x40, 0x7F}, - {0x01, 0x50, 0x7F}, - {0x05, 0x50, 0x7F}, - {0x04, 0x50, 0x7F}, - {0x03, 0x50, 0x7F}, - {0x02, 0x50, 0x7F}, - {0x5B, 0x00, 0x78}, - {0x80, 0x01, 0x7F}, - {0x34, 0x40, 0x27F}, - {0x35, 0x40, 0x27F}, - {0x55, 0x20, 0x78}, - {0x56, 0x01, 0x7F}, - {0x5C, 0x00, 0x78}, - {0x5A, 0x20, 0x78}, - {0x2C, 0x40, 0x27F}, - {0x2D, 0x40, 0x27F}, - {0x36, 0x00, 0x7F}, - {0x37, 0x30, 0x7F}, - {0x38, 0x00, 0x7F}, - {0x39, 0x28, 0x7F}, - {0x54, 0x00, 0x67}, - {0x42, 0x00, 0x7F}, - {0x43, 0x28, 0x7F}, - {0x44, 0x00, 0x7F}, - {0x45, 0x28, 0x7F}, - {0x4B, 0x28, 0x7F}, - {0x4C, 0x00, 0x7F}, - {0x4A, 0x00, 0x7F}, - {0x4D, 0x00, 0x7F}, - {0x63, 0x240, 0x27F}, - {0x26, 0x04, 0x67}, - {0x27, 0x04, 0x67}, - {0x28, 0x04, 0x67}, - {0x29, 0x04, 0x67}, - {0x2A, 0x04, 0x67}, - {0x78, 0x24, 0x7F}, - {0x88, 0x34, 0x7F}, - {0x89, 0x24, 0x7F}, - {0x8A, 0x34, 0x7F}, - {0x8B, 0x34, 0x7F}, - {0x8D, 0x34, 0x7F}, - {0x81, 0x04, 0x67}, - {0x9D, 0x34, 0x7F}, - {0x9F, 0x34, 0x7F}, - {0x92, 0x4C, 0x7F}, - {0x93, 0x4C, 0x7F}, - {0x94, 0x44, 0x7F}, - {0x96, 0x34, 0x7F}, - {0x98, 0x34, 0x7F}, - {0x12, 0x0C, 0x7F}, - {0x13, 0x34, 0x7F}, - {0x14, 0x0C, 0x7F}, - {0x6A, 0x04, 0x67}, - {0x6B, 0x04, 0x67}, - {0x6C, 0x2C, 0x7F}, - {0x6D, 0x04, 0x67}, - {0x6E, 0x04, 0x67}, - {0x6F, 0x24, 0x7F}, - {0x70, 0x04, 0x7F}, - {0x69, 0x0C, 0x7F}, - {0x64, 0x24, 0x27F}, - {0x5D, 0x05, 0x07}, - {0x5E, 0x05, 0x07}, - {0x5F, 0x05, 0x07}, - {0x60, 0x05, 0x07}, - {0x61, 0x05, 0x07}, - {0x47, 0x05, 0x07}, - {0x48, 0x05, 0x07}, - {0x46, 0x05, 0x07}, - {0x49, 0x05, 0x07}, - {0x17, 0x05, 0x07}, - {0x18, 0x05, 0x07}, - {0x19, 0x05, 0x07}, - {0x1A, 0x05, 0x07}, - {0x1B, 0x05, 0x07}, - {0x2B, 0x05, 0x07}, - {0x8F, 0x05, 0x07}, - {0x90, 0x05, 0x07}, - {0x30, 0x05, 0x07}, - {0x31, 0x05, 0x07}, - {0x32, 0x05, 0x07}, - {0x33, 0x05, 0x07}, - {0x52, 0x05, 0x07}, - {0x53, 0x05, 0x07}, - {0x75, 0x05, 0x07}, - {0x76, 0x05, 0x07}, - {0x77, 0x05, 0x07}, - {0x79, 0x05, 0x07}, - {0x7A, 0x05, 0x07}, - {0x11, 0x05, 0x07}, - {0x8E, 0x05, 0x07}, - {0xAA, 0x05, 0x07}, - {0xAB, 0x05, 0x07}, - {0xAC, 0x05, 0x07}, - {0xA2, 0x05, 0x07}, - {0xA3, 0x05, 0x07}, - {0xA4, 0x05, 0x07}, - {0xA5, 0x05, 0x07}, - {0xA6, 0x05, 0x07}, - {0xA7, 0x05, 0x07}, - {0xA8, 0x05, 0x07}, - {0xA9, 0x05, 0x07}, - {0xAD, 0x05, 0x07}, - {0xAE, 0x05, 0x07}, - {0x06, 0x05, 0x07}, - {0x07, 0x05, 0x07}, - {0x08, 0x05, 0x07}, - {0x09, 0x05, 0x07}, - {0x0A, 0x05, 0x07}, - {0x0B, 0x05, 0x07}, - {0x87, 0x05, 0x07}, - {0x86, 0x05, 0x07}, - {0x82, 0x05, 0x07}, - {0x83, 0x05, 0x07}, - {0x85, 0x05, 0x07}, - {0x84, 0x05, 0x07}, - {0x8C, 0x05, 0x07}, - {0x7B, 0x05, 0x07}, - {0x7C, 0x05, 0x07}, - {0x7D, 0x05, 0x07}, - {0x7E, 0x05, 0x07}, - {0x7F, 0x05, 0x07}, - {0x9C, 0x05, 0x07}, - {0x9E, 0x05, 0x07}, - {0xA0, 0x05, 0x07}, - {0xA1, 0x05, 0x07}, - {0x58, 0x00, 0x18}, - {0x59, 0x00, 0x18}, - {0x4F, 0x05, 0x07}, - {0x50, 0x05, 0x07}, - {0x4E, 0x05, 0x07}, - {0x51, 0x05, 0x07}, - {0x2E, 0x05, 0x07}, - {0x2F, 0x05, 0x07}, - {0x3A, 0x05, 0x07}, - {0x3B, 0x05, 0x07}, - {0x3C, 0x05, 0x07}, - {0x3D, 0x05, 0x07}, - {0x95, 0x05, 0x07}, - {0x97, 0x05, 0x07}, - {0x99, 0x05, 0x07}, - {0x9A, 0x05, 0x07}, - {0x9B, 0x05, 0x07}, - {0x15, 0x05, 0x07}, - {0x16, 0x05, 0x07}, - {0x1C, 0x05, 0x07}, - {0x1D, 0x05, 0x07}, - {0x1E, 0x05, 0x07}, - {0x1F, 0x05, 0x07}, - {0x3E, 0x05, 0x07}, - {0x3F, 0x05, 0x07}, - {0x40, 0x05, 0x07}, - {0x41, 0x05, 0x07}, - {0x91, 0x05, 0x07}, - {0x71, 0x05, 0x07}, - {0x72, 0x05, 0x07}, - {0x73, 0x05, 0x07}, - {0x74, 0x05, 0x07}, - {0x22, 0x05, 0x07}, - {0x23, 0x05, 0x07}, - {0x20, 0x05, 0x07}, - {0x21, 0x05, 0x07}, - {0x24, 0x05, 0x07}, - {0x25, 0x05, 0x07}, - {0x62, 0x05, 0x07}, - {0x65, 0x05, 0x07}, - {0x66, 0x05, 0x07}, - {0x67, 0x05, 0x07}, - {0x68, 0x05, 0x07}, -}; - -constexpr u32 NumInitialConfigsCalcio = util::size(InitialConfigsCalcio); diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_copper.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_copper.inc deleted file mode 100644 index a130e4d05..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_copper.inc +++ /dev/null @@ -1,181 +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 . - */ - -constexpr InitialConfig InitialConfigsCopper[] = { - {0x10, 0x20, 0x27F}, - {0x0F, 0x00, 0x267}, - {0x0E, 0x20, 0x27F}, - {0x5B, 0x00, 0x00}, - {0x80, 0x01, 0x7F}, - {0x34, 0x40, 0x267}, - {0x35, 0x40, 0x267}, - {0x55, 0x00, 0x18}, - {0x56, 0x01, 0x67}, - {0x5C, 0x00, 0x00}, - {0x59, 0x00, 0x00}, - {0x5A, 0x10, 0x18}, - {0x2C, 0x40, 0x267}, - {0x2D, 0x40, 0x267}, - {0x2E, 0x40, 0x267}, - {0x2F, 0x40, 0x267}, - {0x36, 0x00, 0x67}, - {0x37, 0x30, 0x7F}, - {0x38, 0x00, 0x67}, - {0x39, 0x28, 0x7F}, - {0x54, 0x00, 0x67}, - {0x9B, 0x30, 0x7F}, - {0x42, 0x00, 0x67}, - {0x43, 0x28, 0x7F}, - {0x44, 0x00, 0x67}, - {0x45, 0x28, 0x7F}, - {0x4B, 0x28, 0x7F}, - {0x4C, 0x00, 0x67}, - {0x4A, 0x00, 0x67}, - {0x4D, 0x00, 0x67}, - {0x64, 0x20, 0x27F}, - {0x63, 0x40, 0x267}, - {0x5E, 0x04, 0x67}, - {0x60, 0x04, 0x67}, - {0x17, 0x24, 0x7F}, - {0x18, 0x24, 0x7F}, - {0x27, 0x04, 0x67}, - {0x2A, 0x04, 0x67}, - {0x2B, 0x04, 0x67}, - {0x90, 0x24, 0x7F}, - {0x32, 0x24, 0x27F}, - {0x33, 0x34, 0x27F}, - {0x76, 0x04, 0x67}, - {0x79, 0x04, 0x67}, - {0x08, 0x24, 0x7F}, - {0x09, 0x24, 0x7F}, - {0x0A, 0x24, 0x7F}, - {0x0B, 0x24, 0x7F}, - {0x88, 0x34, 0x7F}, - {0x89, 0x24, 0x7F}, - {0x8A, 0x34, 0x7F}, - {0x8B, 0x34, 0x7F}, - {0x8D, 0x34, 0x7F}, - {0x81, 0x04, 0x67}, - {0x9D, 0x34, 0x7F}, - {0x9F, 0x34, 0x7F}, - {0xA1, 0x34, 0x7F}, - {0x92, 0x4C, 0x7F}, - {0x93, 0x4C, 0x7F}, - {0x94, 0x44, 0x7F}, - {0x96, 0x34, 0x7F}, - {0x98, 0x34, 0x7F}, - {0x99, 0x34, 0x7F}, - {0x12, 0x04, 0x7F}, - {0x13, 0x04, 0x67}, - {0x14, 0x04, 0x7F}, - {0x6A, 0x04, 0x67}, - {0x6B, 0x04, 0x67}, - {0x6C, 0x2C, 0x7F}, - {0x6D, 0x04, 0x67}, - {0x6E, 0x04, 0x67}, - {0x6F, 0x24, 0x7F}, - {0x70, 0x04, 0x7F}, - {0x73, 0x04, 0x67}, - {0x69, 0x24, 0x7F}, - {0x5D, 0x05, 0x07}, - {0x5F, 0x05, 0x07}, - {0x61, 0x05, 0x07}, - {0x47, 0x05, 0x07}, - {0x48, 0x05, 0x07}, - {0x46, 0x05, 0x07}, - {0x49, 0x05, 0x07}, - {0x19, 0x05, 0x07}, - {0x1A, 0x05, 0x07}, - {0x1B, 0x05, 0x07}, - {0x26, 0x05, 0x07}, - {0x28, 0x05, 0x07}, - {0x29, 0x05, 0x07}, - {0x8F, 0x05, 0x07}, - {0x30, 0x05, 0x07}, - {0x31, 0x05, 0x07}, - {0x52, 0x05, 0x07}, - {0x53, 0x05, 0x07}, - {0x75, 0x05, 0x07}, - {0x77, 0x05, 0x07}, - {0x78, 0x05, 0x07}, - {0x7A, 0x05, 0x07}, - {0x0D, 0x05, 0x07}, - {0x0C, 0x05, 0x07}, - {0x11, 0x05, 0x07}, - {0x8E, 0x05, 0x07}, - {0x00, 0x05, 0x07}, - {0x01, 0x05, 0x07}, - {0x05, 0x05, 0x07}, - {0x04, 0x05, 0x07}, - {0x03, 0x05, 0x07}, - {0x02, 0x05, 0x07}, - {0x06, 0x05, 0x07}, - {0x07, 0x05, 0x07}, - {0x87, 0x05, 0x07}, - {0x86, 0x05, 0x07}, - {0x82, 0x05, 0x07}, - {0x83, 0x05, 0x07}, - {0x85, 0x05, 0x07}, - {0x84, 0x05, 0x07}, - {0x8C, 0x05, 0x07}, - {0x7B, 0x05, 0x07}, - {0x7C, 0x05, 0x07}, - {0x7D, 0x05, 0x07}, - {0x7E, 0x05, 0x07}, - {0x7F, 0x05, 0x07}, - {0x9C, 0x05, 0x07}, - {0x9E, 0x05, 0x07}, - {0xA0, 0x05, 0x07}, - {0x58, 0x00, 0x00}, - {0x4F, 0x05, 0x07}, - {0x50, 0x05, 0x07}, - {0x4E, 0x05, 0x07}, - {0x51, 0x05, 0x07}, - {0x3A, 0x05, 0x07}, - {0x3B, 0x05, 0x07}, - {0x3C, 0x05, 0x07}, - {0x3D, 0x05, 0x07}, - {0x95, 0x05, 0x07}, - {0x97, 0x05, 0x07}, - {0x9A, 0x05, 0x07}, - {0x15, 0x05, 0x07}, - {0x16, 0x05, 0x07}, - {0x1C, 0x05, 0x07}, - {0x1D, 0x05, 0x07}, - {0x1E, 0x05, 0x07}, - {0x1F, 0x05, 0x07}, - {0x3E, 0x05, 0x07}, - {0x3F, 0x05, 0x07}, - {0x40, 0x05, 0x07}, - {0x41, 0x05, 0x07}, - {0x91, 0x05, 0x07}, - {0x71, 0x05, 0x07}, - {0x72, 0x05, 0x07}, - {0x74, 0x05, 0x07}, - {0x22, 0x05, 0x07}, - {0x23, 0x05, 0x07}, - {0x20, 0x05, 0x07}, - {0x21, 0x05, 0x07}, - {0x24, 0x05, 0x07}, - {0x25, 0x05, 0x07}, - {0x62, 0x05, 0x07}, - {0x65, 0x05, 0x07}, - {0x66, 0x05, 0x07}, - {0x67, 0x05, 0x07}, - {0x68, 0x05, 0x07}, -}; - -constexpr u32 NumInitialConfigsCopper = util::size(InitialConfigsCopper); diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_hoag.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_hoag.inc deleted file mode 100644 index 5f1924c45..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_hoag.inc +++ /dev/null @@ -1,194 +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 . - */ - -constexpr InitialConfig InitialConfigsHoag[] = { - {0x5D, 0x00, 0x7F}, - {0x47, 0x28, 0x7F}, - {0x48, 0x00, 0x7F}, - {0x46, 0x00, 0x7F}, - {0x49, 0x00, 0x7F}, - {0x30, 0x40, 0x27F}, - {0x31, 0x40, 0x27F}, - {0x0D, 0x20, 0x27F}, - {0x0C, 0x00, 0x27F}, - {0x10, 0x40, 0x27F}, - {0x0F, 0x00, 0x27F}, - {0x0E, 0x20, 0x27F}, - {0x00, 0x40, 0x7F}, - {0x01, 0x50, 0x7F}, - {0x05, 0x50, 0x7F}, - {0x04, 0x50, 0x7F}, - {0x03, 0x50, 0x7F}, - {0x02, 0x50, 0x7F}, - {0xAA, 0x40, 0x7F}, - {0xAC, 0x40, 0x7F}, - {0xA2, 0x50, 0x7F}, - {0xA3, 0x50, 0x7F}, - {0xA4, 0x50, 0x7F}, - {0xA5, 0x50, 0x7F}, - {0xA6, 0x50, 0x7F}, - {0xA7, 0x50, 0x7F}, - {0xA8, 0x50, 0x7F}, - {0xA9, 0x50, 0x7F}, - {0x83, 0x00, 0x67}, - {0x5B, 0x00, 0x78}, - {0x7C, 0x01, 0x67}, - {0x80, 0x01, 0x7F}, - {0x34, 0x40, 0x27F}, - {0x35, 0x40, 0x27F}, - {0x55, 0x20, 0x78}, - {0x56, 0x01, 0x7F}, - {0x5C, 0x00, 0x78}, - {0x5A, 0x20, 0x78}, - {0x2C, 0x40, 0x27F}, - {0x2D, 0x40, 0x27F}, - {0x2E, 0x40, 0x27F}, - {0x2F, 0x40, 0x27F}, - {0x36, 0x00, 0x7F}, - {0x37, 0x30, 0x7F}, - {0x38, 0x00, 0x7F}, - {0x39, 0x28, 0x7F}, - {0x54, 0x00, 0x67}, - {0x9B, 0x30, 0x7F}, - {0x3E, 0x00, 0x7F}, - {0x3F, 0x20, 0x7F}, - {0x40, 0x00, 0x7F}, - {0x41, 0x30, 0x7F}, - {0x42, 0x00, 0x7F}, - {0x43, 0x28, 0x7F}, - {0x44, 0x00, 0x7F}, - {0x45, 0x28, 0x7F}, - {0x4B, 0x28, 0x7F}, - {0x4C, 0x00, 0x7F}, - {0x4A, 0x00, 0x7F}, - {0x4D, 0x00, 0x7F}, - {0x60, 0x04, 0x67}, - {0x61, 0x2C, 0x7F}, - {0x26, 0x04, 0x67}, - {0x27, 0x04, 0x67}, - {0x28, 0x04, 0x67}, - {0x29, 0x04, 0x67}, - {0x2A, 0x04, 0x67}, - {0x8F, 0x24, 0x7F}, - {0x90, 0x34, 0x7F}, - {0x33, 0x34, 0x27F}, - {0x53, 0x24, 0x7F}, - {0x77, 0x04, 0x67}, - {0x78, 0x24, 0x7F}, - {0x79, 0x04, 0x67}, - {0x7A, 0x04, 0x67}, - {0x11, 0x04, 0x67}, - {0x87, 0x04, 0x67}, - {0x88, 0x34, 0x7F}, - {0x86, 0x2C, 0x7F}, - {0x82, 0x24, 0x7F}, - {0x85, 0x34, 0x7F}, - {0x89, 0x24, 0x7F}, - {0x8A, 0x34, 0x7F}, - {0x8B, 0x34, 0x7F}, - {0x8C, 0x24, 0x7F}, - {0x8D, 0x34, 0x7F}, - {0x7D, 0x04, 0x67}, - {0x7E, 0x04, 0x67}, - {0x7F, 0x34, 0x7F}, - {0x81, 0x04, 0x67}, - {0x9C, 0x24, 0x7F}, - {0x9D, 0x34, 0x7F}, - {0x9F, 0x34, 0x7F}, - {0xA0, 0x04, 0x67}, - {0x50, 0x04, 0x67}, - {0x4E, 0x2C, 0x7F}, - {0x51, 0x04, 0x67}, - {0x92, 0x4C, 0x7F}, - {0x93, 0x4C, 0x7F}, - {0x94, 0x44, 0x7F}, - {0x96, 0x34, 0x7F}, - {0x97, 0x04, 0x67}, - {0x98, 0x34, 0x7F}, - {0x99, 0x04, 0x67}, - {0x1C, 0x24, 0x7F}, - {0x1D, 0x24, 0x7F}, - {0x1E, 0x24, 0x7F}, - {0x1F, 0x24, 0x7F}, - {0x6A, 0x04, 0x67}, - {0x6B, 0x04, 0x67}, - {0x6C, 0x2C, 0x7F}, - {0x6D, 0x04, 0x67}, - {0x6E, 0x04, 0x67}, - {0x6F, 0x24, 0x7F}, - {0x91, 0x24, 0x7F}, - {0x70, 0x04, 0x7F}, - {0x71, 0x04, 0x67}, - {0x72, 0x04, 0x67}, - {0x68, 0x204, 0x267}, - {0x5E, 0x05, 0x07}, - {0x5F, 0x05, 0x07}, - {0x17, 0x05, 0x07}, - {0x18, 0x05, 0x07}, - {0x19, 0x05, 0x07}, - {0x1A, 0x05, 0x07}, - {0x1B, 0x05, 0x07}, - {0x2B, 0x05, 0x07}, - {0x32, 0x05, 0x07}, - {0x52, 0x05, 0x07}, - {0x75, 0x05, 0x07}, - {0x76, 0x05, 0x07}, - {0x8E, 0x05, 0x07}, - {0xAB, 0x05, 0x07}, - {0xAD, 0x05, 0x07}, - {0xAE, 0x05, 0x07}, - {0x06, 0x05, 0x07}, - {0x07, 0x05, 0x07}, - {0x08, 0x05, 0x07}, - {0x09, 0x05, 0x07}, - {0x0A, 0x05, 0x07}, - {0x0B, 0x05, 0x07}, - {0x84, 0x05, 0x07}, - {0x7B, 0x05, 0x07}, - {0x9E, 0x05, 0x07}, - {0xA1, 0x05, 0x07}, - {0x58, 0x00, 0x00}, - {0x59, 0x00, 0x00}, - {0x4F, 0x05, 0x07}, - {0x3A, 0x05, 0x07}, - {0x3B, 0x05, 0x07}, - {0x3C, 0x05, 0x07}, - {0x3D, 0x05, 0x07}, - {0x95, 0x05, 0x07}, - {0x9A, 0x05, 0x07}, - {0x12, 0x05, 0x07}, - {0x13, 0x05, 0x07}, - {0x14, 0x05, 0x07}, - {0x15, 0x05, 0x07}, - {0x16, 0x05, 0x07}, - {0x73, 0x05, 0x07}, - {0x74, 0x05, 0x07}, - {0x22, 0x05, 0x07}, - {0x23, 0x05, 0x07}, - {0x20, 0x05, 0x07}, - {0x21, 0x05, 0x07}, - {0x24, 0x05, 0x07}, - {0x25, 0x05, 0x07}, - {0x62, 0x05, 0x07}, - {0x65, 0x05, 0x07}, - {0x66, 0x05, 0x07}, - {0x67, 0x05, 0x07}, - {0x69, 0x05, 0x07}, - {0x64, 0x05, 0x07}, - {0x63, 0x05, 0x07}, -}; - -constexpr u32 NumInitialConfigsHoag = util::size(InitialConfigsHoag); diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_icosa.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_icosa.inc deleted file mode 100644 index 553f05dc2..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_icosa.inc +++ /dev/null @@ -1,181 +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 . - */ - -constexpr InitialConfig InitialConfigsIcosa[] = { - {0x5D, 0x00, 0x67}, - {0x47, 0x28, 0x7F}, - {0x48, 0x00, 0x67}, - {0x46, 0x00, 0x67}, - {0x49, 0x00, 0x67}, - {0x30, 0x40, 0x27F}, - {0x31, 0x40, 0x27F}, - {0x0D, 0x20, 0x27F}, - {0x0C, 0x00, 0x267}, - {0x10, 0x20, 0x27F}, - {0x0F, 0x00, 0x267}, - {0x0E, 0x20, 0x27F}, - {0x00, 0x48, 0x7F}, - {0x01, 0x50, 0x7F}, - {0x05, 0x50, 0x7F}, - {0x04, 0x50, 0x7F}, - {0x03, 0x50, 0x7F}, - {0x02, 0x50, 0x7F}, - {0x5B, 0x00, 0x78}, - {0x7C, 0x01, 0x67}, - {0x80, 0x01, 0x7F}, - {0x34, 0x40, 0x27F}, - {0x35, 0x40, 0x27F}, - {0x55, 0x20, 0x78}, - {0x56, 0x20, 0x7F}, - {0xA1, 0x30, 0x7F}, - {0x5C, 0x00, 0x78}, - {0x59, 0x00, 0x60}, - {0x5A, 0x30, 0x78}, - {0x2C, 0x40, 0x27F}, - {0x2D, 0x40, 0x27F}, - {0x2E, 0x40, 0x27F}, - {0x2F, 0x40, 0x27F}, - {0x3B, 0x20, 0x7F}, - {0x3C, 0x00, 0x67}, - {0x3D, 0x20, 0x7F}, - {0x36, 0x00, 0x67}, - {0x37, 0x30, 0x7F}, - {0x38, 0x00, 0x67}, - {0x39, 0x28, 0x7F}, - {0x54, 0x00, 0x67}, - {0x9B, 0x30, 0x7F}, - {0x1C, 0x00, 0x67}, - {0x1D, 0x30, 0x7F}, - {0x1E, 0x00, 0x67}, - {0x1F, 0x00, 0x67}, - {0x3F, 0x20, 0x7F}, - {0x40, 0x00, 0x67}, - {0x41, 0x20, 0x7F}, - {0x42, 0x00, 0x67}, - {0x43, 0x28, 0x7F}, - {0x44, 0x00, 0x67}, - {0x45, 0x28, 0x7F}, - {0x22, 0x00, 0x67}, - {0x23, 0x28, 0x7F}, - {0x20, 0x00, 0x67}, - {0x21, 0x00, 0x67}, - {0x4B, 0x28, 0x7F}, - {0x4C, 0x00, 0x67}, - {0x4A, 0x00, 0x67}, - {0x4D, 0x00, 0x67}, - {0x64, 0x20, 0x27F}, - {0x5F, 0x34, 0x7F}, - {0x60, 0x04, 0x67}, - {0x61, 0x2C, 0x7F}, - {0x2A, 0x04, 0x67}, - {0x2B, 0x04, 0x67}, - {0x8F, 0x24, 0x7F}, - {0x33, 0x34, 0x27F}, - {0x52, 0x2C, 0x7F}, - {0x53, 0x24, 0x7F}, - {0x77, 0x04, 0x67}, - {0x78, 0x34, 0x7F}, - {0x11, 0x04, 0x67}, - {0x06, 0x2C, 0x7F}, - {0x08, 0x24, 0x7F}, - {0x09, 0x24, 0x7F}, - {0x0A, 0x24, 0x7F}, - {0x0B, 0x24, 0x7F}, - {0x88, 0x34, 0x7F}, - {0x86, 0x2C, 0x7F}, - {0x82, 0x24, 0x7F}, - {0x85, 0x34, 0x7F}, - {0x89, 0x34, 0x7F}, - {0x8A, 0x34, 0x7F}, - {0x8B, 0x34, 0x7F}, - {0x8C, 0x34, 0x7F}, - {0x8D, 0x24, 0x7F}, - {0x7D, 0x04, 0x67}, - {0x7E, 0x04, 0x67}, - {0x81, 0x04, 0x67}, - {0x9C, 0x34, 0x7F}, - {0x9D, 0x34, 0x7F}, - {0x9E, 0x2C, 0x7F}, - {0x9F, 0x34, 0x7F}, - {0xA0, 0x04, 0x67}, - {0x4F, 0x04, 0x67}, - {0x51, 0x04, 0x67}, - {0x3A, 0x24, 0x7F}, - {0x92, 0x4C, 0x7F}, - {0x93, 0x4C, 0x7F}, - {0x94, 0x44, 0x7F}, - {0x95, 0x04, 0x67}, - {0x96, 0x34, 0x7F}, - {0x97, 0x04, 0x67}, - {0x98, 0x34, 0x7F}, - {0x99, 0x34, 0x7F}, - {0x9A, 0x04, 0x67}, - {0x3E, 0x24, 0x7F}, - {0x6A, 0x04, 0x67}, - {0x6B, 0x04, 0x67}, - {0x6C, 0x2C, 0x7F}, - {0x6D, 0x04, 0x67}, - {0x6E, 0x04, 0x67}, - {0x6F, 0x24, 0x7F}, - {0x91, 0x24, 0x7F}, - {0x70, 0x04, 0x7F}, - {0x71, 0x04, 0x67}, - {0x72, 0x04, 0x67}, - {0x65, 0x34, 0x7F}, - {0x66, 0x04, 0x67}, - {0x67, 0x04, 0x267}, - {0x5E, 0x05, 0x07}, - {0x17, 0x05, 0x07}, - {0x18, 0x05, 0x07}, - {0x19, 0x05, 0x07}, - {0x1A, 0x05, 0x07}, - {0x1B, 0x05, 0x07}, - {0x26, 0x05, 0x07}, - {0x27, 0x05, 0x07}, - {0x28, 0x05, 0x07}, - {0x29, 0x05, 0x07}, - {0x90, 0x05, 0x07}, - {0x32, 0x05, 0x07}, - {0x75, 0x05, 0x07}, - {0x76, 0x05, 0x07}, - {0x79, 0x05, 0x07}, - {0x7A, 0x05, 0x07}, - {0x8E, 0x05, 0x07}, - {0x07, 0x05, 0x07}, - {0x87, 0x05, 0x07}, - {0x83, 0x05, 0x07}, - {0x84, 0x05, 0x07}, - {0x7B, 0x05, 0x07}, - {0x7F, 0x05, 0x07}, - {0x58, 0x00, 0x00}, - {0x50, 0x05, 0x07}, - {0x4E, 0x05, 0x07}, - {0x12, 0x05, 0x07}, - {0x13, 0x05, 0x07}, - {0x14, 0x05, 0x07}, - {0x15, 0x05, 0x07}, - {0x16, 0x05, 0x07}, - {0x73, 0x05, 0x07}, - {0x74, 0x05, 0x07}, - {0x24, 0x05, 0x07}, - {0x25, 0x05, 0x07}, - {0x62, 0x05, 0x07}, - {0x68, 0x05, 0x07}, - {0x69, 0x05, 0x07}, - {0x63, 0x05, 0x07}, -}; - -constexpr u32 NumInitialConfigsIcosa = util::size(InitialConfigsIcosa); diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_iowa.inc b/stratosphere/boot/source/pinmux/pinmux_initial_configuration_iowa.inc deleted file mode 100644 index 471db0e55..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_initial_configuration_iowa.inc +++ /dev/null @@ -1,194 +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 . - */ - -constexpr InitialConfig InitialConfigsIowa[] = { - {0x5D, 0x00, 0x7F}, - {0x47, 0x28, 0x7F}, - {0x48, 0x00, 0x7F}, - {0x46, 0x00, 0x7F}, - {0x49, 0x00, 0x7F}, - {0x30, 0x40, 0x27F}, - {0x31, 0x40, 0x27F}, - {0x0D, 0x20, 0x27F}, - {0x0C, 0x00, 0x27F}, - {0x10, 0x40, 0x27F}, - {0x0F, 0x00, 0x27F}, - {0x0E, 0x20, 0x27F}, - {0x00, 0x40, 0x7F}, - {0x01, 0x50, 0x7F}, - {0x05, 0x50, 0x7F}, - {0x04, 0x50, 0x7F}, - {0x03, 0x50, 0x7F}, - {0x02, 0x50, 0x7F}, - {0xAA, 0x40, 0x7F}, - {0xAC, 0x40, 0x7F}, - {0xA2, 0x50, 0x7F}, - {0xA3, 0x50, 0x7F}, - {0xA4, 0x50, 0x7F}, - {0xA5, 0x50, 0x7F}, - {0xA6, 0x50, 0x7F}, - {0xA7, 0x50, 0x7F}, - {0xA8, 0x50, 0x7F}, - {0xA9, 0x50, 0x7F}, - {0x5B, 0x00, 0x78}, - {0x7C, 0x01, 0x67}, - {0x80, 0x01, 0x7F}, - {0x34, 0x40, 0x27F}, - {0x35, 0x40, 0x27F}, - {0x55, 0x20, 0x78}, - {0x56, 0x20, 0x7F}, - {0xA1, 0x30, 0x7F}, - {0x5C, 0x00, 0x78}, - {0x5A, 0x20, 0x78}, - {0x2C, 0x40, 0x27F}, - {0x2D, 0x40, 0x27F}, - {0x2E, 0x40, 0x27F}, - {0x2F, 0x40, 0x27F}, - {0x3B, 0x20, 0x7F}, - {0x3C, 0x00, 0x7F}, - {0x3D, 0x20, 0x7F}, - {0x36, 0x00, 0x7F}, - {0x37, 0x30, 0x7F}, - {0x38, 0x00, 0x7F}, - {0x39, 0x28, 0x7F}, - {0x54, 0x00, 0x67}, - {0x9B, 0x30, 0x7F}, - {0x1C, 0x00, 0x7F}, - {0x1D, 0x30, 0x7F}, - {0x1E, 0x00, 0x7F}, - {0x1F, 0x00, 0x7F}, - {0x3F, 0x20, 0x7F}, - {0x40, 0x00, 0x7F}, - {0x41, 0x20, 0x7F}, - {0x42, 0x00, 0x7F}, - {0x43, 0x28, 0x7F}, - {0x44, 0x00, 0x7F}, - {0x45, 0x28, 0x7F}, - {0x4B, 0x28, 0x7F}, - {0x4C, 0x00, 0x7F}, - {0x4A, 0x00, 0x7F}, - {0x4D, 0x00, 0x7F}, - {0x64, 0x20, 0x27F}, - {0x5F, 0x34, 0x7F}, - {0x60, 0x04, 0x67}, - {0x61, 0x2C, 0x7F}, - {0x2A, 0x04, 0x67}, - {0x8F, 0x24, 0x7F}, - {0x33, 0x34, 0x27F}, - {0x52, 0x2C, 0x7F}, - {0x53, 0x24, 0x7F}, - {0x77, 0x04, 0x67}, - {0x78, 0x24, 0x7F}, - {0x11, 0x04, 0x67}, - {0x06, 0x2C, 0x7F}, - {0x08, 0x24, 0x7F}, - {0x09, 0x24, 0x7F}, - {0x0A, 0x24, 0x7F}, - {0x0B, 0x24, 0x7F}, - {0x87, 0x04, 0x67}, - {0x88, 0x34, 0x7F}, - {0x86, 0x2C, 0x7F}, - {0x82, 0x24, 0x7F}, - {0x85, 0x34, 0x7F}, - {0x89, 0x24, 0x7F}, - {0x8A, 0x34, 0x7F}, - {0x8B, 0x34, 0x7F}, - {0x8C, 0x24, 0x7F}, - {0x8D, 0x24, 0x7F}, - {0x7D, 0x04, 0x67}, - {0x7E, 0x04, 0x67}, - {0x81, 0x04, 0x67}, - {0x9C, 0x24, 0x7F}, - {0x9D, 0x34, 0x7F}, - {0x9E, 0x2C, 0x7F}, - {0x9F, 0x34, 0x7F}, - {0xA0, 0x04, 0x67}, - {0x4F, 0x04, 0x67}, - {0x51, 0x04, 0x67}, - {0x3A, 0x24, 0x7F}, - {0x92, 0x4C, 0x7F}, - {0x93, 0x4C, 0x7F}, - {0x94, 0x44, 0x7F}, - {0x95, 0x04, 0x67}, - {0x96, 0x34, 0x7F}, - {0x97, 0x04, 0x67}, - {0x98, 0x34, 0x7F}, - {0x9A, 0x04, 0x67}, - {0x3E, 0x24, 0x7F}, - {0x6A, 0x04, 0x67}, - {0x6B, 0x04, 0x67}, - {0x6C, 0x2C, 0x7F}, - {0x6D, 0x04, 0x67}, - {0x6E, 0x04, 0x67}, - {0x6F, 0x24, 0x7F}, - {0x91, 0x24, 0x7F}, - {0x70, 0x04, 0x7F}, - {0x71, 0x04, 0x67}, - {0x72, 0x04, 0x67}, - {0x65, 0x34, 0x7F}, - {0x66, 0x04, 0x67}, - {0x67, 0x04, 0x267}, - {0x5E, 0x05, 0x07}, - {0x17, 0x05, 0x07}, - {0x18, 0x05, 0x07}, - {0x19, 0x05, 0x07}, - {0x1A, 0x05, 0x07}, - {0x1B, 0x05, 0x07}, - {0x26, 0x05, 0x07}, - {0x27, 0x05, 0x07}, - {0x28, 0x05, 0x07}, - {0x29, 0x05, 0x07}, - {0x2B, 0x05, 0x07}, - {0x90, 0x05, 0x07}, - {0x32, 0x05, 0x07}, - {0x75, 0x05, 0x07}, - {0x76, 0x05, 0x07}, - {0x79, 0x05, 0x07}, - {0x7A, 0x05, 0x07}, - {0x8E, 0x05, 0x07}, - {0xAB, 0x05, 0x07}, - {0xAD, 0x05, 0x07}, - {0xAE, 0x05, 0x07}, - {0x07, 0x05, 0x07}, - {0x83, 0x05, 0x07}, - {0x84, 0x05, 0x07}, - {0x7B, 0x05, 0x07}, - {0x7F, 0x05, 0x07}, - {0x58, 0x00, 0x00}, - {0x59, 0x00, 0x00}, - {0x50, 0x05, 0x07}, - {0x4E, 0x05, 0x07}, - {0x99, 0x05, 0x07}, - {0x12, 0x05, 0x07}, - {0x13, 0x05, 0x07}, - {0x14, 0x05, 0x07}, - {0x15, 0x05, 0x07}, - {0x16, 0x05, 0x07}, - {0x73, 0x05, 0x07}, - {0x74, 0x05, 0x07}, - {0x22, 0x05, 0x07}, - {0x23, 0x05, 0x07}, - {0x20, 0x05, 0x07}, - {0x21, 0x05, 0x07}, - {0x24, 0x05, 0x07}, - {0x25, 0x05, 0x07}, - {0x62, 0x05, 0x07}, - {0x68, 0x05, 0x07}, - {0x69, 0x05, 0x07}, - {0x63, 0x05, 0x07}, -}; - -constexpr u32 NumInitialConfigsIowa = util::size(InitialConfigsIowa); diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration.inc b/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration.inc deleted file mode 100644 index cef51f40f..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration.inc +++ /dev/null @@ -1,67 +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 . - */ - -constexpr InitialConfig InitialDrivePadConfigs[] = { - {0x04, 0x01010000, 0x01F1F000}, - {0x0D, 0x01010000, 0x01F1F000}, - {0x10, 0x01010000, 0x01F1F000}, - {0x12, 0x01010000, 0x01F1F000}, - {0x13, 0x01010000, 0x01F1F000}, - {0x14, 0x0001F000, 0x01F1F000}, - {0x15, 0x0001F000, 0x01F1F000}, - {0x24, 0x01010000, 0x01F1F000}, - {0x25, 0x01010000, 0x01F1F000}, - {0x26, 0x01010000, 0x01F1F000}, - {0x27, 0x01010000, 0x01F1F000}, - {0x28, 0x01010000, 0x01F1F000}, - {0x29, 0x01010000, 0x01F1F000}, - {0x2A, 0x01010000, 0x01F1F000}, - {0x2B, 0x01010000, 0x01F1F000}, - {0x2C, 0x01F1F000, 0x01F1F000}, - {0x2D, 0x01F1F000, 0x01F1F000}, - {0x2F, 0x01F1F000, 0x01F1F000}, - {0x30, 0x01404000, 0x01F1F000}, - {0x31, 0x0001F000, 0x01F1F000}, - {0x32, 0x0001F000, 0x01F1F000}, - {0x33, 0x0001F000, 0x01F1F000}, - {0x34, 0x0001F000, 0x01F1F000}, - {0x35, 0x00007000, 0x01F1F000}, - {0x36, 0x00007000, 0x01F1F000}, - {0x46, 0x01010000, 0x01F1F000}, - {0x47, 0x01010000, 0x01F1F000}, - {0x4C, 0x01404000, 0x01F1F000}, - {0x4D, 0x01404000, 0x01F1F000}, - {0x62, 0x0001F000, 0x01F1F000}, - {0x63, 0x0001F000, 0x01F1F000}, - {0x7C, 0x01414000, 0x01F1F000}, - {0x87, 0x01404000, 0x01F1F000}, - {0x88, 0x01404000, 0x01F1F000}, - {0x89, 0x01404000, 0x01F1F000}, - {0x8A, 0x01404000, 0x01F1F000}, - {0x6D, 0x00000000, 0xF0000000}, - {0x6E, 0x00000000, 0xF0000000}, - {0x6F, 0x00000000, 0xF0000000}, - {0x70, 0x00000000, 0xF0000000}, - {0x71, 0x00000000, 0xF0000000}, - {0x72, 0x00000000, 0xF0000000}, - {0x73, 0x00000000, 0xF0000000}, - {0x74, 0x00000000, 0xF0000000}, - {0x75, 0x00000000, 0xF0000000}, - {0x76, 0x00000000, 0xF0000000}, - {0x69, 0x51212000, 0xF1F1F000}, -}; - -constexpr u32 NumInitialDrivePadConfigs = util::size(InitialDrivePadConfigs); diff --git a/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration_hoag.inc b/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration_hoag.inc deleted file mode 100644 index 05458c682..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_initial_drive_pad_configuration_hoag.inc +++ /dev/null @@ -1,67 +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 . - */ - -constexpr InitialConfig InitialDrivePadConfigsHoag[] = { - {0x04, 0x01010000, 0x01F1F000}, - {0x0D, 0x01010000, 0x01F1F000}, - {0x10, 0x01010000, 0x01F1F000}, - {0x12, 0x01010000, 0x01F1F000}, - {0x13, 0x01010000, 0x01F1F000}, - {0x14, 0x0001F000, 0x01F1F000}, - {0x15, 0x0001F000, 0x01F1F000}, - {0x24, 0x01010000, 0x01F1F000}, - {0x25, 0x01010000, 0x01F1F000}, - {0x26, 0x01010000, 0x01F1F000}, - {0x27, 0x01010000, 0x01F1F000}, - {0x28, 0x01010000, 0x01F1F000}, - {0x29, 0x01010000, 0x01F1F000}, - {0x2A, 0x01010000, 0x01F1F000}, - {0x2B, 0x01010000, 0x01F1F000}, - {0x2C, 0x01F1F000, 0x01F1F000}, - {0x2D, 0x01F1F000, 0x01F1F000}, - {0x2F, 0x01F1F000, 0x01F1F000}, - {0x30, 0x01404000, 0x01F1F000}, - {0x31, 0x0001F000, 0x01F1F000}, - {0x32, 0x0001F000, 0x01F1F000}, - {0x33, 0x00004000, 0x01F1F000}, - {0x34, 0x00004000, 0x01F1F000}, - {0x35, 0x00007000, 0x01F1F000}, - {0x36, 0x00007000, 0x01F1F000}, - {0x46, 0x01010000, 0x01F1F000}, - {0x47, 0x01010000, 0x01F1F000}, - {0x4C, 0x01404000, 0x01F1F000}, - {0x4D, 0x01404000, 0x01F1F000}, - {0x62, 0x0001F000, 0x01F1F000}, - {0x63, 0x0001F000, 0x01F1F000}, - {0x7C, 0x01414000, 0x01F1F000}, - {0x87, 0x01404000, 0x01F1F000}, - {0x88, 0x01404000, 0x01F1F000}, - {0x89, 0x01404000, 0x01F1F000}, - {0x8A, 0x01404000, 0x01F1F000}, - {0x6D, 0x00000000, 0xF0000000}, - {0x6E, 0x00000000, 0xF0000000}, - {0x6F, 0x00000000, 0xF0000000}, - {0x70, 0x00000000, 0xF0000000}, - {0x71, 0x00000000, 0xF0000000}, - {0x72, 0x00000000, 0xF0000000}, - {0x73, 0x00000000, 0xF0000000}, - {0x74, 0x00000000, 0xF0000000}, - {0x75, 0x00000000, 0xF0000000}, - {0x76, 0x00000000, 0xF0000000}, - {0x69, 0x51212000, 0xF1F1F000}, -}; - -constexpr u32 NumInitialDrivePadConfigsHoag = util::size(InitialDrivePadConfigsHoag); diff --git a/stratosphere/boot/source/pinmux/pinmux_map.inc b/stratosphere/boot/source/pinmux/pinmux_map.inc deleted file mode 100644 index f6a18c37f..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_map.inc +++ /dev/null @@ -1,361 +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 . - */ - -struct Definition { - u32 reg_offset; - u32 mask_val; - u32 pm_val; -}; - -struct DrivePadDefinition { - u32 reg_offset; - u32 mask_val; -}; - -constexpr Definition Map[] = { - {0x00003000, 0x72FF, 0x01}, /* Sdmmc1Clk */ - {0x00003004, 0x72FF, 0x02}, /* Sdmmc1Cmd */ - {0x00003008, 0x72FF, 0x02}, /* Sdmmc1Dat3 */ - {0x0000300C, 0x72FF, 0x02}, /* Sdmmc1Dat2 */ - {0x00003010, 0x72FF, 0x02}, /* Sdmmc1Dat1 */ - {0x00003014, 0x72FF, 0x01}, /* Sdmmc1Dat0 */ - {0x0000301C, 0x72FF, 0x01}, /* Sdmmc3Clk */ - {0x00003020, 0x72FF, 0x01}, /* Sdmmc3Cmd */ - {0x00003024, 0x72FF, 0x01}, /* Sdmmc3Dat0 */ - {0x00003028, 0x72FF, 0x01}, /* Sdmmc3Dat1 */ - {0x0000302C, 0x72FF, 0x01}, /* Sdmmc3Dat2 */ - {0x00003030, 0x72FF, 0x01}, /* Sdmmc3Dat3 */ - {0x00003038, 0x1DFF, 0x01}, /* PexL0RstN */ - {0x0000303C, 0x1DFF, 0x01}, /* PexL0ClkreqN */ - {0x00003040, 0x1DFF, 0x01}, /* PexWakeN */ - {0x00003044, 0x1DFF, 0x01}, /* PexL1RstN */ - {0x00003048, 0x1DFF, 0x01}, /* PexL1ClkreqN */ - {0x0000304C, 0x19FF, 0x01}, /* SataLedActive */ - {0x00003050, 0x1F2FF, 0x01}, /* Spi1Mosi */ - {0x00003054, 0x1F2FF, 0x01}, /* Spi1Miso */ - {0x00003058, 0x1F2FF, 0x01}, /* Spi1Sck */ - {0x0000305C, 0x1F2FF, 0x01}, /* Spi1Cs0 */ - {0x00003060, 0x1F2FF, 0x01}, /* Spi1Cs1 */ - {0x00003064, 0x72FF, 0x02}, /* Spi2Mosi */ - {0x00003068, 0x72FF, 0x02}, /* Spi2Miso */ - {0x0000306C, 0x72FF, 0x02}, /* Spi2Sck */ - {0x00003070, 0x72FF, 0x02}, /* Spi2Cs0 */ - {0x00003074, 0x72FF, 0x01}, /* Spi2Cs1 */ - {0x00003078, 0x1F2FF, 0x01}, /* Spi4Mosi */ - {0x0000307C, 0x1F2FF, 0x01}, /* Spi4Miso */ - {0x00003080, 0x1F2FF, 0x01}, /* Spi4Sck */ - {0x00003084, 0x1F2FF, 0x01}, /* Spi4Cs0 */ - {0x00003088, 0x72FF, 0x01}, /* QspiSck */ - {0x0000308C, 0x72FF, 0x01}, /* QspiCsN */ - {0x00003090, 0x72FF, 0x01}, /* QspiIo0 */ - {0x00003094, 0x72FF, 0x01}, /* QspiIo1 */ - {0x00003098, 0x72FF, 0x01}, /* QspiIo2 */ - {0x0000309C, 0x72FF, 0x01}, /* QspiIo3 */ - {0x000030A4, 0x19FF, 0x02}, /* Dmic1Clk */ - {0x000030A8, 0x19FF, 0x02}, /* Dmic1Dat */ - {0x000030AC, 0x19FF, 0x02}, /* Dmic2Clk */ - {0x000030B0, 0x19FF, 0x02}, /* Dmic2Dat */ - {0x000030B4, 0x19FF, 0x02}, /* Dmic3Clk */ - {0x000030B8, 0x19FF, 0x02}, /* Dmic3Dat */ - {0x000030BC, 0x1DFF, 0x01}, /* Gen1I2cScl */ - {0x000030C0, 0x1DFF, 0x01}, /* Gen1I2cSda */ - {0x000030C4, 0x1DFF, 0x01}, /* Gen2I2cScl */ - {0x000030C8, 0x1DFF, 0x01}, /* Gen2I2cSda */ - {0x000030CC, 0x1DFF, 0x01}, /* Gen3I2cScl */ - {0x000030D0, 0x1DFF, 0x01}, /* Gen3I2cSda */ - {0x000030D4, 0x1DFF, 0x02}, /* CamI2cScl */ - {0x000030D8, 0x1DFF, 0x02}, /* CamI2cSda */ - {0x000030DC, 0x1DFF, 0x01}, /* PwrI2cScl */ - {0x000030E0, 0x1DFF, 0x01}, /* PwrI2cSda */ - {0x000030E4, 0x19FF, 0x01}, /* Uart1Tx */ - {0x000030E8, 0x19FF, 0x01}, /* Uart1Rx */ - {0x000030EC, 0x19FF, 0x01}, /* Uart1Rts */ - {0x000030F0, 0x19FF, 0x01}, /* Uart1Cts */ - {0x000030F4, 0x19FF, 0x00}, /* Uart2Tx */ - {0x000030F8, 0x19FF, 0x00}, /* Uart2Rx */ - {0x000030FC, 0x19FF, 0x02}, /* Uart2Rts */ - {0x00003100, 0x19FF, 0x02}, /* Uart2Cts */ - {0x00003104, 0x19FF, 0x02}, /* Uart3Tx */ - {0x00003108, 0x19FF, 0x02}, /* Uart3Rx */ - {0x0000310C, 0x19FF, 0x02}, /* Uart3Rts */ - {0x00003110, 0x19FF, 0x02}, /* Uart3Cts */ - {0x00003114, 0x19FF, 0x02}, /* Uart4Tx */ - {0x00003118, 0x19FF, 0x02}, /* Uart4Rx */ - {0x0000311C, 0x19FF, 0x02}, /* Uart4Rts */ - {0x00003120, 0x19FF, 0x02}, /* Uart4Cts */ - {0x00003124, 0x72FF, 0x01}, /* Dap1Fs */ - {0x00003128, 0x72FF, 0x01}, /* Dap1Din */ - {0x0000312C, 0x72FF, 0x01}, /* Dap1Dout */ - {0x00003130, 0x72FF, 0x01}, /* Dap1Sclk */ - {0x00003134, 0x72FF, 0x01}, /* Dap2Fs */ - {0x00003138, 0x72FF, 0x01}, /* Dap2Din */ - {0x0000313C, 0x72FF, 0x01}, /* Dap2Dout */ - {0x00003140, 0x72FF, 0x01}, /* Dap2Sclk */ - {0x00003144, 0x72FF, 0x01}, /* Dap4Fs */ - {0x00003148, 0x72FF, 0x01}, /* Dap4Din */ - {0x0000314C, 0x72FF, 0x01}, /* Dap4Dout */ - {0x00003150, 0x72FF, 0x01}, /* Dap4Sclk */ - {0x00003154, 0x72FF, 0x01}, /* Cam1Mclk */ - {0x00003158, 0x72FF, 0x01}, /* Cam2Mclk */ - {0x0000315C, 0x72FF, 0x01}, /* JtagRtck */ - {0x00003160, 0x118C, 0xFF}, /* Clk32kIn */ - {0x00003164, 0x72FF, 0x02}, /* Clk32kOut */ - {0x00003168, 0x1DFF, 0x01}, /* BattBcl */ - {0x0000316C, 0x11CC, 0xFF}, /* ClkReq */ - {0x00003170, 0x11CC, 0xFF}, /* CpuPwrReq */ - {0x00003174, 0x11CC, 0xFF}, /* PwrIntN */ - {0x00003178, 0x11CC, 0xFF}, /* Shutdown */ - {0x0000317C, 0x11CC, 0xFF}, /* CorePwrReq */ - {0x00003180, 0x19FF, 0x01}, /* AudMclk */ - {0x00003184, 0x19FF, 0x00}, /* DvfsPwm */ - {0x00003188, 0x19FF, 0x00}, /* DvfsClk */ - {0x0000318C, 0x19FF, 0x00}, /* GpioX1Aud */ - {0x00003190, 0x19FF, 0x00}, /* GpioX3Aud */ - {0x00003194, 0x1DFF, 0x00}, /* GpioPcc7 */ - {0x00003198, 0x1DFF, 0x01}, /* HdmiCec */ - {0x0000319C, 0x1DFF, 0x01}, /* HdmiIntDpHpd */ - {0x000031A0, 0x19FF, 0x01}, /* SpdifOut */ - {0x000031A4, 0x19FF, 0x01}, /* SpdifIn */ - {0x000031A8, 0x1DFF, 0x01}, /* UsbVbusEn0 */ - {0x000031AC, 0x1DFF, 0x01}, /* UsbVbusEn1 */ - {0x000031B0, 0x19FF, 0x01}, /* DpHpd0 */ - {0x000031B4, 0x19FF, 0x00}, /* WifiEn */ - {0x000031B8, 0x19FF, 0x00}, /* WifiRst */ - {0x000031BC, 0x19FF, 0x00}, /* WifiWakeAp */ - {0x000031C0, 0x19FF, 0x00}, /* ApWakeBt */ - {0x000031C4, 0x19FF, 0x00}, /* BtRst */ - {0x000031C8, 0x19FF, 0x00}, /* BtWakeAp */ - {0x000031CC, 0x19FF, 0x00}, /* ApWakeNfc */ - {0x000031D0, 0x19FF, 0x00}, /* NfcEn */ - {0x000031D4, 0x19FF, 0x00}, /* NfcInt */ - {0x000031D8, 0x19FF, 0x00}, /* GpsEn */ - {0x000031DC, 0x19FF, 0x00}, /* GpsRst */ - {0x000031E0, 0x19FF, 0x01}, /* CamRst */ - {0x000031E4, 0x19FF, 0x02}, /* CamAfEn */ - {0x000031E8, 0x19FF, 0x02}, /* CamFlashEn */ - {0x000031EC, 0x19FF, 0x01}, /* Cam1Pwdn */ - {0x000031F0, 0x19FF, 0x01}, /* Cam2Pwdn */ - {0x000031F4, 0x19FF, 0x01}, /* Cam1Strobe */ - {0x000031F8, 0x19FF, 0x01}, /* LcdTe */ - {0x000031FC, 0x19FF, 0x03}, /* LcdBlPwm */ - {0x00003200, 0x19FF, 0x00}, /* LcdBlEn */ - {0x00003204, 0x19FF, 0x00}, /* LcdRst */ - {0x00003208, 0x19FF, 0x01}, /* LcdGpio1 */ - {0x0000320C, 0x19FF, 0x02}, /* LcdGpio2 */ - {0x00003210, 0x19FF, 0x00}, /* ApReady */ - {0x00003214, 0x19FF, 0x00}, /* TouchRst */ - {0x00003218, 0x19FF, 0x01}, /* TouchClk */ - {0x0000321C, 0x19FF, 0x00}, /* ModemWakeAp */ - {0x00003220, 0x19FF, 0x00}, /* TouchInt */ - {0x00003224, 0x19FF, 0x00}, /* MotionInt */ - {0x00003228, 0x19FF, 0x00}, /* AlsProxInt */ - {0x0000322C, 0x19FF, 0x00}, /* TempAlert */ - {0x00003230, 0x19FF, 0x00}, /* ButtonPowerOn */ - {0x00003234, 0x19FF, 0x00}, /* ButtonVolUp */ - {0x00003238, 0x19FF, 0x00}, /* ButtonVolDown */ - {0x0000323C, 0x19FF, 0x00}, /* ButtonSlideSw */ - {0x00003240, 0x19FF, 0x00}, /* ButtonHome */ - {0x00003244, 0x19FF, 0x01}, /* GpioPa6 */ - {0x00003248, 0x19FF, 0x00}, /* GpioPe6 */ - {0x0000324C, 0x19FF, 0x00}, /* GpioPe7 */ - {0x00003250, 0x19FF, 0x00}, /* GpioPh6 */ - {0x00003254, 0x72FF, 0x02}, /* GpioPk0 */ - {0x00003258, 0x72FF, 0x02}, /* GpioPk1 */ - {0x0000325C, 0x72FF, 0x02}, /* GpioPk2 */ - {0x00003260, 0x72FF, 0x02}, /* GpioPk3 */ - {0x00003264, 0x72FF, 0x01}, /* GpioPk4 */ - {0x00003268, 0x72FF, 0x01}, /* GpioPk5 */ - {0x0000326C, 0x72FF, 0x01}, /* GpioPk6 */ - {0x00003270, 0x72FF, 0x01}, /* GpioPk7 */ - {0x00003274, 0x72FF, 0x00}, /* GpioPl0 */ - {0x00003278, 0x72FF, 0x01}, /* GpioPl1 */ - {0x0000327C, 0x72FF, 0x01}, /* GpioPz0 */ - {0x00003280, 0x72FF, 0x02}, /* GpioPz1 */ - {0x00003284, 0x72FF, 0x02}, /* GpioPz2 */ - {0x00003288, 0x72FF, 0x01}, /* GpioPz3 */ - {0x0000328C, 0x72FF, 0x01}, /* GpioPz4 */ - {0x00003290, 0x72FF, 0x01}, /* GpioPz5 */ - - /* 5.0.0+ only */ - {0x00003294, 0x1F2FF, 0x02}, /* Sdmmc2Dat0 */ - {0x00003298, 0x1F2FF, 0x02}, /* Sdmmc2Dat1 */ - {0x0000329C, 0x1F2FF, 0x02}, /* Sdmmc2Dat2 */ - {0x000032A0, 0x1F2FF, 0x02}, /* Sdmmc2Dat3 */ - {0x000032A4, 0x1F2FF, 0x02}, /* Sdmmc2Dat4 */ - {0x000032A8, 0x1F2FF, 0x02}, /* Sdmmc2Dat5 */ - {0x000032AC, 0x1F2FF, 0x02}, /* Sdmmc2Dat6 */ - {0x000032B0, 0x1F2FF, 0x02}, /* Sdmmc2Dat7 */ - {0x000032B4, 0x1F2FF, 0x02}, /* Sdmmc2Clk */ - {0x000032B8, 0x1F2FF, 0x00}, /* Sdmmc2Clkb */ - {0x000032BC, 0x1F2FF, 0x02}, /* Sdmmc2Cmd */ - {0x000032C0, 0x1F2FF, 0x00}, /* Sdmmc2Dqs */ - {0x000032C4, 0x1F2FF, 0x00}, /* Sdmmc2Dqsb */ -}; - -constexpr u32 PadNameMax = util::size(Map); - -constexpr DrivePadDefinition DrivePadMap[] = { - {0x000008E4, 0x01F1F000}, /* AlsProxInt */ - {0x000008E8, 0x01F1F000}, /* ApReady */ - {0x000008EC, 0x01F1F000}, /* ApWakeBt */ - {0x000008F0, 0x01F1F000}, /* ApWakeNfc */ - {0x000008F4, 0x01F1F000}, /* AudMclk */ - {0x000008F8, 0x01F1F000}, /* BattBcl */ - {0x000008FC, 0x01F1F000}, /* BtRst */ - {0x00000900, 0x01F1F000}, /* BtWakeAp */ - {0x00000904, 0x01F1F000}, /* ButtonHome */ - {0x00000908, 0x01F1F000}, /* ButtonPowerOn */ - {0x0000090C, 0x01F1F000}, /* ButtonSlideSw */ - {0x00000910, 0x01F1F000}, /* ButtonVolDown */ - {0x00000914, 0x01F1F000}, /* ButtonVolUp */ - {0x00000918, 0x01F1F000}, /* Cam1Mclk */ - {0x0000091C, 0x01F1F000}, /* Cam1Pwdn */ - {0x00000920, 0x01F1F000}, /* Cam1Strobe */ - {0x00000924, 0x01F1F000}, /* Cam2Mclk */ - {0x00000928, 0x01F1F000}, /* Cam2Pwdn */ - {0x0000092C, 0x01F1F000}, /* CamAfEn */ - {0x00000930, 0x01F1F000}, /* CamFlashEn */ - {0x00000934, 0x01F1F000}, /* CamI2cScl */ - {0x00000938, 0x01F1F000}, /* CamI2cSda */ - {0x0000093C, 0x01F1F000}, /* CamRst */ - {0x00000940, 0x01F1F000}, /* Clk32kIn */ - {0x00000944, 0x01F1F000}, /* Clk32kOut */ - {0x00000948, 0x01F1F000}, /* ClkReq */ - {0x0000094C, 0x01F1F000}, /* CorePwrReq */ - {0x00000950, 0x01F1F000}, /* CpuPwrReq */ - {0x00000954, 0xF0000000}, /* Dap1Din */ - {0x00000958, 0xF0000000}, /* Dap1Dout */ - {0x0000095C, 0xF0000000}, /* Dap1Fs */ - {0x00000960, 0xF0000000}, /* Dap1Sclk */ - {0x00000964, 0xF0000000}, /* Dap2Din */ - {0x00000968, 0xF0000000}, /* Dap2Dout */ - {0x0000096C, 0xF0000000}, /* Dap2Fs */ - {0x00000970, 0xF0000000}, /* Dap2Sclk */ - {0x00000974, 0x01F1F000}, /* Dap4Din */ - {0x00000978, 0x01F1F000}, /* Dap4Dout */ - {0x0000097C, 0x01F1F000}, /* Dap4Fs */ - {0x00000980, 0x01F1F000}, /* Dap4Sclk */ - {0x00000984, 0x01F1F000}, /* Dmic1Clk */ - {0x00000988, 0x01F1F000}, /* Dmic1Dat */ - {0x0000098C, 0x01F1F000}, /* Dmic2Clk */ - {0x00000990, 0x01F1F000}, /* Dmic2Dat */ - {0x00000994, 0x01F1F000}, /* Dmic3Clk */ - {0x00000998, 0x01F1F000}, /* Dmic3Dat */ - {0x0000099C, 0x01F1F000}, /* DpHpd */ - {0x000009A0, 0x01F1F000}, /* DvfsClk */ - {0x000009A4, 0x01F1F000}, /* DvfsPwm */ - {0x000009A8, 0x01F1F000}, /* Gen1I2cScl */ - {0x000009AC, 0x01F1F000}, /* Gen1I2cSda */ - {0x000009B0, 0x01F1F000}, /* Gen2I2cScl */ - {0x000009B4, 0x01F1F000}, /* Gen2I2cSda */ - {0x000009B8, 0x01F1F000}, /* Gen3I2cScl */ - {0x000009BC, 0x01F1F000}, /* Gen3I2cSda */ - {0x000009C0, 0x01F1F000}, /* GpioPa6 */ - {0x000009C4, 0x01F1F000}, /* GpioPcc7 */ - {0x000009C8, 0x01F1F000}, /* GpioPe6 */ - {0x000009CC, 0x01F1F000}, /* GpioPe7 */ - {0x000009D0, 0x01F1F000}, /* GpioPh6 */ - {0x000009D4, 0xF0000000}, /* GpioPk0 */ - {0x000009D8, 0xF0000000}, /* GpioPk1 */ - {0x000009DC, 0xF0000000}, /* GpioPk2 */ - {0x000009E0, 0xF0000000}, /* GpioPk3 */ - {0x000009E4, 0xF0000000}, /* GpioPk4 */ - {0x000009E8, 0xF0000000}, /* GpioPk5 */ - {0x000009EC, 0xF0000000}, /* GpioPk6 */ - {0x000009F0, 0xF0000000}, /* GpioPk7 */ - {0x000009F4, 0xF0000000}, /* GpioPl0 */ - {0x000009F8, 0xF0000000}, /* GpioPl1 */ - {0x000009FC, 0x07F7F000}, /* GpioPz0 */ - {0x00000A00, 0x07F7F000}, /* GpioPz1 */ - {0x00000A04, 0x07F7F000}, /* GpioPz2 */ - {0x00000A08, 0x07F7F000}, /* GpioPz3 */ - {0x00000A0C, 0x07F7F000}, /* GpioPz4 */ - {0x00000A10, 0x07F7F000}, /* GpioPz5 */ - {0x00000A14, 0x01F1F000}, /* GpioX1Aud */ - {0x00000A18, 0x01F1F000}, /* GpioX3Aud */ - {0x00000A1C, 0x01F1F000}, /* GpsEn */ - {0x00000A20, 0x01F1F000}, /* GpsRst */ - {0x00000A24, 0x01F1F000}, /* HdmiCec */ - {0x00000A28, 0x01F1F000}, /* HdmiIntDpHpd */ - {0x00000A2C, 0x01F1F000}, /* JtagRtck */ - {0x00000A30, 0x01F1F000}, /* LcdBlEn */ - {0x00000A34, 0x01F1F000}, /* LcdBlPwm */ - {0x00000A38, 0x01F1F000}, /* LcdGpio1 */ - {0x00000A3C, 0x01F1F000}, /* LcdGpio2 */ - {0x00000A40, 0x01F1F000}, /* LcdRst */ - {0x00000A44, 0x01F1F000}, /* LcdTe */ - {0x00000A48, 0x01F1F000}, /* ModemWakeAp */ - {0x00000A4C, 0x01F1F000}, /* MotionInt */ - {0x00000A50, 0x01F1F000}, /* NfcEn */ - {0x00000A54, 0x01F1F000}, /* NfcInt */ - {0x00000A58, 0x01F1F000}, /* PexL0ClkReqN */ - {0x00000A5C, 0x01F1F000}, /* PexL0RstN */ - {0x00000A60, 0x01F1F000}, /* PexL1ClkreqN */ - {0x00000A64, 0x01F1F000}, /* PexL1RstN */ - {0x00000A68, 0x01F1F000}, /* PexWakeN */ - {0x00000A6C, 0x01F1F000}, /* PwrI2cScl */ - {0x00000A70, 0x01F1F000}, /* PwrI2cSda */ - {0x00000A74, 0x01F1F000}, /* PwrIntN */ - {0x00000A78, 0x07F7F000}, /* QspiComp */ - {0x00000A90, 0xF0000000}, /* QspiSck */ - {0x00000A94, 0x01F1F000}, /* SataLedActive */ - {0x00000A98, 0xF7F7F000}, /* Sdmmc1Pad */ - {0x00000AB0, 0xF7F7F000}, /* Sdmmc3Pad */ - {0x00000AC8, 0x01F1F000}, /* Shutdown */ - {0x00000ACC, 0x01F1F000}, /* SpdifIn */ - {0x00000AD0, 0x01F1F000}, /* SpdifOut */ - {0x00000AD4, 0xF0000000}, /* Spi1Cs0 */ - {0x00000AD8, 0xF0000000}, /* Spi1Cs1 */ - {0x00000ADC, 0xF0000000}, /* Spi1Miso */ - {0x00000AE0, 0xF0000000}, /* Spi1Mosi */ - {0x00000AE4, 0xF0000000}, /* Spi1Sck */ - {0x00000AE8, 0xF0000000}, /* Spi2Cs0 */ - {0x00000AEC, 0xF0000000}, /* Spi2Cs1 */ - {0x00000AF0, 0xF0000000}, /* Spi2Miso */ - {0x00000AF4, 0xF0000000}, /* Spi2Mosi */ - {0x00000AF8, 0xF0000000}, /* Spi2Sck */ - {0x00000AFC, 0xF0000000}, /* Spi4Cs0 */ - {0x00000B00, 0xF0000000}, /* Spi4Miso */ - {0x00000B04, 0xF0000000}, /* Spi4Mosi */ - {0x00000B08, 0xF0000000}, /* Spi4Sck */ - {0x00000B0C, 0x01F1F000}, /* TempAlert */ - {0x00000B10, 0x01F1F000}, /* TouchClk */ - {0x00000B14, 0x01F1F000}, /* TouchInt */ - {0x00000B18, 0x01F1F000}, /* TouchRst */ - {0x00000B1C, 0x01F1F000}, /* Uart1Cts */ - {0x00000B20, 0x01F1F000}, /* Uart1Rts */ - {0x00000B24, 0x01F1F000}, /* Uart1Rx */ - {0x00000B28, 0x01F1F000}, /* Uart1Tx */ - {0x00000B2C, 0x01F1F000}, /* Uart2Cts */ - {0x00000B30, 0x01F1F000}, /* Uart2Rts */ - {0x00000B34, 0x01F1F000}, /* Uart2Rx */ - {0x00000B38, 0x01F1F000}, /* Uart2Tx */ - {0x00000B3C, 0x01F1F000}, /* Uart3Cts */ - {0x00000B40, 0x01F1F000}, /* Uart3Rts */ - {0x00000B44, 0x01F1F000}, /* Uart3Rx */ - {0x00000B48, 0x01F1F000}, /* Uart3Tx */ - {0x00000B4C, 0x01F1F000}, /* Uart4Cts */ - {0x00000B50, 0x01F1F000}, /* Uart4Rts */ - {0x00000B54, 0x01F1F000}, /* Uart4Rx */ - {0x00000B58, 0x01F1F000}, /* Uart4Tx */ - {0x00000B5C, 0x01F1F000}, /* UsbVbusEn0 */ - {0x00000B60, 0x01F1F000}, /* UsbVbusEn1 */ - {0x00000B64, 0x01F1F000}, /* WifiEn */ - {0x00000B68, 0x01F1F000}, /* WifiRst */ - {0x00000B6C, 0x01F1F000}, /* WifiWakeAp */ -}; - -constexpr u32 DrivePadNameMax = util::size(DrivePadMap); diff --git a/stratosphere/boot/source/pinmux/pinmux_utils.cpp b/stratosphere/boot/source/pinmux/pinmux_utils.cpp deleted file mode 100644 index 17ce295f8..000000000 --- a/stratosphere/boot/source/pinmux/pinmux_utils.cpp +++ /dev/null @@ -1,523 +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 . - */ -#include -#include "pinmux_utils.hpp" - -namespace ams::pinmux { - - namespace { - - /* Pull in Pinmux map definitions. */ -#include "pinmux_map.inc" - - constexpr u32 ApbMiscPhysicalBase = 0x70000000; - - /* Globals. */ - bool g_initialized_pinmux_vaddr = false; - uintptr_t g_pinmux_vaddr = 0; - - /* Helpers. */ - inline const Definition *GetDefinition(u32 pinmux_name) { - AMS_ABORT_UNLESS(pinmux_name < PadNameMax); - return &Map[pinmux_name]; - } - - inline const DrivePadDefinition *GetDrivePadDefinition(u32 drivepad_name) { - AMS_ABORT_UNLESS(drivepad_name < DrivePadNameMax); - return &DrivePadMap[drivepad_name]; - } - - uintptr_t GetBaseAddress() { - if (!g_initialized_pinmux_vaddr) { - g_pinmux_vaddr = dd::GetIoMapping(ApbMiscPhysicalBase, 0x4000); - g_initialized_pinmux_vaddr = true; - } - return g_pinmux_vaddr; - } - - - - } - - u32 UpdatePark(u32 pinmux_name) { - const uintptr_t pinmux_base_vaddr = GetBaseAddress(); - const Definition *pinmux_def = GetDefinition(pinmux_name); - - /* Fetch this PINMUX's register offset */ - u32 pinmux_reg_offset = pinmux_def->reg_offset; - - /* Fetch this PINMUX's mask value */ - u32 pinmux_mask_val = pinmux_def->mask_val; - - /* Get current register ptr. */ - uintptr_t pinmux_reg = pinmux_base_vaddr + pinmux_reg_offset; - - /* Read from the PINMUX register */ - u32 pinmux_val = reg::Read(pinmux_reg); - - /* This PINMUX supports park change */ - if (pinmux_mask_val & 0x20) { - /* Clear park status if set */ - if (pinmux_val & 0x20) { - pinmux_val &= ~(0x20); - } - } - - /* Write to the appropriate PINMUX register */ - reg::Write(pinmux_reg, pinmux_val); - - /* Do a dummy read from the PINMUX register */ - pinmux_val = reg::Read(pinmux_reg); - - return pinmux_val; - } - - u32 UpdatePad(u32 pinmux_name, u32 pinmux_config_val, u32 pinmux_config_mask_val) { - const uintptr_t pinmux_base_vaddr = GetBaseAddress(); - const Definition *pinmux_def = GetDefinition(pinmux_name); - - /* Fetch this PINMUX's register offset */ - u32 pinmux_reg_offset = pinmux_def->reg_offset; - - /* Fetch this PINMUX's mask value */ - u32 pinmux_mask_val = pinmux_def->mask_val; - - /* Get current register ptr. */ - uintptr_t pinmux_reg = pinmux_base_vaddr + pinmux_reg_offset; - - /* Read from the PINMUX register */ - u32 pinmux_val = reg::Read(pinmux_reg); - - /* This PINMUX register is locked */ - AMS_ABORT_UNLESS((pinmux_val & 0x80) == 0); - - u32 pm_val = (pinmux_config_val & 0x07); - - /* Adjust PM */ - if (pinmux_config_mask_val & 0x07) { - /* Apply additional changes first */ - if (pm_val == 0x05) { - /* This pin supports PUPD change */ - if (pinmux_mask_val & 0x0C) { - /* Change PUPD */ - if ((pinmux_val & 0x0C) != 0x04) { - pinmux_val &= 0xFFFFFFF3; - pinmux_val |= 0x04; - } - } - - /* This pin supports Tristate change */ - if (pinmux_mask_val & 0x10) { - /* Change Tristate */ - if (!(pinmux_val & 0x10)) { - pinmux_val |= 0x10; - } - } - - /* This pin supports EInput change */ - if (pinmux_mask_val & 0x40) { - /* Change EInput */ - if (pinmux_val & 0x40) { - pinmux_val &= 0xFFFFFFBF; - } - } - } else if (pm_val >= 0x06) { - /* Default to safe value */ - pm_val = 0x04; - } - - /* Translate PM value if necessary */ - if (pm_val == 0x04 || pm_val == 0x05) { - pm_val = pinmux_def->pm_val; - } - - /* This pin supports PM change */ - if (pinmux_mask_val & 0x03) { - /* Change PM */ - if ((pinmux_val & 0x03) != (pm_val & 0x03)) { - pinmux_val &= 0xFFFFFFFC; - pinmux_val |= (pm_val & 0x03); - } - } - } - - u32 pupd_config_val = (pinmux_config_val & 0x18); - - /* Adjust PUPD */ - if (pinmux_config_mask_val & 0x18) { - if (pupd_config_val < 0x11) { - /* This pin supports PUPD change */ - if (pinmux_mask_val & 0x0C) { - /* Change PUPD */ - if (((pinmux_val >> 0x02) & 0x03) != (pupd_config_val >> 0x03)) { - pinmux_val &= 0xFFFFFFF3; - pinmux_val |= (pupd_config_val >> 0x01); - } - } - } - } - - u32 eod_config_val = (pinmux_config_val & 0x60); - - /* Adjust EOd field */ - if (pinmux_config_mask_val & 0x60) { - if (eod_config_val == 0x20) { - /* This pin supports Tristate change */ - if (pinmux_mask_val & 0x10) { - /* Change Tristate */ - if (!(pinmux_val & 0x10)) { - pinmux_val |= 0x10; - } - } - - /* This pin supports EInput change */ - if (pinmux_mask_val & 0x40) { - /* Change EInput */ - if (!(pinmux_val & 0x40)) { - pinmux_val |= 0x40; - } - } - - /* This pin supports EOd change */ - if (pinmux_mask_val & 0x800) { - /* Change EOd */ - if (pinmux_val & 0x800) { - pinmux_val &= 0xFFFFF7FF; - } - } - } else if (eod_config_val == 0x40) { - /* This pin supports Tristate change */ - if (pinmux_mask_val & 0x10) { - /* Change Tristate */ - if (pinmux_val & 0x10) { - pinmux_val &= 0xFFFFFFEF; - } - } - - /* This pin supports EInput change */ - if (pinmux_mask_val & 0x40) { - /* Change EInput */ - if (!(pinmux_val & 0x40)) { - pinmux_val |= 0x40; - } - } - - /* This pin supports EOd change */ - if (pinmux_mask_val & 0x800) { - /* Change EOd */ - if (pinmux_val & 0x800) { - pinmux_val &= 0xFFFFF7FF; - } - } - } else if (eod_config_val == 0x60) { - /* This pin supports Tristate change */ - if (pinmux_mask_val & 0x10) { - /* Change Tristate */ - if (pinmux_val & 0x10) { - pinmux_val &= 0xFFFFFFEF; - } - } - - /* This pin supports EInput change */ - if (pinmux_mask_val & 0x40) { - /* Change EInput */ - if (!(pinmux_val & 0x40)) { - pinmux_val |= 0x40; - } - } - - /* This pin supports EOd change */ - if (pinmux_mask_val & 0x800) { - /* Change EOd */ - if (!(pinmux_val & 0x800)) { - pinmux_val |= 0x800; - } - } - } else { - /* This pin supports Tristate change */ - if (pinmux_mask_val & 0x10) { - /* Change Tristate */ - if (pinmux_val & 0x10) { - pinmux_val &= 0xFFFFFFEF; - } - } - - /* This pin supports EInput change */ - if (pinmux_mask_val & 0x40) { - /* Change EInput */ - if (pinmux_val & 0x40) { - pinmux_val &= 0xFFFFFFBF; - } - } - - /* This pin supports EOd change */ - if (pinmux_mask_val & 0x800) { - /* Change EOd */ - if (pinmux_val & 0x800) { - pinmux_val &= 0xFFFFF7FF; - } - } - } - } - - u32 lock_config_val = (pinmux_config_val & 0x80); - - /* Adjust Lock */ - if (pinmux_config_mask_val & 0x80) { - /* This pin supports Lock change */ - if (pinmux_mask_val & 0x80) { - /* Change Lock */ - if ((pinmux_val ^ pinmux_config_val) & 0x80) { - pinmux_val &= 0xFFFFFF7F; - pinmux_val |= lock_config_val; - } - } - } - - u32 ioreset_config_val = (((pinmux_config_val >> 0x08) & 0x1) << 0x10); - - /* Adjust IoReset */ - if (pinmux_config_mask_val & 0x100) { - /* This pin supports IoReset change */ - if (pinmux_mask_val & 0x10000) { - /* Change IoReset */ - if (((pinmux_val >> 0x10) ^ (pinmux_config_val >> 0x08)) & 0x01) { - pinmux_val &= 0xFFFEFFFF; - pinmux_val |= ioreset_config_val; - } - } - } - - u32 park_config_val = (((pinmux_config_val >> 0x0A) & 0x1) << 0x5); - - /* Adjust Park */ - if (pinmux_config_mask_val & 0x400) { - /* This pin supports Park change */ - if (pinmux_mask_val & 0x20) { - /* Change Park */ - if (((pinmux_val >> 0x05) ^ (pinmux_config_val >> 0x0A)) & 0x01) { - pinmux_val &= 0xFFFFFFDF; - pinmux_val |= park_config_val; - } - } - } - - u32 elpdr_config_val = (((pinmux_config_val >> 0x0B) & 0x1) << 0x08); - - /* Adjust ELpdr */ - if (pinmux_config_mask_val & 0x800) { - /* This pin supports ELpdr change */ - if (pinmux_mask_val & 0x100) { - /* Change ELpdr */ - if (((pinmux_val >> 0x08) ^ (pinmux_config_val >> 0x0B)) & 0x01) { - pinmux_val &= 0xFFFFFEFF; - pinmux_val |= elpdr_config_val; - } - } - } - - u32 ehsm_config_val = (((pinmux_config_val >> 0x0C) & 0x1) << 0x09); - - /* Adjust EHsm */ - if (pinmux_config_mask_val & 0x1000) { - /* This pin supports EHsm change */ - if (pinmux_mask_val & 0x200) { - /* Change EHsm */ - if (((pinmux_val >> 0x09) ^ (pinmux_config_val >> 0x0C)) & 0x01) { - pinmux_val &= 0xFFFFFDFF; - pinmux_val |= ehsm_config_val; - } - } - } - - u32 eiohv_config_val = (((pinmux_config_val >> 0x09) & 0x1) << 0x0A); - - /* Adjust EIoHv */ - if (pinmux_config_mask_val & 0x200) { - /* This pin supports EIoHv change */ - if (pinmux_mask_val & 0x400) { - /* Change EIoHv */ - if (((pinmux_val >> 0x0A) ^ (pinmux_config_val >> 0x09)) & 0x01) { - pinmux_val &= 0xFFFFFBFF; - pinmux_val |= eiohv_config_val; - } - } - } - - u32 eschmt_config_val = (((pinmux_config_val >> 0x0D) & 0x1) << 0x0C); - - /* Adjust ESchmt */ - if (pinmux_config_mask_val & 0x2000) { - /* This pin supports ESchmt change */ - if (pinmux_mask_val & 0x1000) { - /* Change ESchmt */ - if (((pinmux_val >> 0x0C) ^ (pinmux_config_val >> 0x0D)) & 0x01) { - pinmux_val &= 0xFFFFEFFF; - pinmux_val |= eschmt_config_val; - } - } - } - - u32 preemp_config_val = (((pinmux_config_val >> 0x10) & 0x1) << 0xF); - - /* Adjust Preemp */ - if (pinmux_config_mask_val & 0x10000) { - /* This pin supports Preemp change */ - if (pinmux_mask_val & 0x8000) { - /* Change Preemp */ - if (((pinmux_val >> 0x0F) ^ (pinmux_config_val >> 0x10)) & 0x01) { - pinmux_val &= 0xFFFF7FFF; - pinmux_val |= preemp_config_val; - } - } - } - - u32 drvtype_config_val = (((pinmux_config_val >> 0x0E) & 0x3) << 0xD); - - /* Adjust DrvType */ - if (pinmux_config_mask_val & 0xC000) { - /* This pin supports DrvType change */ - if (pinmux_mask_val & 0x6000) { - /* Change DrvType */ - if (((pinmux_val >> 0x0D) ^ (pinmux_config_val >> 0x0E)) & 0x03) { - pinmux_val &= 0xFFFF9FFF; - pinmux_val |= drvtype_config_val; - } - } - } - - /* Write to the appropriate PINMUX register */ - reg::Write(pinmux_reg, pinmux_val); - - /* Do a dummy read from the PINMUX register */ - pinmux_val = reg::Read(pinmux_reg); - - return pinmux_val; - } - - u32 UpdateDrivePad(u32 pinmux_drivepad_name, u32 pinmux_drivepad_config_val, u32 pinmux_drivepad_config_mask_val) { - const uintptr_t pinmux_base_vaddr = GetBaseAddress(); - const DrivePadDefinition *pinmux_drivepad_def = GetDrivePadDefinition(pinmux_drivepad_name); - - /* Fetch this PINMUX drive group's register offset */ - u32 pinmux_drivepad_reg_offset = pinmux_drivepad_def->reg_offset; - - /* Fetch this PINMUX drive group's mask value */ - u32 pinmux_drivepad_mask_val = pinmux_drivepad_def->mask_val; - - /* Get current register ptr. */ - uintptr_t pinmux_drivepad_reg = pinmux_base_vaddr + pinmux_drivepad_reg_offset; - - /* Read from the PINMUX drive group register */ - u32 pinmux_drivepad_val = reg::Read(pinmux_drivepad_reg); - - /* Adjust DriveDownStrength */ - if (pinmux_drivepad_config_mask_val & 0x1F000) { - u32 mask_val = 0x7F000; - - /* Adjust mask value */ - if ((pinmux_drivepad_mask_val & 0x7F000) != 0x7F000) - mask_val = 0x1F000; - - /* This drive group supports DriveDownStrength change */ - if (pinmux_drivepad_mask_val & mask_val) { - /* Change DriveDownStrength */ - if (((pinmux_drivepad_config_val & 0x7F000) & mask_val) != (pinmux_drivepad_val & mask_val)) { - pinmux_drivepad_val &= ~(mask_val); - pinmux_drivepad_val |= ((pinmux_drivepad_config_val & 0x7F000) & mask_val); - } - } - } - - /* Adjust DriveUpStrength */ - if (pinmux_drivepad_config_mask_val & 0x1F00000) { - u32 mask_val = 0x7F00000; - - /* Adjust mask value */ - if ((pinmux_drivepad_mask_val & 0x7F00000) != 0x7F00000) - mask_val = 0x1F00000; - - /* This drive group supports DriveUpStrength change */ - if (pinmux_drivepad_mask_val & mask_val) { - /* Change DriveUpStrength */ - if (((pinmux_drivepad_config_val & 0x7F00000) & mask_val) != (pinmux_drivepad_val & mask_val)) { - pinmux_drivepad_val &= ~(mask_val); - pinmux_drivepad_val |= ((pinmux_drivepad_config_val & 0x7F00000) & mask_val); - } - } - } - - /* Adjust DriveDownSlew */ - if (pinmux_drivepad_config_mask_val & 0x30000000) { - /* This drive group supports DriveDownSlew change */ - if (pinmux_drivepad_mask_val & 0x30000000) { - /* Change DriveDownSlew */ - if ((pinmux_drivepad_val ^ pinmux_drivepad_config_val) & 0x30000000) { - pinmux_drivepad_val &= 0xCFFFFFFF; - pinmux_drivepad_val |= (pinmux_drivepad_config_val & 0x30000000); - } - } - } - - /* Adjust DriveUpSlew */ - if (pinmux_drivepad_config_mask_val & 0xC0000000) { - /* This drive group supports DriveUpSlew change */ - if (pinmux_drivepad_mask_val & 0xC0000000) { - /* Change DriveUpSlew */ - if ((pinmux_drivepad_val ^ pinmux_drivepad_config_val) & 0xC0000000) { - pinmux_drivepad_val &= 0x3FFFFFFF; - pinmux_drivepad_val |= (pinmux_drivepad_config_val & 0xC0000000); - } - } - } - - /* Write to the appropriate PINMUX drive group register */ - reg::Write(pinmux_drivepad_reg, pinmux_drivepad_val); - - /* Do a dummy read from the PINMUX drive group register */ - pinmux_drivepad_val = reg::Read(pinmux_drivepad_reg); - - return pinmux_drivepad_val; - } - - u32 DummyReadDrivePad(u32 pinmux_drivepad_name) { - const uintptr_t pinmux_base_vaddr = GetBaseAddress(); - const DrivePadDefinition *pinmux_drivepad_def = GetDrivePadDefinition(pinmux_drivepad_name); - - /* Fetch this PINMUX drive group's register offset */ - u32 pinmux_drivepad_reg_offset = pinmux_drivepad_def->reg_offset; - - /* Get current register ptr. */ - uintptr_t pinmux_drivepad_reg = pinmux_base_vaddr + pinmux_drivepad_reg_offset; - - return reg::Read(pinmux_drivepad_reg); - } - - void UpdateAllParks() { - /* Update parks. */ - for (size_t i = 0; i < PadNameMax; i++) { - UpdatePark(static_cast(i)); - } - } - - void DummyReadAllDrivePads() { - /* Dummy read all drive pads. */ - for (size_t i = 0; i < DrivePadNameMax; i++) { - DummyReadDrivePad(static_cast(i)); - } - } - -} diff --git a/stratosphere/boot2/Makefile b/stratosphere/boot2/Makefile index 7320b9f99..aab86a63a 100644 --- a/stratosphere/boot2/Makefile +++ b/stratosphere/boot2/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/boot2/source/boot2_main.cpp b/stratosphere/boot2/source/boot2_main.cpp index b7e7d4570..a7e196b84 100644 --- a/stratosphere/boot2/source/boot2_main.cpp +++ b/stratosphere/boot2/source/boot2_main.cpp @@ -75,7 +75,7 @@ void __appInit(void) { R_ABORT_UNLESS(pminfoInitialize()); R_ABORT_UNLESS(pmshellInitialize()); R_ABORT_UNLESS(setsysInitialize()); - R_ABORT_UNLESS(gpioInitialize()); + gpio::Initialize(); }); /* Mount the SD card. */ @@ -86,7 +86,7 @@ void __appInit(void) { void __appExit(void) { fs::Unmount("sdmc"); - gpioExit(); + gpio::Finalize(); setsysExit(); pmshellExit(); pminfoExit(); diff --git a/stratosphere/creport/Makefile b/stratosphere/creport/Makefile index 7320b9f99..aab86a63a 100644 --- a/stratosphere/creport/Makefile +++ b/stratosphere/creport/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index 01a468385..9b1b93517 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -351,6 +351,10 @@ namespace ams::creport { void CrashReport::SaveToFile(ScopedFile &file) { file.WriteFormat("Atmosphère Crash Report (v1.5):\n"); + + /* TODO: Remove in Atmosphere 1.0.0. */ + file.WriteFormat("Mesosphere: %s\n", svc::IsKernelMesosphere() ? "Enabled" : "Disabled"); + file.WriteFormat("Result: 0x%X (2%03d-%04d)\n\n", this->result.GetValue(), this->result.GetModule(), this->result.GetDescription()); /* Process Info. */ diff --git a/stratosphere/dmnt/Makefile b/stratosphere/dmnt/Makefile index 7320b9f99..aab86a63a 100644 --- a/stratosphere/dmnt/Makefile +++ b/stratosphere/dmnt/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/eclct.stub/Makefile b/stratosphere/eclct.stub/Makefile index 7320b9f99..aab86a63a 100644 --- a/stratosphere/eclct.stub/Makefile +++ b/stratosphere/eclct.stub/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/erpt/Makefile b/stratosphere/erpt/Makefile index 7320b9f99..aab86a63a 100644 --- a/stratosphere/erpt/Makefile +++ b/stratosphere/erpt/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/fatal/Makefile b/stratosphere/fatal/Makefile index 7320b9f99..aab86a63a 100644 --- a/stratosphere/fatal/Makefile +++ b/stratosphere/fatal/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/fatal/source/fatal_config.cpp b/stratosphere/fatal/source/fatal_config.cpp index 38889fb2e..8b144a4be 100644 --- a/stratosphere/fatal/source/fatal_config.cpp +++ b/stratosphere/fatal/source/fatal_config.cpp @@ -57,9 +57,6 @@ namespace ams::fatal::srv { } FatalConfig::FatalConfig() { - /* Clear this. */ - std::memset(this, 0, sizeof(*this)); - /* Get information from set. */ settings::system::GetSerialNumber(std::addressof(this->serial_number)); settings::system::GetFirmwareVersion(std::addressof(this->firmware_version)); @@ -69,11 +66,16 @@ namespace ams::fatal::srv { /* Read information from settings. */ settings::fwdbg::GetSettingsItemValue(&this->transition_to_fatal, sizeof(this->transition_to_fatal), "fatal", "transition_to_fatal"); settings::fwdbg::GetSettingsItemValue(&this->show_extra_info, sizeof(this->show_extra_info), "fatal", "show_extra_info"); - settings::fwdbg::GetSettingsItemValue(&this->quest_reboot_interval_second, sizeof(this->quest_reboot_interval_second), "fatal", "quest_reboot_interval_second"); + + u64 quest_interval_second; + settings::fwdbg::GetSettingsItemValue(&quest_interval_second, sizeof(quest_interval_second), "fatal", "quest_reboot_interval_second"); + this->quest_reboot_interval = TimeSpan::FromSeconds(quest_interval_second); /* Atmosphere extension for automatic reboot. */ - if (settings::fwdbg::GetSettingsItemValue(&this->fatal_auto_reboot_interval, sizeof(this->fatal_auto_reboot_interval), "atmosphere", "fatal_auto_reboot_interval") == sizeof(this->fatal_auto_reboot_interval)) { - this->fatal_auto_reboot_enabled = this->fatal_auto_reboot_interval != 0; + u64 auto_reboot_ms; + if (settings::fwdbg::GetSettingsItemValue(&auto_reboot_ms, sizeof(auto_reboot_ms), "atmosphere", "fatal_auto_reboot_interval") == sizeof(auto_reboot_ms)) { + this->fatal_auto_reboot_interval = TimeSpan::FromMilliSeconds(auto_reboot_ms); + this->fatal_auto_reboot_enabled = auto_reboot_ms != 0; } /* Setup messages. */ diff --git a/stratosphere/fatal/source/fatal_config.hpp b/stratosphere/fatal/source/fatal_config.hpp index b34e79525..2e0941c68 100644 --- a/stratosphere/fatal/source/fatal_config.hpp +++ b/stratosphere/fatal/source/fatal_config.hpp @@ -20,18 +20,18 @@ namespace ams::fatal::srv { class FatalConfig { private: - settings::system::SerialNumber serial_number; - settings::system::FirmwareVersion firmware_version; - u64 language_code; - u64 quest_reboot_interval_second; - bool transition_to_fatal; - bool show_extra_info; - bool quest_flag; - const char *error_msg; - const char *error_desc; - const char *quest_desc; - u64 fatal_auto_reboot_interval; - bool fatal_auto_reboot_enabled; + settings::system::SerialNumber serial_number{}; + settings::system::FirmwareVersion firmware_version{}; + u64 language_code{}; + TimeSpan quest_reboot_interval{}; + bool transition_to_fatal{}; + bool show_extra_info{}; + bool quest_flag{}; + const char *error_msg{}; + const char *error_desc{}; + const char *quest_desc{}; + TimeSpan fatal_auto_reboot_interval{}; + bool fatal_auto_reboot_enabled{}; public: FatalConfig(); @@ -67,12 +67,12 @@ namespace ams::fatal::srv { return this->fatal_auto_reboot_enabled; } - u64 GetQuestRebootTimeoutInterval() const { - return this->quest_reboot_interval_second * 1'000'000'000ul; + TimeSpan GetQuestRebootTimeoutInterval() const { + return this->quest_reboot_interval; } - u64 GetFatalRebootTimeoutInterval() const { - return this->fatal_auto_reboot_interval * 1'000'000ul; + TimeSpan GetFatalRebootTimeoutInterval() const { + return this->fatal_auto_reboot_interval; } const char *GetErrorMessage() const { diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index 3b576347b..09e866241 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -92,7 +92,7 @@ void __appInit(void) { R_ABORT_UNLESS(psmInitialize()); R_ABORT_UNLESS(spsmInitialize()); R_ABORT_UNLESS(plInitialize(::PlServiceType_User)); - R_ABORT_UNLESS(gpioInitialize()); + gpio::Initialize(); R_ABORT_UNLESS(fsInitialize()); }); @@ -105,7 +105,7 @@ void __appExit(void) { /* Cleanup services. */ fsExit(); plExit(); - gpioExit(); + gpio::Finalize(); spsmExit(); psmExit(); lblExit(); diff --git a/stratosphere/fatal/source/fatal_repair.cpp b/stratosphere/fatal/source/fatal_repair.cpp index bc57fe698..77e3685d4 100644 --- a/stratosphere/fatal/source/fatal_repair.cpp +++ b/stratosphere/fatal/source/fatal_repair.cpp @@ -33,22 +33,21 @@ namespace ams::fatal::srv { bool IsInRepairWithoutVolHeld() { if (IsInRepair()) { - GpioPadSession vol_btn; - if (R_FAILED(gpioOpenSession(&vol_btn, GpioPadName_ButtonVolUp))) { + gpio::GpioPadSession vol_btn; + if (R_FAILED(gpio::OpenSession(std::addressof(vol_btn), gpio::DeviceCode_ButtonVolUp))) { return true; } /* Ensure we close even on early return. */ - ON_SCOPE_EXIT { gpioPadClose(&vol_btn); }; + ON_SCOPE_EXIT { gpio::CloseSession(std::addressof(vol_btn)); }; /* Set direction input. */ - gpioPadSetDirection(&vol_btn, GpioDirection_Input); + gpio::SetDirection(std::addressof(vol_btn), gpio::Direction_Input); /* Ensure that we're holding the volume button for a full second. */ auto start = os::GetSystemTick(); do { - GpioValue val; - if (R_FAILED(gpioPadGetValue(&vol_btn, &val)) || val != GpioValue_Low) { + if (gpio::GetValue(std::addressof(vol_btn)) != gpio::GpioValue_Low) { return true; } diff --git a/stratosphere/fatal/source/fatal_task_power.cpp b/stratosphere/fatal/source/fatal_task_power.cpp index 69d84ef46..5f9acaefe 100644 --- a/stratosphere/fatal/source/fatal_task_power.cpp +++ b/stratosphere/fatal/source/fatal_task_power.cpp @@ -54,16 +54,16 @@ namespace ams::fatal::srv { class RebootTimingObserver { private: os::Tick start_tick; + TimeSpan interval; bool flag; - s32 interval; public: - RebootTimingObserver(bool flag, s32 interval) : start_tick(os::GetSystemTick()), flag(flag), interval(interval) { + RebootTimingObserver(bool flag, TimeSpan iv) : start_tick(os::GetSystemTick()), interval(iv), flag(flag) { /* ... */ } bool IsRebootTiming() const { auto current_tick = os::GetSystemTick(); - return this->flag && (current_tick - this->start_tick).ToTimeSpan().GetSeconds() >= this->interval; + return this->flag && (current_tick - this->start_tick).ToTimeSpan() >= this->interval; } }; @@ -75,7 +75,7 @@ namespace ams::fatal::srv { /* Task Implementations. */ bool PowerControlTask::TryShutdown() { /* Set a timeout of 30 seconds. */ - constexpr s32 MaxShutdownWaitSeconds = 30; + constexpr auto MaxShutdownWaitInterval = TimeSpan::FromSeconds(30); auto start_tick = os::GetSystemTick(); @@ -84,7 +84,7 @@ namespace ams::fatal::srv { while (true) { auto cur_tick = os::GetSystemTick(); - if ((cur_tick - start_tick).ToTimeSpan().GetSeconds() > MaxShutdownWaitSeconds) { + if ((cur_tick - start_tick).ToTimeSpan() > MaxShutdownWaitInterval) { break; } @@ -156,33 +156,33 @@ namespace ams::fatal::srv { RebootTimingObserver fatal_reboot_helper(config.IsFatalRebootEnabled(), config.GetFatalRebootTimeoutInterval()); bool check_vol_up = true, check_vol_down = true; - GpioPadSession vol_up_btn, vol_down_btn; - if (R_FAILED(gpioOpenSession(&vol_up_btn, GpioPadName_ButtonVolUp))) { + gpio::GpioPadSession vol_up_btn, vol_down_btn; + if (R_FAILED(gpio::OpenSession(std::addressof(vol_up_btn), gpio::DeviceCode_ButtonVolUp))) { check_vol_up = false; } - if (R_FAILED(gpioOpenSession(&vol_down_btn, GpioPadName_ButtonVolDown))) { + if (R_FAILED(gpio::OpenSession(std::addressof(vol_down_btn), gpio::DeviceCode_ButtonVolDn))) { check_vol_down = false; } /* Ensure we close on early return. */ - ON_SCOPE_EXIT { if (check_vol_up) { gpioPadClose(&vol_up_btn); } }; - ON_SCOPE_EXIT { if (check_vol_down) { gpioPadClose(&vol_down_btn); } }; + ON_SCOPE_EXIT { if (check_vol_up) { gpio::CloseSession(std::addressof(vol_up_btn)); } }; + ON_SCOPE_EXIT { if (check_vol_down) { gpio::CloseSession(std::addressof(vol_down_btn)); } }; /* Set direction input. */ if (check_vol_up) { - gpioPadSetDirection(&vol_up_btn, GpioDirection_Input); + gpio::SetDirection(std::addressof(vol_up_btn), gpio::Direction_Input); } if (check_vol_down) { - gpioPadSetDirection(&vol_down_btn, GpioDirection_Input); + gpio::SetDirection(std::addressof(vol_down_btn), gpio::Direction_Input); } BpcSleepButtonState state; - GpioValue val; while (true) { if (fatal_reboot_helper.IsRebootTiming() || (quest_reboot_helper.IsRebootTiming()) || - (check_vol_up && R_SUCCEEDED(gpioPadGetValue(&vol_up_btn, &val)) && val == GpioValue_Low) || - (check_vol_down && R_SUCCEEDED(gpioPadGetValue(&vol_down_btn, &val)) && val == GpioValue_Low) || - (R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held)) { + (check_vol_up && gpio::GetValue(std::addressof(vol_up_btn)) == gpio::GpioValue_Low) || + (check_vol_down && gpio::GetValue(std::addressof(vol_down_btn)) == gpio::GpioValue_Low) || + (R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held)) + { /* If any of the above conditions succeeded, we should reboot. */ bpcRebootSystem(); return; diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index 327904f9d..1e6730cfa 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -214,7 +214,9 @@ namespace ams::fatal::srv { font::AddSpacingLines(0.5f); font::PrintFormatLine( "Program: %016lX", static_cast(this->context->program_id)); font::AddSpacingLines(0.5f); - font::PrintFormatLine("Firmware: %s (Atmosphère %u.%u.%u-%s)", config.GetFirmwareVersion().display_version, ATMOSPHERE_RELEASE_VERSION, ams::GetGitRevision()); + + /* TODO: Remove Mesosphere identifier in 1.0.0. */ + font::PrintFormatLine("Firmware: %s (Atmosphère%s %u.%u.%u-%s)", config.GetFirmwareVersion().display_version, svc::IsKernelMesosphere() ? " M" : "", ATMOSPHERE_RELEASE_VERSION, ams::GetGitRevision()); font::AddSpacingLines(1.5f); if (!exosphere::ResultVersionMismatch::Includes(this->context->result)) { font::Print(config.GetErrorDescription()); diff --git a/stratosphere/fatal/source/fatal_task_sound.cpp b/stratosphere/fatal/source/fatal_task_sound.cpp index cc235b7d5..ad5d6eb59 100644 --- a/stratosphere/fatal/source/fatal_task_sound.cpp +++ b/stratosphere/fatal/source/fatal_task_sound.cpp @@ -70,16 +70,16 @@ namespace ams::fatal::srv { /* Talk to the ALC5639 over GPIO, and disable audio output */ { - GpioPadSession audio; - if (R_SUCCEEDED(gpioOpenSession(&audio, GpioPadName_AudioCodec))) { - ON_SCOPE_EXIT { gpioPadClose(&audio); }; + gpio::GpioPadSession audio; + if (R_SUCCEEDED(gpio::OpenSession(std::addressof(audio), gpio::DeviceCode_CodecLdoEnTemp))) { + ON_SCOPE_EXIT { gpio::CloseSession(std::addressof(audio)); }; /* Set direction output, sleep 200 ms so it can take effect. */ - gpioPadSetDirection(&audio, GpioDirection_Output); - svcSleepThread(200000000UL); + gpio::SetDirection(std::addressof(audio), gpio::Direction_Output); + os::SleepThread(TimeSpan::FromMilliSeconds(200)); /* Pull audio codec low. */ - gpioPadSetValue(&audio, GpioValue_Low); + gpio::SetValue(std::addressof(audio), gpio::GpioValue_Low); } } } diff --git a/stratosphere/jpegdec/Makefile b/stratosphere/jpegdec/Makefile index 9ddacd7e6..b590eef9e 100644 --- a/stratosphere/jpegdec/Makefile +++ b/stratosphere/jpegdec/Makefile @@ -23,23 +23,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/loader/Makefile b/stratosphere/loader/Makefile index a92187ef8..1957b6cdb 100644 --- a/stratosphere/loader/Makefile +++ b/stratosphere/loader/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/loader/source/ldr_capabilities.cpp b/stratosphere/loader/source/ldr_capabilities.cpp index 6c170863c..6b0600a8c 100644 --- a/stratosphere/loader/source/ldr_capabilities.cpp +++ b/stratosphere/loader/source/ldr_capabilities.cpp @@ -26,6 +26,7 @@ namespace ams::ldr::caps { SyscallMask = 4, MapRange = 6, MapPage = 7, + MapRegion = 10, InterruptPair = 11, ApplicationType = 13, KernelVersion = 14, @@ -46,6 +47,9 @@ namespace ams::ldr::caps { return static_cast(__builtin_ctz(~cap.value)); } + constexpr inline util::BitPack32 EmptyCapability = {~u32{}}; + static_assert(GetCapabilityId(EmptyCapability) == CapabilityId::Empty); + #define CAPABILITY_CLASS_NAME(id) Capability##id #define DEFINE_CAPABILITY_CLASS(id, member_functions) \ @@ -181,6 +185,35 @@ namespace ams::ldr::caps { } ); + enum class MemoryRegionType : u32 { + None = 0, + KernelTraceBuffer = 1, + OnMemoryBootImage = 2, + DTB = 3, + }; + + DEFINE_CAPABILITY_CLASS(MapRegion, + DEFINE_CAPABILITY_FIELD(Region0, IdBits, 6, MemoryRegionType); + DEFINE_CAPABILITY_FIELD(ReadOnly0, Region0, 1, bool); + DEFINE_CAPABILITY_FIELD(Region1, ReadOnly0, 6, MemoryRegionType); + DEFINE_CAPABILITY_FIELD(ReadOnly1, Region1, 1, bool); + DEFINE_CAPABILITY_FIELD(Region2, ReadOnly1, 6, MemoryRegionType); + DEFINE_CAPABILITY_FIELD(ReadOnly2, Region2, 1, bool); + + bool IsValid(const util::BitPack32 *kac, size_t kac_count) const { + for (size_t i = 0; i < kac_count; i++) { + if (GetCapabilityId(kac[i]) == Id) { + const auto restriction = Decode(kac[i]); + + if (this->GetValue() == restriction.GetValue()) { + return true; + } + } + } + return false; + } + ); + DEFINE_CAPABILITY_CLASS(InterruptPair, DEFINE_CAPABILITY_FIELD(InterruptId0, IdBits, 10); DEFINE_CAPABILITY_FIELD(InterruptId1, InterruptId0, 10); @@ -304,6 +337,7 @@ namespace ams::ldr::caps { VALIDATE_CASE(KernelFlags); VALIDATE_CASE(SyscallMask); VALIDATE_CASE(MapPage); + VALIDATE_CASE(MapRegion); VALIDATE_CASE(InterruptPair); VALIDATE_CASE(ApplicationType); VALIDATE_CASE(KernelVersion); @@ -376,4 +410,28 @@ namespace ams::ldr::caps { } } + void ProcessCapabilities(void *kac, size_t kac_size) { + util::BitPack32 *caps = reinterpret_cast(kac); + const size_t num_caps = kac_size / sizeof(*caps); + + for (size_t i = 0; i < num_caps; i++) { + const auto cur_cap = caps[i]; + switch (GetCapabilityId(cur_cap)) { + case CapabilityId::MapRegion: + { + /* MapRegion was added in 8.0.0+. */ + /* To prevent kernel error, we should reject the descriptor on lower firmwares. */ + /* NOTE: We also allow it on any firmware under mesosphere, as an extension. */ + const bool is_allowed = (hos::GetVersion() >= hos::Version_8_0_0 || svc::IsKernelMesosphere()); + if (!is_allowed) { + caps[i] = EmptyCapability; + } + } + break; + default: + break; + } + } + } + } diff --git a/stratosphere/loader/source/ldr_capabilities.hpp b/stratosphere/loader/source/ldr_capabilities.hpp index 07ae0e9c9..acaa9873f 100644 --- a/stratosphere/loader/source/ldr_capabilities.hpp +++ b/stratosphere/loader/source/ldr_capabilities.hpp @@ -23,4 +23,6 @@ namespace ams::ldr::caps { u16 GetProgramInfoFlags(const void *kac, size_t kac_size); void SetProgramInfoFlags(u16 flags, void *kac, size_t kac_size); + void ProcessCapabilities(void *kac, size_t kac_size); + } diff --git a/stratosphere/loader/source/ldr_content_management.cpp b/stratosphere/loader/source/ldr_content_management.cpp index 9a622d639..46c3d6a51 100644 --- a/stratosphere/loader/source/ldr_content_management.cpp +++ b/stratosphere/loader/source/ldr_content_management.cpp @@ -58,15 +58,15 @@ namespace ams::ldr { } /* Mount the atmosphere code file system. */ - R_TRY(fs::MountCodeForAtmosphereWithRedirection(std::addressof(this->ams_code_info), AtmosphereCodeMountName, content_path, loc.program_id, this->override_status.IsHbl(), this->override_status.IsProgramSpecific())); + R_TRY(fs::MountCodeForAtmosphereWithRedirection(std::addressof(this->ams_code_verification_data), AtmosphereCodeMountName, content_path, loc.program_id, this->override_status.IsHbl(), this->override_status.IsProgramSpecific())); this->mounted_ams = true; /* Mount the sd or base code file system. */ - R_TRY(fs::MountCodeForAtmosphere(std::addressof(this->sd_or_base_code_info), SdOrCodeMountName, content_path, loc.program_id)); + R_TRY(fs::MountCodeForAtmosphere(std::addressof(this->sd_or_base_code_verification_data), SdOrCodeMountName, content_path, loc.program_id)); this->mounted_sd_or_code = true; /* Mount the base code file system. */ - if (R_SUCCEEDED(fs::MountCode(std::addressof(this->base_code_info), CodeMountName, content_path, loc.program_id))) { + if (R_SUCCEEDED(fs::MountCode(std::addressof(this->base_code_verification_data), CodeMountName, content_path, loc.program_id))) { this->mounted_code = true; } diff --git a/stratosphere/loader/source/ldr_content_management.hpp b/stratosphere/loader/source/ldr_content_management.hpp index a1972d75d..c06ce220c 100644 --- a/stratosphere/loader/source/ldr_content_management.hpp +++ b/stratosphere/loader/source/ldr_content_management.hpp @@ -25,9 +25,9 @@ namespace ams::ldr { private: std::scoped_lock lk; cfg::OverrideStatus override_status; - fs::CodeInfo ams_code_info; - fs::CodeInfo sd_or_base_code_info; - fs::CodeInfo base_code_info; + fs::CodeVerificationData ams_code_verification_data; + fs::CodeVerificationData sd_or_base_code_verification_data; + fs::CodeVerificationData base_code_verification_data; Result result; bool has_status; bool mounted_ams; @@ -47,16 +47,16 @@ namespace ams::ldr { return this->override_status; } - const fs::CodeInfo &GetAtmosphereCodeInfo() const { - return this->ams_code_info; + const fs::CodeVerificationData &GetAtmosphereCodeVerificationData() const { + return this->ams_code_verification_data; } - const fs::CodeInfo &GetSdOrBaseCodeInfo() const { - return this->sd_or_base_code_info; + const fs::CodeVerificationData &GetSdOrBaseCodeVerificationData() const { + return this->sd_or_base_code_verification_data; } - const fs::CodeInfo &GetCodeInfo() const { - return this->base_code_info; + const fs::CodeVerificationData &GetCodeVerificationData() const { + return this->base_code_verification_data; } private: Result Initialize(const ncm::ProgramLocation &loc); diff --git a/stratosphere/loader/source/ldr_meta.cpp b/stratosphere/loader/source/ldr_meta.cpp index 75c7ac666..fb46f49d6 100644 --- a/stratosphere/loader/source/ldr_meta.cpp +++ b/stratosphere/loader/source/ldr_meta.cpp @@ -107,7 +107,7 @@ namespace ams::ldr { Result ValidateAcidSignature(Meta *meta) { /* Loader did not check signatures prior to 10.0.0. */ if (hos::GetVersion() < hos::Version_10_0_0) { - meta->is_signed = false; + meta->check_verification_data = false; return ResultSuccess(); } @@ -123,7 +123,7 @@ namespace ams::ldr { const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size); R_UNLESS(is_signature_valid || !IsEnabledProgramVerification(), ResultInvalidAcidSignature()); - meta->is_signed = is_signature_valid; + meta->check_verification_data = is_signature_valid; return ResultSuccess(); } @@ -198,6 +198,8 @@ namespace ams::ldr { if (status.IsHbl()) { if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), SdOrBaseMetaPath, fs::OpenMode_Read))) { ON_SCOPE_EXIT { fs::CloseFile(file); }; + + if (R_SUCCEEDED(LoadMetaFromFile(file, &g_original_meta_cache))) { Meta *o_meta = &g_original_meta_cache.meta; @@ -212,6 +214,15 @@ namespace ams::ldr { caps::SetProgramInfoFlags(program_info_flags, meta->aci_kac, meta->aci->kac_size); } } + + /* When hbl is applet, adjust main thread priority. */ + if ((caps::GetProgramInfoFlags(meta->aci_kac, meta->aci->kac_size) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet) { + constexpr auto HblMainThreadPriorityApplication = 44; + constexpr auto HblMainThreadPriorityApplet = 40; + if (meta->npdm->main_thread_priority == HblMainThreadPriorityApplication) { + meta->npdm->main_thread_priority = HblMainThreadPriorityApplet; + } + } } else if (hos::GetVersion() >= hos::Version_10_0_0) { /* If storage id is none, there is no base code filesystem, and thus it is impossible for us to validate. */ /* However, if we're an application, we are guaranteed a base code filesystem. */ @@ -220,11 +231,16 @@ namespace ams::ldr { ON_SCOPE_EXIT { fs::CloseFile(file); }; R_TRY(LoadMetaFromFile(file, &g_original_meta_cache)); R_TRY(ValidateAcidSignature(&g_original_meta_cache.meta)); - meta->modulus = g_original_meta_cache.meta.modulus; - meta->is_signed = g_original_meta_cache.meta.is_signed; + meta->modulus = g_original_meta_cache.meta.modulus; + meta->check_verification_data = g_original_meta_cache.meta.check_verification_data; } } + /* Pre-process the capabilities. */ + /* This is used to e.g. avoid passing memory region descriptor to older kernels. */ + caps::ProcessCapabilities(meta->acid_kac, meta->acid->kac_size); + caps::ProcessCapabilities(meta->aci_kac, meta->aci->kac_size); + /* Set output. */ g_cached_program_id = loc.program_id; g_cached_override_status = status; diff --git a/stratosphere/loader/source/ldr_meta.hpp b/stratosphere/loader/source/ldr_meta.hpp index 3fca88c7f..c933b0fbe 100644 --- a/stratosphere/loader/source/ldr_meta.hpp +++ b/stratosphere/loader/source/ldr_meta.hpp @@ -32,7 +32,7 @@ namespace ams::ldr { void *aci_kac; void *modulus; - bool is_signed; + bool check_verification_data; }; /* Meta API. */ diff --git a/stratosphere/loader/source/ldr_process_creation.cpp b/stratosphere/loader/source/ldr_process_creation.cpp index 7447af874..fdc07c5e7 100644 --- a/stratosphere/loader/source/ldr_process_creation.cpp +++ b/stratosphere/loader/source/ldr_process_creation.cpp @@ -210,7 +210,7 @@ namespace ams::ldr { return ResultSuccess(); } - Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeInfo &code_info) { + Result ValidateMeta(const Meta *meta, const ncm::ProgramLocation &loc, const fs::CodeVerificationData &code_verification_data) { /* Validate version. */ R_TRY(ValidateProgramVersion(loc.program_id, meta->npdm->version)); @@ -222,15 +222,15 @@ namespace ams::ldr { R_TRY(caps::ValidateCapabilities(meta->acid_kac, meta->acid->kac_size, meta->aci_kac, meta->aci->kac_size)); /* If we have data to validate, validate it. */ - if (code_info.is_signed && meta->is_signed) { - const u8 *sig = code_info.signature; - const size_t sig_size = sizeof(code_info.signature); + if (code_verification_data.has_data && meta->check_verification_data) { + const u8 *sig = code_verification_data.signature; + const size_t sig_size = sizeof(code_verification_data.signature); const u8 *mod = static_cast(meta->modulus); const size_t mod_size = crypto::Rsa2048PssSha256Verifier::ModulusSize; const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent(); const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize; - const u8 *hsh = code_info.hash; - const size_t hsh_size = sizeof(code_info.hash); + const u8 *hsh = code_verification_data.target_hash; + const size_t hsh_size = sizeof(code_verification_data.target_hash); const bool is_signature_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, hsh_size); R_UNLESS(is_signature_valid, ResultInvalidNcaSignature()); @@ -596,7 +596,7 @@ namespace ams::ldr { R_TRY(LoadMetaFromCache(&meta, loc, override_status)); /* Validate meta. */ - R_TRY(ValidateMeta(&meta, loc, mount.GetCodeInfo())); + R_TRY(ValidateMeta(&meta, loc, mount.GetCodeVerificationData())); /* Load, validate NSOs. */ R_TRY(LoadNsoHeaders(nso_headers, has_nso)); diff --git a/stratosphere/ncm/Makefile b/stratosphere/ncm/Makefile index a92187ef8..1957b6cdb 100644 --- a/stratosphere/ncm/Makefile +++ b/stratosphere/ncm/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/pgl/Makefile b/stratosphere/pgl/Makefile index 7320b9f99..aab86a63a 100644 --- a/stratosphere/pgl/Makefile +++ b/stratosphere/pgl/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/pm/Makefile b/stratosphere/pm/Makefile index a92187ef8..1957b6cdb 100644 --- a/stratosphere/pm/Makefile +++ b/stratosphere/pm/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/pm/source/impl/pm_resource_manager.cpp b/stratosphere/pm/source/impl/pm_resource_manager.cpp index a9ba8277e..9a726ced4 100644 --- a/stratosphere/pm/source/impl/pm_resource_manager.cpp +++ b/stratosphere/pm/source/impl/pm_resource_manager.cpp @@ -168,6 +168,17 @@ namespace ams::pm::resource { } } + bool IsKTraceEnabled() { + if (!svc::IsKernelMesosphere()) { + return false; + } + + u64 value = 0; + R_ABORT_UNLESS(svc::GetInfo(std::addressof(value), svc::InfoType_MesosphereMeta, INVALID_HANDLE, svc::MesosphereMetaInfo_IsKTraceEnabled)); + + return value != 0; + } + } /* Resource API. */ @@ -206,10 +217,8 @@ namespace ams::pm::resource { g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_EventCountMax] += ExtraSystemEventCount600; g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_SessionCountMax] += ExtraSystemSessionCount600; } - if (hos_version >= hos::Version_9_0_0) { + if (hos_version >= hos::Version_9_2_0) { /* 9.2.0 increased the system session limit. */ - /* NOTE: We don't currently support detection of minor version, so we will provide this increase on 9.0.0+. */ - /* This shouldn't impact any existing behavior in undesirable ways. */ g_resource_limits[ResourceLimitGroup_System][svc::LimitableResource_SessionCountMax] += ExtraSystemSessionCount920; } @@ -232,7 +241,8 @@ namespace ams::pm::resource { } /* Choose and initialize memory arrangement. */ - if (hos_version >= hos::Version_6_0_0) { + const bool use_dynamic_memory_arrangement = (hos_version >= hos::Version_6_0_0) || (svc::IsKernelMesosphere() && hos_version >= hos::Version_5_0_0); + if (use_dynamic_memory_arrangement) { /* 6.0.0 retrieves memory limit information from the kernel, rather than using a hardcoded profile. */ g_memory_arrangement = spl::MemoryArrangement_Dynamic; @@ -253,20 +263,27 @@ namespace ams::pm::resource { g_memory_resource_limits[spl::MemoryArrangement_Dynamic][ResourceLimitGroup_System] = total_memory - reserved_non_system_size; } else { + /* Older system versions retrieve memory arrangement from spl, and use hardcoded profiles. */ g_memory_arrangement = spl::GetMemoryArrangement(); - } - /* Adjust memory limits for atmosphere. */ - /* We take memory away from applet normally, but away from application on < 3.0.0 to avoid a rare hang on boot. */ - /* NOTE: On Version 5.0.0+, we cannot set the pools so simply. We must instead modify the kernel, which we do */ - /* via patches in fusee-secondary. */ - if (hos_version < hos::Version_6_0_0) { + /* Adjust memory limits for atmosphere. */ + /* We take memory away from applet normally, but away from application on < 3.0.0 to avoid a rare hang on boot. */ + /* NOTE: On Version 5.0.0+, we cannot set the pools so simply. We must instead modify the kernel, which we do */ + /* via patches in fusee-secondary. */ const size_t extra_memory_size = hos_version == hos::Version_5_0_0 ? ExtraSystemMemorySizeAtmosphere500 : ExtraSystemMemorySizeAtmosphere; const auto src_group = hos_version >= hos::Version_3_0_0 ? ResourceLimitGroup_Applet : ResourceLimitGroup_Application; for (size_t i = 0; i < spl::MemoryArrangement_Count; i++) { g_memory_resource_limits[i][ResourceLimitGroup_System] += extra_memory_size; g_memory_resource_limits[i][src_group] -= extra_memory_size; } + + /* If KTrace is enabled, account for that by subtracting the memory from the applet pool. */ + if (IsKTraceEnabled()) { + constexpr size_t KTraceBufferSize = 16_MB; + for (size_t i = 0; i < spl::MemoryArrangement_Count; i++) { + g_memory_resource_limits[i][ResourceLimitGroup_Applet] -= KTraceBufferSize; + } + } } /* Actually set resource limits. */ diff --git a/stratosphere/ro/Makefile b/stratosphere/ro/Makefile index 7320b9f99..aab86a63a 100644 --- a/stratosphere/ro/Makefile +++ b/stratosphere/ro/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.cpp b/stratosphere/ro/source/impl/ro_nrr_utils.cpp index f4cbfc7a1..d8adf28db 100644 --- a/stratosphere/ro/source/impl/ro_nrr_utils.cpp +++ b/stratosphere/ro/source/impl/ro_nrr_utils.cpp @@ -157,7 +157,7 @@ namespace ams::ro::impl { return ResultSuccess(); } - Result ValidateNrr(const NrrHeader *header, u64 size, ncm::ProgramId program_id, ModuleType expected_type, bool enforce_type) { + Result ValidateNrr(const NrrHeader *header, u64 size, ncm::ProgramId program_id, NrrKind nrr_kind, bool enforce_nrr_kind) { /* Check magic. */ R_UNLESS(header->IsMagicValid(), ResultInvalidNrr()); @@ -182,9 +182,9 @@ namespace ams::ro::impl { /* Check program id. */ R_UNLESS(header->GetProgramId() == program_id, ResultInvalidNrr()); - /* Check type. */ - if (hos::GetVersion() >= hos::Version_7_0_0 && enforce_type) { - R_UNLESS(header->GetType() == expected_type, ResultInvalidNrrType()); + /* Check nrr kind. */ + if (hos::GetVersion() >= hos::Version_7_0_0 && enforce_nrr_kind) { + R_UNLESS(header->GetNrrKind() == nrr_kind, ResultInvalidNrrKind()); } } @@ -194,7 +194,7 @@ namespace ams::ro::impl { } /* Utilities for working with NRRs. */ - Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, Handle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type) { + Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, Handle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, NrrKind nrr_kind, bool enforce_nrr_kind) { map::MappedCodeMemory nrr_mcm(ResultInternalError{}); /* First, map the NRR. */ @@ -209,7 +209,7 @@ namespace ams::ro::impl { R_TRY(nrr_map.GetResult()); NrrHeader *nrr_header = reinterpret_cast(map_address); - R_TRY(ValidateNrr(nrr_header, nrr_heap_size, program_id, expected_type, enforce_type)); + R_TRY(ValidateNrr(nrr_header, nrr_heap_size, program_id, nrr_kind, enforce_nrr_kind)); /* Invalidation here actually prevents them from unmapping at scope exit. */ nrr_map.Invalidate(); diff --git a/stratosphere/ro/source/impl/ro_nrr_utils.hpp b/stratosphere/ro/source/impl/ro_nrr_utils.hpp index 3a675ad8f..f0e208ee1 100644 --- a/stratosphere/ro/source/impl/ro_nrr_utils.hpp +++ b/stratosphere/ro/source/impl/ro_nrr_utils.hpp @@ -20,7 +20,7 @@ namespace ams::ro::impl { /* Utilities for working with NRRs. */ - Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, Handle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, ModuleType expected_type, bool enforce_type); + Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, void *out_hash, size_t out_hash_size, Handle process_handle, ncm::ProgramId program_id, u64 nrr_heap_address, u64 nrr_heap_size, NrrKind nrr_kind, bool enforce_nrr_kind); Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address); bool ValidateNrrHashTableEntry(const void *signed_area, size_t signed_area_size, size_t hashes_offset, size_t num_hashes, const void *nrr_hash, const u8 *hash_table, const void *desired_hash); diff --git a/stratosphere/ro/source/impl/ro_service_impl.cpp b/stratosphere/ro/source/impl/ro_service_impl.cpp index 5568b0f43..54cd6555a 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.cpp +++ b/stratosphere/ro/source/impl/ro_service_impl.cpp @@ -382,13 +382,13 @@ namespace ams::ro::impl { } /* Context utilities. */ - Result RegisterProcess(size_t *out_context_id, Handle process_handle, os::ProcessId process_id) { + Result RegisterProcess(size_t *out_context_id, os::ManagedHandle process_handle, os::ProcessId process_id) { /* Validate process handle. */ { os::ProcessId handle_pid = os::InvalidProcessId; /* Validate handle is a valid process handle. */ - R_UNLESS(R_SUCCEEDED(os::TryGetProcessId(&handle_pid, process_handle)), ResultInvalidProcess()); + R_UNLESS(R_SUCCEEDED(os::TryGetProcessId(&handle_pid, process_handle.Get())), ResultInvalidProcess()); /* Validate process id. */ R_UNLESS(handle_pid == process_id, ResultInvalidProcess()); @@ -397,7 +397,7 @@ namespace ams::ro::impl { /* Check if a process context already exists. */ R_UNLESS(GetContextByProcessId(process_id) == nullptr, ResultInvalidSession()); - *out_context_id = AllocateContext(process_handle, process_id); + *out_context_id = AllocateContext(process_handle.Move(), process_id); return ResultSuccess(); } @@ -413,13 +413,13 @@ namespace ams::ro::impl { } /* Service implementations. */ - Result RegisterModuleInfo(size_t context_id, Handle process_h, u64 nrr_address, u64 nrr_size, ModuleType expected_type, bool enforce_type) { + Result RegisterModuleInfo(size_t context_id, os::ManagedHandle process_handle, u64 nrr_address, u64 nrr_size, NrrKind nrr_kind, bool enforce_nrr_kind) { /* Get context. */ ProcessContext *context = GetContextById(context_id); AMS_ABORT_UNLESS(context != nullptr); /* Get program id. */ - const ncm::ProgramId program_id = context->GetProgramId(process_h); + const ncm::ProgramId program_id = context->GetProgramId(process_handle.Get()); /* Validate address/size. */ R_TRY(ValidateAddressAndNonZeroSize(nrr_address, nrr_size)); @@ -435,7 +435,7 @@ namespace ams::ro::impl { /* Map. */ NrrHeader *header = nullptr; u64 mapped_code_address = 0; - R_TRY(MapAndValidateNrr(&header, &mapped_code_address, std::addressof(signed_area_hash), sizeof(signed_area_hash), context->process_handle, program_id, nrr_address, nrr_size, expected_type, enforce_type)); + R_TRY(MapAndValidateNrr(&header, &mapped_code_address, std::addressof(signed_area_hash), sizeof(signed_area_hash), context->process_handle, program_id, nrr_address, nrr_size, nrr_kind, enforce_nrr_kind)); /* Set NRR info. */ context->SetNrrInfoInUse(nrr_info, true); diff --git a/stratosphere/ro/source/impl/ro_service_impl.hpp b/stratosphere/ro/source/impl/ro_service_impl.hpp index a8f45456d..76cfe8242 100644 --- a/stratosphere/ro/source/impl/ro_service_impl.hpp +++ b/stratosphere/ro/source/impl/ro_service_impl.hpp @@ -30,12 +30,12 @@ namespace ams::ro::impl { bool ShouldEaseNroRestriction(); /* Context utilities. */ - Result RegisterProcess(size_t *out_context_id, Handle process_handle, os::ProcessId process_id); + Result RegisterProcess(size_t *out_context_id, os::ManagedHandle process_handle, os::ProcessId process_id); Result ValidateProcess(size_t context_id, os::ProcessId process_id); void UnregisterProcess(size_t context_id); /* Service implementations. */ - Result RegisterModuleInfo(size_t context_id, Handle process_h, u64 nrr_address, u64 nrr_size, ModuleType expected_type, bool enforce_type); + Result RegisterModuleInfo(size_t context_id, os::ManagedHandle process_h, u64 nrr_address, u64 nrr_size, NrrKind nrr_kind, bool enforce_nrr_kind); Result UnregisterModuleInfo(size_t context_id, u64 nrr_address); Result MapManualLoadModuleMemory(u64 *out_address, size_t context_id, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size); Result UnmapManualLoadModuleMemory(size_t context_id, u64 nro_address); diff --git a/stratosphere/ro/source/ro_main.cpp b/stratosphere/ro/source/ro_main.cpp index f3de32bb9..a7d71adf8 100644 --- a/stratosphere/ro/source/ro_main.cpp +++ b/stratosphere/ro/source/ro_main.cpp @@ -95,11 +95,11 @@ namespace { constexpr size_t DebugMonitorMaxSessions = 2; /* NOTE: Official code passes 32 for ldr:ro max sessions. We will pass 2, because that's the actual limit. */ - constexpr sm::ServiceName ForSelfServiceName = sm::ServiceName::Encode("ldr:ro"); - constexpr size_t ForSelfMaxSessions = 2; + constexpr sm::ServiceName UserServiceName = sm::ServiceName::Encode("ldr:ro"); + constexpr size_t UserMaxSessions = 2; - constexpr sm::ServiceName ForOthersServiceName = sm::ServiceName::Encode("ro:1"); - constexpr size_t ForOthersMaxSessions = 2; + constexpr sm::ServiceName JitPluginServiceName = sm::ServiceName::Encode("ro:1"); + constexpr size_t JitPluginMaxSessions = 2; } @@ -120,9 +120,9 @@ int main(int argc, char **argv) /* Create services. */ R_ABORT_UNLESS((g_server_manager.RegisterServer(DebugMonitorServiceName, DebugMonitorMaxSessions))); - R_ABORT_UNLESS((g_server_manager.RegisterServer(ForSelfServiceName, ForSelfMaxSessions))); + R_ABORT_UNLESS((g_server_manager.RegisterServer(UserServiceName, UserMaxSessions))); if (hos::GetVersion() >= hos::Version_7_0_0) { - R_ABORT_UNLESS((g_server_manager.RegisterServer(ForOthersServiceName, ForOthersMaxSessions))); + R_ABORT_UNLESS((g_server_manager.RegisterServer(JitPluginServiceName, JitPluginMaxSessions))); } /* Loop forever, servicing our services. */ diff --git a/stratosphere/ro/source/ro_ro_service.cpp b/stratosphere/ro/source/ro_ro_service.cpp index 47eb27b02..13c91118d 100644 --- a/stratosphere/ro/source/ro_ro_service.cpp +++ b/stratosphere/ro/source/ro_ro_service.cpp @@ -27,7 +27,7 @@ namespace ams::ro { impl::SetDevelopmentFunctionEnabled(is_development_function_enabled); } - RoService::RoService(ModuleType t) : context_id(impl::InvalidContextId), type(t) { + RoService::RoService(NrrKind k) : context_id(impl::InvalidContextId), nrr_kind(k) { /* ... */ } @@ -47,7 +47,7 @@ namespace ams::ro { Result RoService::RegisterModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size) { R_TRY(impl::ValidateProcess(this->context_id, client_pid.GetValue())); - return impl::RegisterModuleInfo(this->context_id, svc::InvalidHandle, nrr_address, nrr_size, ModuleType::ForSelf, true); + return impl::RegisterModuleInfo(this->context_id, svc::InvalidHandle, nrr_address, nrr_size, NrrKind_User, true); } Result RoService::UnregisterModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address) { @@ -56,12 +56,20 @@ namespace ams::ro { } Result RoService::RegisterProcessHandle(const sf::ClientProcessId &client_pid, sf::CopyHandle process_h) { - return impl::RegisterProcess(std::addressof(this->context_id), process_h.GetValue(), client_pid.GetValue()); + /* Ensure we manage references to the process handle correctly. */ + os::ManagedHandle process_handle(process_h.GetValue()); + + /* Register the process. */ + return impl::RegisterProcess(std::addressof(this->context_id), std::move(process_handle), client_pid.GetValue()); } - Result RoService::RegisterModuleInfoEx(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle process_h) { + Result RoService::RegisterProcessModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle process_h) { + /* Ensure we manage references to the process handle correctly. */ + os::ManagedHandle process_handle(process_h.GetValue()); + + /* Register the module. */ R_TRY(impl::ValidateProcess(this->context_id, client_pid.GetValue())); - return impl::RegisterModuleInfo(this->context_id, process_h.GetValue(), nrr_address, nrr_size, this->type, this->type == ModuleType::ForOthers); + return impl::RegisterModuleInfo(this->context_id, std::move(process_handle), nrr_address, nrr_size, this->nrr_kind, this->nrr_kind == NrrKind_JitPlugin); } } diff --git a/stratosphere/ro/source/ro_ro_service.hpp b/stratosphere/ro/source/ro_ro_service.hpp index c3dd6d15b..8e1702568 100644 --- a/stratosphere/ro/source/ro_ro_service.hpp +++ b/stratosphere/ro/source/ro_ro_service.hpp @@ -26,9 +26,9 @@ namespace ams::ro { class RoService { private: size_t context_id; - ModuleType type; + NrrKind nrr_kind; protected: - explicit RoService(ModuleType t); + explicit RoService(NrrKind k); public: virtual ~RoService(); public: @@ -38,19 +38,18 @@ namespace ams::ro { Result RegisterModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size); Result UnregisterModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address); Result RegisterProcessHandle(const sf::ClientProcessId &client_pid, sf::CopyHandle process_h); - Result RegisterModuleInfoEx(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle process_h); + Result RegisterProcessModuleInfo(const sf::ClientProcessId &client_pid, u64 nrr_address, u64 nrr_size, sf::CopyHandle process_h); }; static_assert(ro::impl::IsIRoInterface); - class RoServiceForSelf final : public RoService { + class RoUserService final : public RoService { public: - RoServiceForSelf() : RoService(ro::ModuleType::ForSelf) { /* ... */ } + RoUserService() : RoService(NrrKind_User) { /* ... */ } }; - /* TODO: This is really JitPlugin... */ - class RoServiceForOthers final : public RoService { + class RoJitPluginService final : public RoService { public: - RoServiceForOthers() : RoService(ro::ModuleType::ForOthers) { /* ... */ } + RoJitPluginService() : RoService(NrrKind_JitPlugin) { /* ... */ } }; } diff --git a/stratosphere/sm/Makefile b/stratosphere/sm/Makefile index a92187ef8..1957b6cdb 100644 --- a/stratosphere/sm/Makefile +++ b/stratosphere/sm/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/stratosphere/sm/source/impl/sm_service_manager.cpp b/stratosphere/sm/source/impl/sm_service_manager.cpp index 603a1d976..d74467795 100644 --- a/stratosphere/sm/source/impl/sm_service_manager.cpp +++ b/stratosphere/sm/source/impl/sm_service_manager.cpp @@ -350,6 +350,12 @@ namespace ams::sm::impl { return service == ServiceName::Encode("fsp-srv"); } + bool ShouldCloseOnClientDisconnect(ServiceName service) { + /* jit sysmodule is closed and relaunched by am for each application which uses it. */ + constexpr auto JitU = ServiceName::Encode("jit:u"); + return service == JitU; + } + Result GetMitmServiceHandleImpl(Handle *out, ServiceInfo *service_info, const MitmProcessInfo &client_info) { /* Send command to query if we should mitm. */ bool should_mitm; @@ -419,6 +425,28 @@ namespace ams::sm::impl { return ResultSuccess(); } + + } + + /* Client disconnection callback. */ + void OnClientDisconnected(os::ProcessId process_id) { + /* Ensure that the process id is valid. */ + if (process_id == os::InvalidProcessId) { + return; + } + + /* NOTE: Nintendo unregisters all services a process hosted on client close. */ + /* We do not do this as an atmosphere extension, in order to reduce the number */ + /* of sessions open at any given time. */ + /* However, certain system behavior (jit) relies on this occurring. */ + /* As such, we will special case the system components which rely on the behavior. */ + for (size_t i = 0; i < ServiceCountMax; i++) { + if (g_service_list[i].name != InvalidServiceName && g_service_list[i].owner_process_id == process_id) { + if (ShouldCloseOnClientDisconnect(g_service_list[i].name)) { + g_service_list[i].Free(); + } + } + } } /* Process management. */ @@ -588,6 +616,15 @@ namespace ams::sm::impl { *out = INVALID_HANDLE; *out_query = INVALID_HANDLE; + /* If we don't have a future mitm declaration, add one. */ + /* Client will clear this when ready to process. */ + bool has_existing_future_declaration = HasFutureMitmDeclaration(service); + if (!has_existing_future_declaration) { + R_TRY(AddFutureMitmDeclaration(service)); + } + + auto future_guard = SCOPE_GUARD { if (!has_existing_future_declaration) { ClearFutureMitmDeclaration(service); } }; + /* Create mitm handles. */ { os::ManagedHandle hnd, port_hnd, qry_hnd, mitm_qry_hnd; @@ -603,9 +640,7 @@ namespace ams::sm::impl { *out_query = qry_hnd.Move(); } - /* Clear the future declaration, if one exists. */ - ClearFutureMitmDeclaration(service); - + future_guard.Cancel(); return ResultSuccess(); } @@ -651,6 +686,34 @@ namespace ams::sm::impl { return ResultSuccess(); } + Result ClearFutureMitm(os::ProcessId process_id, ServiceName service) { + /* Validate service name. */ + R_TRY(ValidateServiceName(service)); + + /* Check that the process is registered and allowed to register the service. */ + if (!IsInitialProcess(process_id)) { + ProcessInfo *proc = GetProcessInfo(process_id); + R_UNLESS(proc != nullptr, sm::ResultInvalidClient()); + R_TRY(ValidateAccessControl(AccessControlEntry(proc->access_control, proc->access_control_size), service, true, false)); + } + + /* Check that a future mitm declaration is present or we have a mitm. */ + if (HasMitm(service)) { + /* Validate that the service exists. */ + ServiceInfo *service_info = GetServiceInfo(service); + R_UNLESS(service_info != nullptr, sm::ResultNotRegistered()); + + /* Validate that the client process_id is the mitm process. */ + R_UNLESS(service_info->mitm_process_id == process_id, sm::ResultNotAllowed()); + } else { + R_UNLESS(HasFutureMitmDeclaration(service), sm::ResultNotRegistered()); + } + + /* Clear the forward declaration. */ + ClearFutureMitmDeclaration(service); + return ResultSuccess(); + } + Result AcknowledgeMitmSession(MitmProcessInfo *out_info, Handle *out_hnd, os::ProcessId process_id, ServiceName service) { /* Validate service name. */ R_TRY(ValidateServiceName(service)); diff --git a/stratosphere/sm/source/impl/sm_service_manager.hpp b/stratosphere/sm/source/impl/sm_service_manager.hpp index f3ae700fd..cc628eec6 100644 --- a/stratosphere/sm/source/impl/sm_service_manager.hpp +++ b/stratosphere/sm/source/impl/sm_service_manager.hpp @@ -19,6 +19,9 @@ namespace ams::sm::impl { + /* Client disconnection callback. */ + void OnClientDisconnected(os::ProcessId process_id); + /* Process management. */ Result RegisterProcess(os::ProcessId process_id, ncm::ProgramId program_id, cfg::OverrideStatus, const void *acid_sac, size_t acid_sac_size, const void *aci_sac, size_t aci_sac_size); Result UnregisterProcess(os::ProcessId process_id); @@ -37,6 +40,7 @@ namespace ams::sm::impl { Result InstallMitm(Handle *out, Handle *out_query, os::ProcessId process_id, ServiceName service); Result UninstallMitm(os::ProcessId process_id, ServiceName service); Result DeclareFutureMitm(os::ProcessId process_id, ServiceName service); + Result ClearFutureMitm(os::ProcessId process_id, ServiceName service); Result AcknowledgeMitmSession(MitmProcessInfo *out_info, Handle *out_hnd, os::ProcessId process_id, ServiceName service); /* Dmnt record extensions. */ diff --git a/stratosphere/sm/source/sm_user_service.cpp b/stratosphere/sm/source/sm_user_service.cpp index 67143af19..de9c8b6e2 100644 --- a/stratosphere/sm/source/sm_user_service.cpp +++ b/stratosphere/sm/source/sm_user_service.cpp @@ -19,6 +19,12 @@ namespace ams::sm { + UserService::~UserService() { + if (this->has_initialized) { + impl::OnClientDisconnected(this->process_id); + } + } + Result UserService::RegisterClient(const sf::ClientProcessId &client_process_id) { this->process_id = client_process_id.GetValue(); this->has_initialized = true; @@ -75,6 +81,11 @@ namespace ams::sm { return impl::DeclareFutureMitm(this->process_id, service); } + Result UserService::AtmosphereClearFutureMitm(ServiceName service) { + R_TRY(this->EnsureInitialized()); + return impl::ClearFutureMitm(this->process_id, service); + } + Result UserService::AtmosphereHasService(sf::Out out, ServiceName service) { R_TRY(this->EnsureInitialized()); diff --git a/stratosphere/sm/source/sm_user_service.hpp b/stratosphere/sm/source/sm_user_service.hpp index bc2c02f3f..dfb64b530 100644 --- a/stratosphere/sm/source/sm_user_service.hpp +++ b/stratosphere/sm/source/sm_user_service.hpp @@ -26,6 +26,8 @@ namespace ams::sm { bool has_initialized = false; private: Result EnsureInitialized(); + public: + virtual ~UserService(); public: /* Official commands. */ Result RegisterClient(const sf::ClientProcessId &client_process_id); @@ -40,6 +42,7 @@ namespace ams::sm { Result AtmosphereHasMitm(sf::Out out, ServiceName service); Result AtmosphereWaitMitm(ServiceName service); Result AtmosphereDeclareFutureMitm(ServiceName service); + Result AtmosphereClearFutureMitm(ServiceName service); Result AtmosphereHasService(sf::Out out, ServiceName service); Result AtmosphereWaitService(ServiceName service); diff --git a/stratosphere/spl/Makefile b/stratosphere/spl/Makefile index a92187ef8..1957b6cdb 100644 --- a/stratosphere/spl/Makefile +++ b/stratosphere/spl/Makefile @@ -18,24 +18,9 @@ export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ export DEPSDIR := $(CURDIR)/$(BUILD) - -CFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.c)) $(notdir $(wildcard $(dir)/*.board.*.c)) $(notdir $(wildcard $(dir)/*.os.*.c)), \ - $(notdir $(wildcard $(dir)/*.c)))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).c))) -CFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).c))) - -CPPFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.cpp)) $(notdir $(wildcard $(dir)/*.board.*.cpp)) $(notdir $(wildcard $(dir)/*.os.*.cpp)), \ - $(notdir $(wildcard $(dir)/*.cpp)))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).cpp))) -CPPFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).cpp))) - -SFILES := $(foreach dir,$(SOURCES),$(filter-out $(notdir $(wildcard $(dir)/*.arch.*.s)) $(notdir $(wildcard $(dir)/*.board.*.s)) $(notdir $(wildcard $(dir)/*.os.*.s)), \ - $(notdir $(wildcard $(dir)/*.s)))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.arch.$(ATMOSPHERE_ARCH_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.board.$(ATMOSPHERE_BOARD_NAME).s))) -SFILES += $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.os.$(ATMOSPHERE_OS_NAME).s))) +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) diff --git a/troposphere/reboot_to_payload/source/main.c b/troposphere/reboot_to_payload/source/main.c index 3c6dcf0e1..80fe79e3c 100644 --- a/troposphere/reboot_to_payload/source/main.c +++ b/troposphere/reboot_to_payload/source/main.c @@ -4,7 +4,7 @@ #include -#define IRAM_PAYLOAD_MAX_SIZE 0x2F000 +#define IRAM_PAYLOAD_MAX_SIZE 0x20000 #define IRAM_PAYLOAD_BASE 0x40010000 static alignas(0x1000) u8 g_reboot_payload[IRAM_PAYLOAD_MAX_SIZE]; @@ -13,7 +13,7 @@ static alignas(0x1000) u8 g_work_page[0x1000]; void do_iram_dram_copy(void *buf, uintptr_t iram_addr, size_t size, int option) { memcpy(g_work_page, buf, size); - + SecmonArgs args = {0}; args.X[0] = 0xF0000201; /* smcAmsIramCopy */ args.X[1] = (uintptr_t)g_work_page; /* DRAM Address */ @@ -21,7 +21,7 @@ void do_iram_dram_copy(void *buf, uintptr_t iram_addr, size_t size, int option) args.X[3] = size; /* Copy size */ args.X[4] = option; /* 0 = Read, 1 = Write */ svcCallSecureMonitor(&args); - + memcpy(buf, g_work_page, size); } @@ -42,18 +42,18 @@ static void clear_iram(void) { static void reboot_to_payload(void) { clear_iram(); - + for (size_t i = 0; i < IRAM_PAYLOAD_MAX_SIZE; i += 0x1000) { copy_to_iram(IRAM_PAYLOAD_BASE + i, &g_reboot_payload[i], 0x1000); } - + splSetConfig((SplConfigItem)65001, 2); } int main(int argc, char **argv) { consoleInit(NULL); - + bool can_reboot = true; Result rc = splInitialize(); if (R_FAILED(rc)) { @@ -70,7 +70,7 @@ int main(int argc, char **argv) printf("Press [-] to reboot to payload\n"); } } - + printf("Press [L] to exit\n"); // Main loop @@ -89,7 +89,7 @@ int main(int argc, char **argv) if (can_reboot && kDown & KEY_MINUS) { reboot_to_payload(); } - if (kDown & KEY_L) { break; } // break in order to return to hbmenu + if (kDown & KEY_L) { break; } // break in order to return to hbmenu consoleUpdate(NULL); } @@ -97,7 +97,7 @@ int main(int argc, char **argv) if (can_reboot) { splExit(); } - + consoleExit(NULL); return 0; } diff --git a/utilities/erpt.py b/utilities/erpt.py index 0c139d49e..5079afcc7 100644 --- a/utilities/erpt.py +++ b/utilities/erpt.py @@ -273,11 +273,12 @@ def get_full(nxo): s.resolved = LOAD_BASE + s.value if s.name: if s.type == STT_FUNC: - print(hex(s.resolved), s.name) - idaapi.add_entry(s.resolved, s.resolved, s.name, 0) + #print(hex(s.resolved), s.name) + #idaapi.add_entry(s.resolved, s.resolved, s.name, 0) + pass else: - idaapi.force_name(s.resolved, s.name) - + #idaapi.force_name(s.resolved, s.name) + pass else: # NULL symbol s.resolved = 0 @@ -353,12 +354,18 @@ def find_categories(full, num_fields): return list(up('<'+'I'*num_fields, full[ind:ind+4*num_fields])) def find_types(full, num_fields): - KNOWN = range(10) + [4, 4, 2, 4] - ind = full.index(''.join(pk('