mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 04:41:12 +00:00
sdmmc: complete abstract logic for Sdmmc1 power controller
This commit is contained in:
parent
bd9b01e405
commit
9340183541
9 changed files with 429 additions and 15 deletions
|
@ -20,4 +20,7 @@ namespace ams::dd {
|
||||||
|
|
||||||
uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size);
|
uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size);
|
||||||
|
|
||||||
|
u32 ReadIoRegister(dd::PhysicalAddress phys_addr);
|
||||||
|
void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <vapours/results/nim_results.hpp>
|
#include <vapours/results/nim_results.hpp>
|
||||||
#include <vapours/results/ns_results.hpp>
|
#include <vapours/results/ns_results.hpp>
|
||||||
#include <vapours/results/os_results.hpp>
|
#include <vapours/results/os_results.hpp>
|
||||||
|
#include <vapours/results/pcv_results.hpp>
|
||||||
#include <vapours/results/pgl_results.hpp>
|
#include <vapours/results/pgl_results.hpp>
|
||||||
#include <vapours/results/pm_results.hpp>
|
#include <vapours/results/pm_results.hpp>
|
||||||
#include <vapours/results/psc_results.hpp>
|
#include <vapours/results/psc_results.hpp>
|
||||||
|
|
26
libraries/libvapours/include/vapours/results/pcv_results.hpp
Normal file
26
libraries/libvapours/include/vapours/results/pcv_results.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/results/results_common.hpp>
|
||||||
|
|
||||||
|
namespace ams::pcv {
|
||||||
|
|
||||||
|
R_DEFINE_NAMESPACE_RESULT_MODULE(133);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(IllegalRequest, 16);
|
||||||
|
|
||||||
|
}
|
|
@ -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(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(SET_SW_CLAMP_CRAIL, 0, 1);
|
||||||
|
|
||||||
DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3);
|
DEFINE_PMC_REG_TWO_BIT_ENUM(IO_DPD_REQ_CODE, 30, IDLE, DPD_OFF, DPD_ON, RESERVED3);
|
||||||
|
|
|
@ -66,4 +66,32 @@ namespace ams::dd {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 ReadIoRegister(dd::PhysicalAddress phys_addr) {
|
||||||
|
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
return reg::Read(dd::QueryIoMapping(phys_addr, sizeof(u32)));
|
||||||
|
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
|
||||||
|
u32 val;
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
#include <mesosphere.hpp>
|
||||||
|
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||||
|
#include <exosphere.hpp>
|
||||||
|
#else
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -23,6 +23,7 @@
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#endif
|
#endif
|
||||||
#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp"
|
#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp"
|
||||||
|
#include "sdmmc_io_impl.board.nintendo_nx.hpp"
|
||||||
#include "sdmmc_timer.hpp"
|
#include "sdmmc_timer.hpp"
|
||||||
|
|
||||||
namespace ams::sdmmc::impl {
|
namespace ams::sdmmc::impl {
|
||||||
|
@ -875,7 +876,9 @@ namespace ams::sdmmc::impl {
|
||||||
/* Nintendo sets the current bus power regardless of whether the call succeeds. */
|
/* Nintendo sets the current bus power regardless of whether the call succeeds. */
|
||||||
ON_SCOPE_EXIT { this->current_bus_power = BusPower_3_3V; };
|
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();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,23 +890,34 @@ namespace ams::sdmmc::impl {
|
||||||
|
|
||||||
/* If we're at 3.3V, lower to 1.8V. */
|
/* 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;
|
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;
|
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() {
|
Result Sdmmc1Controller::LowerBusPowerForRegisterControl() {
|
||||||
/* Nintendo sets the current bus power regardless of whether the call succeeds. */
|
/* Nintendo sets the current bus power regardless of whether the call succeeds. */
|
||||||
ON_SCOPE_EXIT { this->current_bus_power = BusPower_1_8V; };
|
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();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -911,14 +925,17 @@ namespace ams::sdmmc::impl {
|
||||||
SdHostStandardController::EnsureControl();
|
SdHostStandardController::EnsureControl();
|
||||||
|
|
||||||
if (IsSocMariko()) {
|
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 {
|
} else {
|
||||||
switch (bus_power) {
|
switch (bus_power) {
|
||||||
case BusPower_1_8V:
|
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;
|
break;
|
||||||
case BusPower_3_3V:
|
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;
|
break;
|
||||||
case BusPower_Off:
|
case BusPower_Off:
|
||||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
@ -1051,9 +1068,17 @@ namespace ams::sdmmc::impl {
|
||||||
this->is_pcv_control = false;
|
this->is_pcv_control = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* TODO: equivalent of pinmux::Initialize(); */
|
/* pinmux::Initialize(); */
|
||||||
/* TODO: equivalent of pinmux::OpenSession(std::addressof(this->pinmux_session), pinmux::AssignablePinGroupName_Sdmmc1); */
|
/* This just opens a session handle to pinmux service, no work to do. */
|
||||||
/* TODO: equivalent of pcv::Initialize(); */
|
|
||||||
|
/* 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. */
|
/* Perform base initialization. */
|
||||||
SdmmcController::Initialize();
|
SdmmcController::Initialize();
|
||||||
|
@ -1063,9 +1088,18 @@ namespace ams::sdmmc::impl {
|
||||||
/* Perform base finalization. */
|
/* Perform base finalization. */
|
||||||
SdmmcController::Finalize();
|
SdmmcController::Finalize();
|
||||||
|
|
||||||
/* TODO: equivalent of pcv::Finalize(); */
|
/* pcv::Finalize(); */
|
||||||
/* TODO: equivalent of pinmux::CloseSession(std::addressof(this->pinmux_session)); */
|
/* As with initialize, we mostly don't care about the globals this touches. */
|
||||||
/* TODO: equivalent of pinmux::Finalize(); */
|
/* 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)
|
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||||
/* Mark ourselves as initialized by register control. */
|
/* Mark ourselves as initialized by register control. */
|
||||||
|
@ -1099,4 +1133,172 @@ namespace ams::sdmmc::impl {
|
||||||
}
|
}
|
||||||
#endif
|
#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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,6 +210,25 @@ namespace ams::sdmmc::impl {
|
||||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||||
static constinit inline os::InterruptEventType s_interrupt_event{};
|
static constinit inline os::InterruptEventType s_interrupt_event{};
|
||||||
#endif
|
#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:
|
private:
|
||||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||||
/* TODO: pinmux::PinmuxSession pinmux_session; */
|
/* TODO: pinmux::PinmuxSession pinmux_session; */
|
||||||
|
@ -218,6 +237,8 @@ namespace ams::sdmmc::impl {
|
||||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||||
bool is_pcv_control;
|
bool is_pcv_control;
|
||||||
#endif
|
#endif
|
||||||
|
TYPED_STORAGE(PowerController) power_controller_storage;
|
||||||
|
PowerController *power_controller;
|
||||||
private:
|
private:
|
||||||
Result PowerOnForRegisterControl(BusPower bus_power);
|
Result PowerOnForRegisterControl(BusPower bus_power);
|
||||||
void PowerOffForRegisterControl();
|
void PowerOffForRegisterControl();
|
||||||
|
@ -381,6 +402,7 @@ namespace ams::sdmmc::impl {
|
||||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||||
this->is_pcv_control = false;
|
this->is_pcv_control = false;
|
||||||
#endif
|
#endif
|
||||||
|
this->power_controller = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Initialize() override;
|
virtual void Initialize() override;
|
||||||
|
|
Loading…
Reference in a new issue