diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index 140d96163..5fe60639c 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -40,4 +40,3 @@ #include #include #include -#include \ No newline at end of file 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/libvapours/include/vapours.hpp b/libraries/libvapours/include/vapours.hpp index eebb4f838..0bd48b651 100644 --- a/libraries/libvapours/include/vapours.hpp +++ b/libraries/libvapours/include/vapours.hpp @@ -26,6 +26,11 @@ #include #include #include + +#if defined(ATMOSPHERE_BOARD_NINTENDO_NX) +#include +#endif + #include #include diff --git a/libraries/libvapours/include/vapours/results/sdmmc_results.hpp b/libraries/libvapours/include/vapours/results/sdmmc_results.hpp index ad63fbd30..dec582eb0 100644 --- a/libraries/libvapours/include/vapours/results/sdmmc_results.hpp +++ b/libraries/libvapours/include/vapours/results/sdmmc_results.hpp @@ -40,14 +40,26 @@ namespace ams::sdmmc { R_DEFINE_ERROR_RESULT(AutoCommandResponseTimeoutError, 44); R_DEFINE_ERROR_RESULT(CommandCompleteSoftwareTimeout, 45); R_DEFINE_ERROR_RESULT(TransferCompleteSoftwareTimeout, 46); - 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(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(SdCardNotReadyToVoltageSwitch, 96); + R_DEFINE_ERROR_RESULT(SdCardNotCompleteVoltageSwitch, 97); R_DEFINE_ERROR_RANGE(HostControllerUnexpected, 128, 158); - R_DEFINE_ERROR_RESULT(SdHostStandardUnknownAutoCmdError, 130); - R_DEFINE_ERROR_RESULT(SdHostStandardUnknownError, 131); + 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); diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp index 2e5e7115d..291e74617 100644 --- a/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp +++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp @@ -24,6 +24,7 @@ #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_OS_EVENTS @@ -32,6 +33,7 @@ #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_OS_EVENTS @@ -40,6 +42,7 @@ #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_OS_EVENTS diff --git a/libraries/libvapours/include/vapours/tegra.hpp b/libraries/libvapours/include/vapours/tegra.hpp new file mode 100644 index 000000000..7d70c4a79 --- /dev/null +++ b/libraries/libvapours/include/vapours/tegra.hpp @@ -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 . + */ +#pragma once +#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 84% rename from libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp index 4ad9de8ba..b7f850e49 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_apb_misc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp @@ -14,11 +14,19 @@ * 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_EMMC4_PAD_CFGPADCTRL (0xAB4) +#define APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL (0xABC) #define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00) #define APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0 (0xc00) @@ -41,6 +49,15 @@ 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_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 98% rename from libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_clkrst.hpp index 395a006ea..2b738d197 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) 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 96% rename from libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_flow_ctlr.hpp index ef74c7f78..2047a44d3 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_flow_ctlr.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_flow_ctlr.hpp @@ -14,7 +14,12 @@ * 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) 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 5840b9d45..c138bd6aa 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_mc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_mc.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define MC_INTSTATUS (0x000) #define MC_INTMASK (0x004) 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 96% rename from libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_pinmux.hpp index 8e5424880..25abf272a 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pinmux.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_pinmux.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define PINMUX_AUX_GEN1_I2C_SCL (0x30BC) #define PINMUX_AUX_GEN1_I2C_SDA (0x30C0) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp similarity index 98% rename from libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp rename to libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp index 28c0e298b..f80f61b70 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp @@ -14,7 +14,12 @@ * along with this program. If not, see . */ #pragma once -#include +#include +#include +#include +#include +#include +#include #define APBDEV_PMC_CNTRL (0x000) #define APBDEV_PMC_WAKE_MASK (0x00C) 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/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp index 7d055b34c..6c12ec22d 100644 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.cpp @@ -62,6 +62,66 @@ namespace ams::sdmmc::impl { 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)); @@ -156,7 +216,7 @@ namespace ams::sdmmc::impl { /* 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, FIVE_TWELVE_KB), + 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. */ @@ -173,6 +233,31 @@ namespace ams::sdmmc::impl { 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; @@ -216,6 +301,11 @@ namespace ams::sdmmc::impl { 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), @@ -855,7 +945,7 @@ namespace ams::sdmmc::impl { AMS_ABORT_UNLESS(this->is_device_clock_enable); /* Check if we need to temporarily re-enable the device clock. */ - bool clock_disabled = reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + 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) { @@ -884,7 +974,7 @@ namespace ams::sdmmc::impl { AMS_ABORT_UNLESS(this->is_device_clock_enable); /* Check if we need to temporarily re-enable the device clock. */ - bool clock_disabled = reg::HasValue(this->registers->clock_control, SD_REG_BITS_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, DISABLE)); + 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) { 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 index 2caf3558a..7bdf0a3cd 100644 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.hpp +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_controller.hpp @@ -67,12 +67,18 @@ namespace ams::sdmmc::impl { } #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(); @@ -83,8 +89,10 @@ namespace ams::sdmmc::impl { #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(); 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 index 2e5a73eee..e104374ed 100644 --- a/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_registers.hpp +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sd_host_standard_registers.hpp @@ -83,7 +83,7 @@ namespace ams::sdmmc::impl { #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, FOUR_KB, EIGHT_KB, SIXTEEN_KB, THIRTY_TWO_KB, SIXTY_FOUR_KB, ONE_TWENTY_EIGHT_KB, TWO_FIFTY_SIX_KB, FIVE_TWELVE_KB); + 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); @@ -105,6 +105,7 @@ namespace ams::sdmmc::impl { 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); @@ -114,7 +115,14 @@ namespace ams::sdmmc::impl { 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); @@ -139,30 +147,47 @@ namespace ams::sdmmc::impl { 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_STATUS_ENABLE_COMMAND_COMPLETE, 0, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_ENABLE_TRANSFER_COMPLETE, 1, MASKED, ENABLED); + 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_STATUS_ENABLE_COMMAND_COMPLETE, __ENUM__), \ - SD_REG_BITS_ENUM(NORMAL_INTERRUPT_STATUS_ENABLE_TRANSFER_COMPLETE, __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_STATUS_ENABLE_COMMAND_TIMEOUT_ERROR, 0, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_COMMAND_CRC_ERROR, 1, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_COMMAND_END_BIT_ERROR, 2, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_COMMAND_INDEX_ERROR, 3, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_DATA_TIMEOUT_ERROR, 4, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_DATA_CRC_ERROR, 5, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_DATA_END_BIT_ERROR, 6, MASKED, ENABLED); - DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_AUTO_CMD_ERROR, 8, MASKED, ENABLED); + 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_STATUS_ENABLE_COMMAND_TIMEOUT_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_COMMAND_CRC_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_COMMAND_END_BIT_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_COMMAND_INDEX_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_DATA_TIMEOUT_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_DATA_CRC_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_DATA_END_BIT_ERROR, __ENUM__), \ - SD_REG_BITS_ENUM(ERROR_INTERRUPT_STATUS_ENABLE_AUTO_CMD_ERROR, __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..b9b4fc4cd --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp @@ -0,0 +1,872 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 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_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::Mutex g_soc_mutex(false); + + #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->GetOutboundTapValue())); + + /* 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; + } + +} 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 index 1da622594..64f5d2d70 100644 --- 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 @@ -24,6 +24,9 @@ namespace ams::sdmmc::impl { 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 { @@ -72,37 +75,303 @@ namespace ams::sdmmc::impl { static_assert(sizeof(SdmmcRegisters) == SdmmcRegistersSize); private: SdmmcRegisters *sdmmc_registers; - /* TODO */ + 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; } - /* TODO */ + 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; + } }; class Sdmmc2And4Controller : public SdmmcController { - /* TODO */ + 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) { /* ... */ } - /* TODO */ + 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 Sdmmc4RegistersPhysicalAddress = UINT64_C(0x700B0600); class Sdmmc4Controller : public Sdmmc2And4Controller { - /* TODO */ + 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) { /* ... */ } - - /* TODO */ }; }