From 934018354119eba5300f59f275c19f92409ed661 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 21 Oct 2020 12:26:15 -0700 Subject: [PATCH] sdmmc: complete abstract logic for Sdmmc1 power controller --- .../include/vapours/dd/dd_io_mapping.hpp | 3 + .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/pcv_results.hpp | 26 ++ .../include/vapours/tegra/tegra_pmc.hpp | 4 + .../libvapours/source/dd/dd_io_mapping.cpp | 28 +++ .../impl/sdmmc_io_impl.board.nintendo_nx.cpp | 66 +++++ .../impl/sdmmc_io_impl.board.nintendo_nx.hpp | 62 +++++ ...mmc_sdmmc_controller.board.nintendo_nx.cpp | 232 ++++++++++++++++-- ...mmc_sdmmc_controller.board.nintendo_nx.hpp | 22 ++ 9 files changed, 429 insertions(+), 15 deletions(-) create mode 100644 libraries/libvapours/include/vapours/results/pcv_results.hpp create mode 100644 libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp create mode 100644 libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp diff --git a/libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp b/libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp index 520575b03..f82552bb0 100644 --- a/libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp +++ b/libraries/libvapours/include/vapours/dd/dd_io_mapping.hpp @@ -20,4 +20,7 @@ namespace ams::dd { uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size); + u32 ReadIoRegister(dd::PhysicalAddress phys_addr); + void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value); + } diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index 40d809b90..596114b95 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/results/pcv_results.hpp b/libraries/libvapours/include/vapours/results/pcv_results.hpp new file mode 100644 index 000000000..ec0aa6d46 --- /dev/null +++ b/libraries/libvapours/include/vapours/results/pcv_results.hpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +namespace ams::pcv { + + R_DEFINE_NAMESPACE_RESULT_MODULE(133); + + R_DEFINE_ERROR_RESULT(IllegalRequest, 16); + +} diff --git a/libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp b/libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp index f80f61b70..635917e97 100644 --- a/libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp +++ b/libraries/libvapours/include/vapours/tegra/tegra_pmc.hpp @@ -202,6 +202,10 @@ DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE2, 29, OFF, ON); DEFINE_PMC_REG(PWRGATE_STATUS_CE123, 9, 3); +DEFINE_PMC_REG_BIT_ENUM(NO_IOPOWER_SDMMC1, 12, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(PWR_DET_SDMMC1, 12, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(PWR_DET_VAL_SDMMC1, 12, DISABLE, ENABLE); + DEFINE_PMC_REG(SET_SW_CLAMP_CRAIL, 0, 1); DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3); diff --git a/libraries/libvapours/source/dd/dd_io_mapping.cpp b/libraries/libvapours/source/dd/dd_io_mapping.cpp index c029a0434..3ece3e2c4 100644 --- a/libraries/libvapours/source/dd/dd_io_mapping.cpp +++ b/libraries/libvapours/source/dd/dd_io_mapping.cpp @@ -66,4 +66,32 @@ namespace ams::dd { #endif } + u32 ReadIoRegister(dd::PhysicalAddress phys_addr) { + #if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) + return reg::Read(dd::QueryIoMapping(phys_addr, sizeof(u32))); + #elif defined(ATMOSPHERE_IS_STRATOSPHERE) + + u32 val; + R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(val), phys_addr, 0, 0)); + return val; + + #else + #error "Unknown execution context for ams::dd::ReadIoRegister!" + #endif + } + + void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value) { + #if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) + reg::Write(dd::QueryIoMapping(phys_addr, sizeof(u32)), value); + #elif defined(ATMOSPHERE_IS_STRATOSPHERE) + + u32 out_val; + R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(val), phys_addr, 0xFFFFFFFF, value)); + AMS_UNUSED(out_val); + + #else + #error "Unknown execution context for ams::dd::WriteIoRegister!" + #endif + } + } diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp new file mode 100644 index 000000000..82c9de3b0 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#elif defined(ATMOSPHERE_IS_EXOSPHERE) +#include +#else +#include +#endif +#include "sdmmc_io_impl.board.nintendo_nx.hpp" + +namespace ams::sdmmc::impl { + + Result SetSdCardVoltageEnabled(bool en) { + /* TODO */ + } + + Result SetSdCardVoltageValue(u32 micro_volts) { + /* TODO */ + } + + namespace pinmux_impl { + + void SetPinAssignment(PinAssignment assignment) { + /* TODO */ + } + + } + + namespace gpio_impl { + + void OpenSession(GpioPadName pad) { + /* TODO */ + } + + void CloseSession(GpioPadName pad) { + /* TODO */ + } + + void SetDirection(GpioPadName pad, Direction direction) { + /* TODO */ + } + + void SetValue(GpioPadName pad, GpioValue value) { + /* TODO */ + } + + + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp new file mode 100644 index 000000000..89b623073 --- /dev/null +++ b/libraries/libvapours/source/sdmmc/impl/sdmmc_io_impl.board.nintendo_nx.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::sdmmc::impl { + + Result SetSdCardVoltageEnabled(bool en); + Result SetSdCardVoltageValue(u32 micro_volts); + + namespace pinmux_impl { + + enum PinAssignment { + PinAssignment_Sdmmc1OutputHigh = 2, + PinAssignment_Sdmmc1ResetState = 3, + PinAssignment_Sdmmc1SchmtEnable = 4, + PinAssignment_Sdmmc1SchmtDisable = 5, + }; + + void SetPinAssignment(PinAssignment assignment); + + } + + namespace gpio_impl { + + enum GpioValue { + GpioValue_Low = 0, + GpioValue_High = 1 + }; + + enum Direction { + Direction_Input = 0, + Direction_Output = 1, + }; + + enum GpioPadName { + GpioPadName_PowSdEn = 2, + }; + + void OpenSession(GpioPadName pad); + void CloseSession(GpioPadName pad); + + void SetDirection(GpioPadName pad, Direction direction); + void SetValue(GpioPadName pad, GpioValue value); + + + } + +} diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.cpp index fffaad71a..a8fe01d26 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 @@ -23,6 +23,7 @@ #include #endif #include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp" +#include "sdmmc_io_impl.board.nintendo_nx.hpp" #include "sdmmc_timer.hpp" namespace ams::sdmmc::impl { @@ -875,7 +876,9 @@ namespace ams::sdmmc::impl { /* 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); */ + /* pcv::PowerOn(pcv::PowerControlTarget_SdCard, 3300000); */ + R_TRY(this->power_controller->PowerOn(BusPower_3_3V)); + return ResultSuccess(); } @@ -887,23 +890,34 @@ namespace ams::sdmmc::impl { /* If we're at 3.3V, lower to 1.8V. */ { - /* TODO: equivalent of pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + /* pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + this->power_controller->LowerBusPower(); + + /* Set our bus power. */ this->current_bus_power = BusPower_1_8V; } - /* TODO: Equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */ + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1OutputHigh); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1OutputHigh); - /* TODO: Equivalent of pcv::PowerOff(pcv::PowerControlTarget_SdCard); */ + + /* pcv::PowerOff(pcv::PowerControlTarget_SdCard); */ + this->power_controller->PowerOff(); + + /* Set our bus power. */ this->current_bus_power = BusPower_Off; - /* TODO: Equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1ResetState); */ + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1ResetState); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1ResetState); } Result Sdmmc1Controller::LowerBusPowerForRegisterControl() { /* Nintendo sets the current bus power regardless of whether the call succeeds. */ ON_SCOPE_EXIT { this->current_bus_power = BusPower_1_8V; }; - /* TODO: equivalent of return pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + /* pcv::ChangeVoltage(pcv::PowerControlTarget_SdCard, 1800000); */ + R_TRY(this->power_controller->LowerBusPower()); + return ResultSuccess(); } @@ -911,14 +925,17 @@ namespace ams::sdmmc::impl { SdHostStandardController::EnsureControl(); if (IsSocMariko()) { - /* TODO: equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtEnable); } else { switch (bus_power) { case BusPower_1_8V: - /* TODO: equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtEnable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtEnable); break; case BusPower_3_3V: - /* TODO: equivalent of pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtDisable); */ + /* pinmux::SetPinAssignment(std::addressof(this->pinmux_session), pinmux::PinAssignment_Sdmmc1SchmtDisable); */ + pinmux_impl::SetPinAssignment(pinmux_impl::PinAssignment_Sdmmc1SchmtDisable); break; case BusPower_Off: AMS_UNREACHABLE_DEFAULT_CASE(); @@ -1051,9 +1068,17 @@ namespace ams::sdmmc::impl { 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(); */ + /* pinmux::Initialize(); */ + /* This just opens a session handle to pinmux service, no work to do. */ + + /* pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */ + /* This just sets the session's internal value to the pin group name, so nothing to do here either. */ + + /* pcv::Initialize(); */ + /* This initializes a lot of globals in pcv, most of which we don't care about. */ + /* However, we do care about the Sdmmc1PowerController. */ + AMS_ABORT_UNLESS(this->power_controller == nullptr); + this->power_controller = new (GetPointer(this->power_controller_storage)) PowerController; /* Perform base initialization. */ SdmmcController::Initialize(); @@ -1063,9 +1088,18 @@ namespace ams::sdmmc::impl { /* 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(); */ + /* pcv::Finalize(); */ + /* As with initialize, we mostly don't care about the globals this touches. */ + /* However, we do want to finalize the Sdmmc1PowerController. */ + AMS_ABORT_UNLESS(this->power_controller != nullptr); + this->power_controller->~PowerController(); + this->power_controller = nullptr; + + /* pinmux::CloseSession(std::addressof(this->pinmux_session)); */ + /* This does nothing. */ + + /* pinmux::Finalize(); */ + /* This does nothing. */ #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) /* Mark ourselves as initialized by register control. */ @@ -1099,4 +1133,172 @@ namespace ams::sdmmc::impl { } #endif + namespace { + + constexpr inline dd::PhysicalAddress PmcRegistersPhysicalAddress = 0x7000E400; + + } + + Result Sdmmc1Controller::PowerController::ControlVddioSdmmc1(BusPower bus_power) { + /* Configure appropriate voltage. */ + switch (bus_power) { + case BusPower_Off: + R_TRY(SetSdCardVoltageEnabled(false)); + break; + case BusPower_1_8V: + R_TRY(SetSdCardVoltageValue(1'800'000)); + R_TRY(SetSdCardVoltageEnabled(true)); + break; + case BusPower_3_3V: + R_TRY(SetSdCardVoltageValue(3'300'000)); + R_TRY(SetSdCardVoltageEnabled(true)); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + + return ResultSuccess(); + } + + void Sdmmc1Controller::PowerController::SetSdmmcIoMode(bool is_3_3V) { + /* Determine the address we're updating. */ + constexpr dd::PhysicalAddress ApbdevPmcPwrDetValAddress = PmcRegistersPhysicalAddress + APBDEV_PMC_PWR_DET_VAL; + + /* Read the current value. */ + u32 value = dd::ReadIoRegister(ApbdevPmcPwrDetValAddress); + + /* Mask out the existing bits. */ + value &= ~(reg::EncodeMask(PMC_REG_BITS_MASK(PWR_DET_VAL_SDMMC1))); + + /* ORR in the new bits. */ + value |= reg::Encode(PMC_REG_BITS_ENUM_SEL(PWR_DET_VAL_SDMMC1, is_3_3V, ENABLE, DISABLE)); + + /* Write the new value. */ + dd::WriteIoRegister(ApbdevPmcPwrDetValAddress, value); + + /* Read the value back to be sure our write takes. */ + dd::ReadIoRegister(ApbdevPmcPwrDetValAddress); + } + + void Sdmmc1Controller::PowerController::ControlRailSdmmc1Io(bool is_power_on) { + /* Determine the address we're updating. */ + constexpr dd::PhysicalAddress ApbdevPmcNoIoPowerAddress = PmcRegistersPhysicalAddress + APBDEV_PMC_NO_IOPOWER; + + /* Read the current value. */ + u32 value = dd::ReadIoRegister(ApbdevPmcNoIoPowerAddress); + + /* Mask out the existing bits. */ + value &= ~(reg::EncodeMask(PMC_REG_BITS_MASK(NO_IOPOWER_SDMMC1))); + + /* ORR in the new bits. */ + value |= reg::Encode(PMC_REG_BITS_ENUM_SEL(NO_IOPOWER_SDMMC1, is_power_on, DISABLE, ENABLE)); + + /* Write the new value. */ + dd::WriteIoRegister(ApbdevPmcNoIoPowerAddress, value); + + /* Read the value back to be sure our write takes. */ + dd::ReadIoRegister(ApbdevPmcNoIoPowerAddress); + } + + Sdmmc1Controller::PowerController::PowerController() : current_bus_power(BusPower_Off) { + /* gpio::Initialize(); */ + /* ... */ + + /* Open gpio session. */ + /* gpio::OpenSession(std::addressof(this->gpio_pad_session), gpio::GpioPadName_PowSdEn); */ + gpio_impl::OpenSession(gpio_impl::GpioPadName_PowSdEn); + + /* Configure the gpio as low/output. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_Low); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_Low); + + /* gpio::SetDirection(std::addressof(this->gpio_pad_session), gpio::Direction_Output); */ + gpio_impl::SetDirection(gpio_impl::GpioPadName_PowSdEn, gpio_impl::Direction_Output); + } + + Sdmmc1Controller::PowerController::~PowerController() { + /* gpio::CloseSession(std::addressof(this->gpio_pad_session)); */ + gpio_impl::CloseSession(gpio_impl::GpioPadName_PowSdEn); + + /* gpio::Finalize(); */ + /* ... */ + } + + Result Sdmmc1Controller::PowerController::PowerOn(BusPower bus_power) { + /* Bus power should be off, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power == BusPower_Off); + R_SUCCEED_IF(this->current_bus_power != BusPower_Off); + + /* Power on requires the target bus power be 3.3V. */ + AMS_ABORT_UNLESS(bus_power == BusPower_3_3V); + + /* Enable the rail. */ + this->ControlRailSdmmc1Io(true); + + /* Set the SD power GPIO to high. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_High); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_High); + + /* Wait 10ms for power change to take. */ + WaitMicroSeconds(10000); + + /* Configure Sdmmc1 IO as 3.3V. */ + this->SetSdmmcIoMode(true); + R_TRY(this->ControlVddioSdmmc1(BusPower_3_3V)); + + /* Wait 130 us for changes to take. */ + WaitMicroSeconds(130); + + /* Update our current bus power. */ + this->current_bus_power = bus_power; + + return ResultSuccess(); + } + + Result Sdmmc1Controller::PowerController::PowerOff() { + /* Bus power should be on, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power != BusPower_Off); + R_SUCCEED_IF(this->current_bus_power == BusPower_Off); + + /* Bus power should be 1.8V. */ + /* NOTE: the result returned here is 0x8C0 (regulator::ResultIllegalRequest()) on newer firmwares. */ + AMS_ASSERT(this->current_bus_power == BusPower_1_8V); + R_UNLESS(this->current_bus_power == BusPower_1_8V, pcv::ResultIllegalRequest()); + + /* Disable vddio, and wait 4 ms. */ + this->ControlVddioSdmmc1(BusPower_Off); + WaitMicroSeconds(4000); + + /* Set the SD power GPIO to low. */ + /* gpio::SetValue(std::addressof(this->gpio_pad_session), gpio::GpioValue_Low); */ + gpio_impl::SetValue(gpio_impl::GpioPadName_PowSdEn, gpio_impl::GpioValue_Low); + + /* Wait 239ms for the gpio config to take. */ + WaitMicroSeconds(239000); + + /* Disable the rail. */ + this->ControlRailSdmmc1Io(false); + this->SetSdmmcIoMode(true); + + /* Update our current bus power. */ + this->current_bus_power = BusPower_Off; + + return ResultSuccess(); + } + + Result Sdmmc1Controller::PowerController::LowerBusPower() { + /* Bus power should be 3.3V, and if it's not we don't need to do anything. */ + AMS_ASSERT(this->current_bus_power == BusPower_3_3V); + R_SUCCEED_IF(this->current_bus_power != BusPower_3_3V); + + /* Configure as 1.8V, then wait 150us for it to take. */ + R_TRY(this->ControlVddioSdmmc1(BusPower_1_8V)); + WaitMicroSeconds(150); + this->SetSdmmcIoMode(false); + + /* Update our current bus power. */ + this->current_bus_power = BusPower_1_8V; + + return ResultSuccess(); + } + } diff --git a/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp b/libraries/libvapours/source/sdmmc/impl/sdmmc_sdmmc_controller.board.nintendo_nx.hpp index 830f9c9d9..4c5fba691 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 @@ -210,6 +210,25 @@ namespace ams::sdmmc::impl { #if defined(AMS_SDMMC_USE_OS_EVENTS) static constinit inline os::InterruptEventType s_interrupt_event{}; #endif + + /* NOTE: This is a fascimile of pcv's Sdmmc1PowerController. */ + class PowerController { + NON_COPYABLE(PowerController); + NON_MOVEABLE(PowerController); + private: + BusPower current_bus_power; + private: + Result ControlVddioSdmmc1(BusPower bus_power); + void SetSdmmcIoMode(bool is_3_3V); + void ControlRailSdmmc1Io(bool is_power_on); + public: + PowerController(); + ~PowerController(); + + Result PowerOn(BusPower bus_power); + Result PowerOff(); + Result LowerBusPower(); + }; private: #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) /* TODO: pinmux::PinmuxSession pinmux_session; */ @@ -218,6 +237,8 @@ namespace ams::sdmmc::impl { #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) bool is_pcv_control; #endif + TYPED_STORAGE(PowerController) power_controller_storage; + PowerController *power_controller; private: Result PowerOnForRegisterControl(BusPower bus_power); void PowerOffForRegisterControl(); @@ -381,6 +402,7 @@ namespace ams::sdmmc::impl { #if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL) this->is_pcv_control = false; #endif + this->power_controller = nullptr; } virtual void Initialize() override;