From bd9b01e4053ec205555238de024cc7ac081809d4 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 20 Oct 2020 23:51:48 -0700 Subject: [PATCH] sdmmc: skeleton implementation of Sdmmc1Controller --- .../vapours/sdmmc/sdmmc_build_config.hpp | 3 + .../include/vapours/tegra/tegra_apb_misc.hpp | 7 + .../source/sdmmc/impl/sdmmc_port_gc_asic0.cpp | 33 +++ .../source/sdmmc/impl/sdmmc_port_gc_asic0.hpp | 26 ++ .../source/sdmmc/impl/sdmmc_port_mmc0.cpp | 2 +- .../source/sdmmc/impl/sdmmc_port_sd_card0.cpp | 33 +++ .../source/sdmmc/impl/sdmmc_port_sd_card0.hpp | 26 ++ ...mmc_sdmmc_controller.board.nintendo_nx.cpp | 230 ++++++++++++++++++ ...mmc_sdmmc_controller.board.nintendo_nx.hpp | 212 +++++++++++++++- .../impl/sdmmc_select_sdmmc_controller.hpp | 4 +- .../libvapours/source/sdmmc/sdmmc_common.cpp | 14 +- 11 files changed, 581 insertions(+), 9 deletions(-) create mode 100644 libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.cpp create mode 100644 libraries/libvapours/source/sdmmc/impl/sdmmc_port_gc_asic0.hpp create mode 100644 libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.cpp create mode 100644 libraries/libvapours/source/sdmmc/impl/sdmmc_port_sd_card0.hpp 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(); }