diff --git a/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp b/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp
index 291e74617..4ecbdc7ea 100644
--- a/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp
+++ b/libraries/libvapours/include/vapours/sdmmc/sdmmc_build_config.hpp
@@ -27,6 +27,7 @@
//#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_OS_EVENTS
//#define AMS_SDMMC_USE_OS_TIMER
#define AMS_SDMMC_USE_UTIL_TIMER
@@ -36,6 +37,7 @@
//#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_OS_EVENTS
//#define AMS_SDMMC_USE_OS_TIMER
#define AMS_SDMMC_USE_UTIL_TIMER
@@ -45,6 +47,7 @@
#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_OS_EVENTS
#define AMS_SDMMC_USE_OS_TIMER
//#define AMS_SDMMC_USE_UTIL_TIMER
diff --git a/libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp b/libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp
index 984abd560..1ceabe5b2 100644
--- a/libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp
+++ b/libraries/libvapours/include/vapours/tegra/tegra_apb_misc.hpp
@@ -25,6 +25,8 @@
#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)
@@ -52,6 +54,11 @@ 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);
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..4dcd07c78
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.cpp
@@ -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 .
+ */
+#include
+#include "sdmmc_port_mmc0.hpp"
+#include "sdmmc_select_sdmmc_controller.hpp"
+
+
+namespace ams::sdmmc::impl {
+
+ namespace {
+
+ SdmmcControllerForPortGcAsic0 g_gc_asic0_host_controller;
+
+ }
+
+ IHostController *GetHostControllerOfPortGcAsic0() {
+ return std::addressof(g_gc_asic0_host_controller);
+ }
+
+}
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..7dada879f
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.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 "sdmmc_i_host_controller.hpp"
+#include "sdmmc_i_device_accessor.hpp"
+
+namespace ams::sdmmc::impl {
+
+ IHostController *GetHostControllerOfPortGcAsic0();
+ IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0();
+
+}
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp
index 3d7aab91d..60512b485 100644
--- a/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_mmc0.cpp
@@ -22,7 +22,7 @@ namespace ams::sdmmc::impl {
namespace {
- SdmmcControllerForMmc g_mmc0_host_controller;
+ SdmmcControllerForPortMmc0 g_mmc0_host_controller;
}
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..de45bb20c
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.cpp
@@ -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 .
+ */
+#include
+#include "sdmmc_port_mmc0.hpp"
+#include "sdmmc_select_sdmmc_controller.hpp"
+
+
+namespace ams::sdmmc::impl {
+
+ namespace {
+
+ SdmmcControllerForPortSdCard0 g_sd_card0_host_controller;
+
+ }
+
+ IHostController *GetHostControllerOfPortSdCard0() {
+ return std::addressof(g_sd_card0_host_controller);
+ }
+
+}
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..72d4bb4b4
--- /dev/null
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.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 "sdmmc_i_host_controller.hpp"
+#include "sdmmc_i_device_accessor.hpp"
+
+namespace ams::sdmmc::impl {
+
+ IHostController *GetHostControllerOfPortSdCard0();
+ IDeviceAccessor *GetDeviceAccessorOfPortSdCard0();
+
+}
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
index b9b4fc4cd..fffaad71a 100644
--- 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
@@ -869,4 +869,234 @@ namespace ams::sdmmc::impl {
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; };
+
+ /* TODO: equivalent of return pcv::PowerOn(pcv::PowerControlTarget_SdCard, 3300000); */
+ 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. */
+ {
+ /* TODO: equivalent of pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */
+ this->current_bus_power = BusPower_1_8V;
+ }
+
+ /* TODO: Equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */
+
+ /* TODO: Equivalent of pcv::PowerOff(pcv::PowerControlTarget_SdCard); */
+ this->current_bus_power = BusPower_Off;
+
+ /* TODO: Equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::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; };
+
+ /* TODO: equivalent of return pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */
+ return ResultSuccess();
+ }
+
+ void Sdmmc1Controller::SetSchmittTriggerForRegisterControl(BusPower bus_power) {
+ SdHostStandardController::EnsureControl();
+
+ if (IsSocMariko()) {
+ /* TODO: equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */
+ } else {
+ switch (bus_power) {
+ case BusPower_1_8V:
+ /* TODO: equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */
+ break;
+ case BusPower_3_3V:
+ /* TODO: equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::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
+
+ /* TODO: equivalent of pinmux::Initialize(); */
+ /* TODO: equivalent of pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */
+ /* TODO: equivalent of pcv::Initialize(); */
+
+ /* Perform base initialization. */
+ SdmmcController::Initialize();
+ }
+
+ void Sdmmc1Controller::FinalizeForRegisterControl() {
+ /* Perform base finalization. */
+ SdmmcController::Finalize();
+
+ /* TODO: equivalent of pcv::Finalize(); */
+ /* TODO: equivalent of pinmux::CloseSession(std::addressof(this->pinmux_session)); */
+ /* TODO: equivalent of pinmux::Finalize(); */
+
+ #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
+
}
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 ce8acc293..830f9c9d9 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
@@ -203,6 +203,216 @@ namespace ams::sdmmc::impl {
}
};
+ 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
+ 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
+ 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
+ }
+
+ 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 {
@@ -330,7 +540,7 @@ namespace ams::sdmmc::impl {
}
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override {
- /* SDMMC4 only supports 1.8v. */
+ /* SDMMC2 only supports 1.8v. */
AMS_ABORT_UNLESS(bus_power == BusPower_1_8V);
/* Ensure that we can control registers. */
diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp
index 635fc9d41..9d3b9432a 100644
--- a/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp
+++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_select_sdmmc_controller.hpp
@@ -22,7 +22,9 @@
namespace ams::sdmmc::impl {
- using SdmmcControllerForMmc = Sdmmc4Controller;
+ using SdmmcControllerForPortSdCard0 = Sdmmc1Controller;
+ using SdmmcControllerForPortGcAsic0 = Sdmmc2Controller;
+ using SdmmcControllerForPortMmc0 = Sdmmc4Controller;
}
diff --git a/libraries/libvapours/source/sdmmc/sdmmc_common.cpp b/libraries/libvapours/source/sdmmc/sdmmc_common.cpp
index bdc61b9fe..b26561b28 100644
--- a/libraries/libvapours/source/sdmmc/sdmmc_common.cpp
+++ b/libraries/libvapours/source/sdmmc/sdmmc_common.cpp
@@ -18,6 +18,8 @@
#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 {
@@ -27,9 +29,9 @@ namespace ams::sdmmc {
/* Get the controller. */
impl::IHostController *host_controller = nullptr;
switch (port) {
- case Port_Mmc0: host_controller = impl::GetHostControllerOfPortMmc0(); break;
- case Port_SdCard0: /* TODO */ break;
- case Port_GcAsic0: /* TODO */ break;
+ 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();
}
@@ -42,9 +44,9 @@ namespace ams::sdmmc {
/* Get the accessor. */
impl::IDeviceAccessor *device_accessor = nullptr;
switch (port) {
- case Port_Mmc0: device_accessor = impl::GetDeviceAccessorOfPortMmc0(); break;
- case Port_SdCard0: /* TODO */ break;
- case Port_GcAsic0: /* TODO */ break;
+ 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();
}