mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 04:41:12 +00:00
sdmmc: implement clock reset controller for register api
This commit is contained in:
parent
397d0c4295
commit
37704d670b
10 changed files with 748 additions and 12 deletions
|
@ -51,6 +51,11 @@ namespace ams::reg {
|
||||||
return (EncodeValue(values) | ...);
|
return (EncodeValue(values) | ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename... Masks> requires ((sizeof...(Masks) > 0) && (std::is_same<Masks, BitsMask>::value && ...))
|
||||||
|
constexpr ALWAYS_INLINE u32 EncodeMask(const Masks... masks) {
|
||||||
|
return (EncodeMask(masks) | ...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
template<typename IntType> requires UnsignedNonConstIntegral<IntType>
|
||||||
ALWAYS_INLINE void Write(volatile IntType *reg, std::type_identity_t<IntType> val) { *reg = val; }
|
ALWAYS_INLINE void Write(volatile IntType *reg, std::type_identity_t<IntType> val) { *reg = val; }
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
//#define AMS_SDMMC_USE_OS_TIMER
|
//#define AMS_SDMMC_USE_OS_TIMER
|
||||||
#define AMS_SDMMC_USE_UTIL_TIMER
|
#define AMS_SDMMC_USE_UTIL_TIMER
|
||||||
//#define AMS_SDMMC_ENABLE_MMC_HS400
|
//#define AMS_SDMMC_ENABLE_MMC_HS400
|
||||||
|
//#define AMS_SDMMC_SET_PLLC4_BASE
|
||||||
|
|
||||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
//#define AMS_SDMMC_USE_OS_TIMER
|
//#define AMS_SDMMC_USE_OS_TIMER
|
||||||
#define AMS_SDMMC_USE_UTIL_TIMER
|
#define AMS_SDMMC_USE_UTIL_TIMER
|
||||||
//#define AMS_SDMMC_ENABLE_MMC_HS400
|
//#define AMS_SDMMC_ENABLE_MMC_HS400
|
||||||
|
//#define AMS_SDMMC_SET_PLLC4_BASE
|
||||||
|
|
||||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||||
|
|
||||||
|
@ -57,6 +59,7 @@
|
||||||
#define AMS_SDMMC_USE_OS_TIMER
|
#define AMS_SDMMC_USE_OS_TIMER
|
||||||
//#define AMS_SDMMC_USE_UTIL_TIMER
|
//#define AMS_SDMMC_USE_UTIL_TIMER
|
||||||
#define AMS_SDMMC_ENABLE_MMC_HS400
|
#define AMS_SDMMC_ENABLE_MMC_HS400
|
||||||
|
#define AMS_SDMMC_SET_PLLC4_BASE
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error "Unknown execution context for ams::sdmmc!"
|
#error "Unknown execution context for ams::sdmmc!"
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#define CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER (0x374)
|
#define CLK_RST_CONTROLLER_SUPER_CCLKLP_DIVIDER (0x374)
|
||||||
#define CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 (0x388)
|
#define CLK_RST_CONTROLLER_CPU_SOFTRST_CTRL2 (0x388)
|
||||||
#define CLK_RST_CONTROLLER_SPARE_REG0 (0x55C)
|
#define CLK_RST_CONTROLLER_SPARE_REG0 (0x55C)
|
||||||
|
#define CLK_RST_CONTROLLER_PLLC4_BASE (0x5A4)
|
||||||
|
|
||||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA (0x0F8)
|
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRA (0x0F8)
|
||||||
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB (0x0FC)
|
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRB (0x0FC)
|
||||||
|
@ -79,6 +80,10 @@ DEFINE_CLK_RST_REG(CPU_SOFTRST_CTRL2_CAR2PMC_CPU_ACK_WIDTH, 0, 12);
|
||||||
|
|
||||||
DEFINE_CLK_RST_REG_TWO_BIT_ENUM(SPARE_REG0_CLK_M_DIVISOR, 2, CLK_M_DIVISOR1, CLK_M_DIVISOR2, CLK_M_DIVISOR3, CLK_M_DIVISOR4);
|
DEFINE_CLK_RST_REG_TWO_BIT_ENUM(SPARE_REG0_CLK_M_DIVISOR, 2, CLK_M_DIVISOR1, CLK_M_DIVISOR2, CLK_M_DIVISOR3, CLK_M_DIVISOR4);
|
||||||
|
|
||||||
|
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_IDDQ, 18, OFF, ON);
|
||||||
|
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_LOCK, 27, NOT_LOCK, LOCK_FEQ_AND_PHASE);
|
||||||
|
DEFINE_CLK_RST_REG_BIT_ENUM(PLLC4_BASE_PLLC4_ENABLE, 30, DISABLE, ENABLE);
|
||||||
|
|
||||||
/* RST_DEVICES */
|
/* RST_DEVICES */
|
||||||
#define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004)
|
#define CLK_RST_CONTROLLER_RST_DEVICES_L (0x004)
|
||||||
#define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008)
|
#define CLK_RST_CONTROLLER_RST_DEVICES_H (0x008)
|
||||||
|
@ -98,16 +103,21 @@ DEFINE_CLK_RST_REG_TWO_BIT_ENUM(SPARE_REG0_CLK_M_DIVISOR, 2, CLK_M_DIVISOR1, CLK
|
||||||
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364)
|
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_W (0x364)
|
||||||
|
|
||||||
/* CLK_SOURCE */
|
/* CLK_SOURCE */
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 (0x124)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 (0x128)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 (0x150)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 (0x154)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 (0x164)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE (0x1D4)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA (0x178)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT (0x3B4)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB (0x17C)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC (0x1A0)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF (0x62C)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 (0x1BC)
|
||||||
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC (0x630)
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE (0x1D4)
|
||||||
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT (0x3B4)
|
||||||
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_ACTMON (0x3E8)
|
||||||
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_REF (0x62C)
|
||||||
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_DVFS_SOC (0x630)
|
||||||
|
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM (0x694)
|
||||||
|
|
||||||
/* RST_DEV_*_SET */
|
/* RST_DEV_*_SET */
|
||||||
#define CLK_RST_CONTROLLER_RST_DEV_L_SET (0x300)
|
#define CLK_RST_CONTROLLER_RST_DEV_L_SET (0x300)
|
||||||
|
@ -169,6 +179,19 @@ DEFINE_CLK_RST_REG(CLK_SOURCE_I2C1_I2C1_CLK_DIVISOR, 0, 8);
|
||||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C5_I2C5_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_I2C5_I2C5_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||||
DEFINE_CLK_RST_REG(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 0, 8);
|
DEFINE_CLK_RST_REG(CLK_SOURCE_I2C5_I2C5_CLK_DIVISOR, 0, 8);
|
||||||
|
|
||||||
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC1_SDMMC1_CLK_SRC, 29, PLLP_OUT0, PLLA_OUT, PLLC_OUT0, PLLC4_OUT2, PLLM_OUT0, PLLE_OUT0, CLK_M, PLLC4_OUT0);
|
||||||
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC2_SDMMC2_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
|
||||||
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC4_SDMMC4_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
|
||||||
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC3_SDMMC3_CLK_SRC, 29, PLLP_OUT0, PLLA_OUT, PLLC_OUT0, PLLC4_OUT2, PLLC4_OUT1, PLLE_OUT0, CLK_M, PLLC4_OUT0);
|
||||||
|
|
||||||
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, 29, PLLP_OUT0, _RSVD1_, _RSVD2_, PLLC4_OUT2, _RSVD4_, _RSVD5_, CLK_M, PLLC4_OUT0);
|
||||||
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, 29, PLLP_OUT0, PLLC4_OUT2_LJ, PLLC4_OUT0_LJ, PLLC4_OUT2, PLLC4_OUT1, PLLC4_OUT1_LJ, CLK_M, PLLC4_OUT0);
|
||||||
|
|
||||||
|
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC1_SDMMC1_CLK_DIVISOR, 0, 8);
|
||||||
|
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC2_SDMMC2_CLK_DIVISOR, 0, 8);
|
||||||
|
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC4_SDMMC4_CLK_DIVISOR, 0, 8);
|
||||||
|
DEFINE_CLK_RST_REG(CLK_SOURCE_SDMMC3_SDMMC3_CLK_DIVISOR, 0, 8);
|
||||||
|
|
||||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTA_UARTA_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTB_UARTB_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_UARTC_UARTC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||||
|
@ -184,6 +207,9 @@ DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_REF_DVFS_REF_CLK_SRC, 29, PLLP
|
||||||
DEFINE_CLK_RST_REG(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 0, 8);
|
DEFINE_CLK_RST_REG(CLK_SOURCE_DVFS_SOC_DVFS_SOC_DIVISOR, 0, 8);
|
||||||
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_DVFS_SOC_DVFS_SOC_CLK_SRC, 29, PLLP_OUT0, PLLC2_OUT0, PLLC_OUT0, PLLC4_OUT0, RESERVED4, PLLC4_OUT1, CLK_M, PLLC4_OUT2);
|
||||||
|
|
||||||
|
DEFINE_CLK_RST_REG(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 0, 8);
|
||||||
|
DEFINE_CLK_RST_REG_THREE_BIT_ENUM(CLK_SOURCE_LEGACY_TM_CLK_SRC, 29, PLLP_OUT3, PLLC_OUT0, PLLC2_OUT0, CLK_M, PLLP_OUT0, PLLC4_OUT0, PLLC4_OUT1, PLLC4_OUT2);
|
||||||
|
|
||||||
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_SET_SET_COP_RST, 1, DISABLE, ENABLE);
|
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_SET_SET_COP_RST, 1, DISABLE, ENABLE);
|
||||||
|
|
||||||
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_CLR_CLR_COP_RST, 1, DISABLE, ENABLE);
|
DEFINE_CLK_RST_REG_BIT_ENUM(RST_DEV_L_CLR_CLR_COP_RST, 1, DISABLE, ENABLE);
|
||||||
|
@ -204,6 +230,9 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA
|
||||||
HANDLER(L, RTC, 0, 4) \
|
HANDLER(L, RTC, 0, 4) \
|
||||||
HANDLER(L, TMR, 0, 5) \
|
HANDLER(L, TMR, 0, 5) \
|
||||||
HANDLER(L, GPIO, 0, 8) \
|
HANDLER(L, GPIO, 0, 8) \
|
||||||
|
HANDLER(L, SDMMC2, 0, 9) \
|
||||||
|
HANDLER(L, SDMMC1, 0, 14) \
|
||||||
|
HANDLER(L, SDMMC4, 0, 15) \
|
||||||
HANDLER(L, USBD, 0, 22) \
|
HANDLER(L, USBD, 0, 22) \
|
||||||
HANDLER(L, CACHE2, 0, 31) \
|
HANDLER(L, CACHE2, 0, 31) \
|
||||||
HANDLER(H, MEM, 1, 0) \
|
HANDLER(H, MEM, 1, 0) \
|
||||||
|
@ -215,6 +244,7 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA
|
||||||
HANDLER(H, I2C5, 1, 15) \
|
HANDLER(H, I2C5, 1, 15) \
|
||||||
HANDLER(H, EMC, 1, 25) \
|
HANDLER(H, EMC, 1, 25) \
|
||||||
HANDLER(H, USB2, 1, 26) \
|
HANDLER(H, USB2, 1, 26) \
|
||||||
|
HANDLER(U, SDMMC3, 2, 5) \
|
||||||
HANDLER(U, CSITE, 2, 9) \
|
HANDLER(U, CSITE, 2, 9) \
|
||||||
HANDLER(U, IRAMA, 2, 20) \
|
HANDLER(U, IRAMA, 2, 20) \
|
||||||
HANDLER(U, IRAMB, 2, 21) \
|
HANDLER(U, IRAMB, 2, 21) \
|
||||||
|
@ -244,6 +274,7 @@ DEFINE_CLK_RST_REG_BIT_ENUM(RST_CPUG_CMPLX_CLR_CLR_NONCPURESET, 29, DISABLE, ENA
|
||||||
HANDLER(X, GPU, 5, 24) \
|
HANDLER(X, GPU, 5, 24) \
|
||||||
HANDLER(X, DBGAPB, 5, 25) \
|
HANDLER(X, DBGAPB, 5, 25) \
|
||||||
HANDLER(X, PLLG_REF, 5, 29) \
|
HANDLER(X, PLLG_REF, 5, 29) \
|
||||||
|
HANDLER(Y, LEGACY_TM, 6, 1) \
|
||||||
HANDLER(Y, MC_CCPA, 6, 8) \
|
HANDLER(Y, MC_CCPA, 6, 8) \
|
||||||
HANDLER(Y, MC_CDPA, 6, 9) \
|
HANDLER(Y, MC_CDPA, 6, 9) \
|
||||||
HANDLER(Y, PLLP_OUT_CPU, 6, 31)
|
HANDLER(Y, PLLP_OUT_CPU, 6, 31)
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* 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_clock_reset_controller.hpp"
|
||||||
|
#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp"
|
||||||
|
#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp"
|
||||||
|
|
||||||
|
namespace ams::sdmmc::impl::ClockResetController {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constinit bool g_is_module_initialized[Module_Count] = {};
|
||||||
|
|
||||||
|
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||||
|
|
||||||
|
constinit os::SdkMutex g_module_mutex;
|
||||||
|
|
||||||
|
#define AMS_SDMMC_LOCK_MODULE_MUTEX() std::scoped_lock lk(g_module_mutex)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define AMS_SDMMC_LOCK_MODULE_MUTEX()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||||
|
constinit bool g_is_pcv_control = false;
|
||||||
|
|
||||||
|
#define AMS_SDMMC_IF_IS_PCV_CONTROL() if (g_is_pcv_control)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define AMS_SDMMC_IF_IS_PCV_CONTROL() if constexpr (false)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(Module module) {
|
||||||
|
/* Acquire exclusive access to module state. */
|
||||||
|
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||||
|
|
||||||
|
/* Mark the module as initialized. */
|
||||||
|
g_is_module_initialized[module] = true;
|
||||||
|
|
||||||
|
/* Initialize the module. */
|
||||||
|
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||||
|
ClockResetController::pcv::Initialize(module);
|
||||||
|
} else {
|
||||||
|
ClockResetController::reg::Initialize(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize(Module module) {
|
||||||
|
/* Acquire exclusive access to module state. */
|
||||||
|
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||||
|
|
||||||
|
/* Finalize the module. */
|
||||||
|
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||||
|
ClockResetController::pcv::Finalize(module);
|
||||||
|
} else {
|
||||||
|
ClockResetController::reg::Finalize(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark the module as finalized. */
|
||||||
|
g_is_module_initialized[module] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAvailable(Module module) {
|
||||||
|
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||||
|
return ClockResetController::pcv::IsAvailable(module);
|
||||||
|
} else {
|
||||||
|
return ClockResetController::reg::IsAvailable(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||||
|
void SwitchToPcvControl() {
|
||||||
|
/* Acquire exclusive access to module state. */
|
||||||
|
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||||
|
|
||||||
|
/* If we're already using pcv control, we don't need to do anything. */
|
||||||
|
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finalize all modules. */
|
||||||
|
for (int i = 0; i < Module_Count; ++i) {
|
||||||
|
if (g_is_module_initialized[i]) {
|
||||||
|
ClockResetController::reg::Finalize(static_cast<Module>(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark that we've switched to pcv control. */
|
||||||
|
|
||||||
|
/* Initialize modules using pcv control. */
|
||||||
|
for (int i = 0; i < Module_Count; ++i) {
|
||||||
|
if (g_is_module_initialized[i]) {
|
||||||
|
ClockResetController::pcv::Initialize(static_cast<Module>(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||||
|
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||||
|
return ClockResetController::pcv::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency);
|
||||||
|
} else {
|
||||||
|
return ClockResetController::reg::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssertReset(Module module) {
|
||||||
|
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||||
|
return ClockResetController::pcv::AssertReset(module);
|
||||||
|
} else {
|
||||||
|
return ClockResetController::reg::AssertReset(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||||
|
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||||
|
return ClockResetController::pcv::ReleaseReset(module, target_frequency_khz);
|
||||||
|
} else {
|
||||||
|
return ClockResetController::reg::ReleaseReset(module, target_frequency_khz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* 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_clock_reset_controller.pcv.board.nintendo_nx.hpp"
|
||||||
|
|
||||||
|
namespace ams::sdmmc::impl::ClockResetController::pcv {
|
||||||
|
|
||||||
|
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||||
|
|
||||||
|
void Initialize(Module module) {
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
AMS_ABORT("PCV Control not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize(Module module) {
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
AMS_ABORT("PCV Control not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAvailable(Module module) {
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
AMS_ABORT("PCV Control not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||||
|
AMS_UNUSED(out_actual_frequency, module, target_frequency);
|
||||||
|
AMS_ABORT("PCV Control not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssertReset(Module module) {
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
AMS_ABORT("PCV Control not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||||
|
AMS_UNUSED(module, target_frequency_khz);
|
||||||
|
AMS_ABORT("PCV Control not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
void Initialize(Module module) {
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
AMS_ABORT("PCV Control not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize(Module module) {
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
AMS_ABORT("PCV Control not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAvailable(Module module) {
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
AMS_ABORT("PCV Control not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||||
|
AMS_UNUSED(out_actual_frequency, module, target_frequency);
|
||||||
|
AMS_ABORT("PCV Control not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssertReset(Module module) {
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
AMS_ABORT("PCV Control not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||||
|
AMS_UNUSED(module, target_frequency_khz);
|
||||||
|
AMS_ABORT("PCV Control not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
#include "sdmmc_clock_reset_controller.hpp"
|
||||||
|
|
||||||
|
namespace ams::sdmmc::impl::ClockResetController::pcv {
|
||||||
|
|
||||||
|
void Initialize(Module module);
|
||||||
|
void Finalize(Module module);
|
||||||
|
bool IsAvailable(Module module);
|
||||||
|
|
||||||
|
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
|
||||||
|
void AssertReset(Module module);
|
||||||
|
void ReleaseReset(Module module, u32 target_frequency_khz);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,391 @@
|
||||||
|
/*
|
||||||
|
* 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_clock_reset_controller.reg.board.nintendo_nx.hpp"
|
||||||
|
#include "sdmmc_timer.hpp"
|
||||||
|
|
||||||
|
namespace ams::sdmmc::impl::ClockResetController::reg {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||||
|
|
||||||
|
constinit os::SdkMutex g_init_mutex;
|
||||||
|
|
||||||
|
#define AMS_SDMMC_LOCK_INIT_MUTEX() std::scoped_lock lk(g_init_mutex)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define AMS_SDMMC_LOCK_INIT_MUTEX()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
constinit bool g_is_initialized = false;
|
||||||
|
|
||||||
|
struct ModuleInfo {
|
||||||
|
u32 target_frequency_khz;
|
||||||
|
u32 actual_frequency_khz;
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit ModuleInfo g_module_infos[Module_Count] = {};
|
||||||
|
|
||||||
|
constexpr inline dd::PhysicalAddress ClockResetControllerRegistersPhysicalAddress = UINT64_C(0x60006000);
|
||||||
|
constexpr inline size_t ClockResetControllerRegistersSize = 4_KB;
|
||||||
|
|
||||||
|
constinit uintptr_t g_clkrst_registers_address = 0;
|
||||||
|
|
||||||
|
[[maybe_unused]] void InitializePllc4() {
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Check if PLLC4_BASE has the expected value; if it does, we have nothing to do. */
|
||||||
|
constexpr u32 ExpectedPllc4Base = 0x58006804;
|
||||||
|
if (ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE) == ExpectedPllc4Base) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable PLLC4_ENABLE, if it's currently set. */
|
||||||
|
if (ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE))) {
|
||||||
|
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, DISABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Operate on the register with PLLC4_ENABLE cleared. */
|
||||||
|
{
|
||||||
|
/* Clear IDDQ, read to be sure it takes, wait 5 us. */
|
||||||
|
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_IDDQ, OFF));
|
||||||
|
ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE);
|
||||||
|
WaitMicroSeconds(5);
|
||||||
|
|
||||||
|
/* Write the expected value sans IDDQ/PLLC4_ENABLE. */
|
||||||
|
constexpr u32 ExpectedPllc4BaseMask = ~ams::reg::EncodeMask(CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_ENABLE),
|
||||||
|
CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_IDDQ));
|
||||||
|
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, ExpectedPllc4Base & ExpectedPllc4BaseMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write PLLC4_ENABLE, and read to be sure our configuration takes. */
|
||||||
|
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE));
|
||||||
|
ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE);
|
||||||
|
|
||||||
|
/* Wait up to 1s for changes to take. */
|
||||||
|
{
|
||||||
|
ManualTimer timer(1000);
|
||||||
|
while (true) {
|
||||||
|
/* Check if we're done. */
|
||||||
|
if (!ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_LOCK, NOT_LOCK))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that we haven't timed out. */
|
||||||
|
AMS_ABORT_UNLESS(timer.Update());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeLegacyTmClk() {
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Configure the legacy tm clock as 12MHz. */
|
||||||
|
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_LEGACY_TM_CLK_SRC, PLLP_OUT0),
|
||||||
|
CLK_RST_REG_BITS_VALUE(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 66));
|
||||||
|
|
||||||
|
/* Enable clock to the legacy tm. */
|
||||||
|
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_CLK_ENB_LEGACY_TM, ENABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsResetReleased(Module module) {
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Get the reset bit from RST_DEVICES_* */
|
||||||
|
switch (module) {
|
||||||
|
case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC1_RST, DISABLE));
|
||||||
|
case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC2_RST, DISABLE));
|
||||||
|
case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_REG_BITS_ENUM(RST_DEVICES_U_SWR_SDMMC3_RST, DISABLE));
|
||||||
|
case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC4_RST, DISABLE));
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetReset(Module module) {
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Set reset in RST_DEV_*_SET */
|
||||||
|
switch (module) {
|
||||||
|
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE));
|
||||||
|
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE));
|
||||||
|
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE));
|
||||||
|
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE));
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearReset(Module module) {
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Set reset in RST_DEV_*_CLR */
|
||||||
|
switch (module) {
|
||||||
|
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE));
|
||||||
|
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE));
|
||||||
|
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE));
|
||||||
|
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE));
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsClockEnabled(Module module) {
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Get the enable bit from CLK_OUT_ENB_* */
|
||||||
|
switch (module) {
|
||||||
|
case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||||
|
case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||||
|
case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||||
|
case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetClockEnable(Module module) {
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Set clock enable bit in CLK_ENB_*_SET */
|
||||||
|
switch (module) {
|
||||||
|
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||||
|
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||||
|
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||||
|
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearClockEnable(Module module) {
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Set clock enable bit in CLK_ENB_*_CLR */
|
||||||
|
switch (module) {
|
||||||
|
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||||
|
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||||
|
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||||
|
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetClockSourceSdmmc(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) {
|
||||||
|
/* Check that we can write to output. */
|
||||||
|
AMS_ABORT_UNLESS(out_actual_frequency_khz != nullptr);
|
||||||
|
|
||||||
|
/* Determine frequency/divisor. */
|
||||||
|
u32 clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLP_OUT0));
|
||||||
|
u8 n;
|
||||||
|
switch (target_frequency_khz) {
|
||||||
|
case 25'000:
|
||||||
|
*out_actual_frequency_khz = 24'728;
|
||||||
|
n = 31;
|
||||||
|
break;
|
||||||
|
case 26'000:
|
||||||
|
*out_actual_frequency_khz = 25'500;
|
||||||
|
n = 30;
|
||||||
|
break;
|
||||||
|
case 40'800:
|
||||||
|
*out_actual_frequency_khz = 40'800;
|
||||||
|
n = 18;
|
||||||
|
break;
|
||||||
|
case 50'000:
|
||||||
|
*out_actual_frequency_khz = 48'000;
|
||||||
|
n = 15;
|
||||||
|
break;
|
||||||
|
case 52'000:
|
||||||
|
*out_actual_frequency_khz = 51'000;
|
||||||
|
n = 14;
|
||||||
|
break;
|
||||||
|
case 100'000:
|
||||||
|
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||||
|
*out_actual_frequency_khz = 99'840;
|
||||||
|
n = 2;
|
||||||
|
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2));
|
||||||
|
#else
|
||||||
|
*out_actual_frequency_khz = 90'667;
|
||||||
|
n = 7;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case 200'000:
|
||||||
|
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||||
|
*out_actual_frequency_khz = 199'680;
|
||||||
|
n = 0;
|
||||||
|
if (module == Module_Sdmmc2 || module == Module_Sdmmc4) {
|
||||||
|
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, PLLC4_OUT2_LJ));
|
||||||
|
} else {
|
||||||
|
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
*out_actual_frequency_khz = 163'200;
|
||||||
|
n = 3;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case 208'000:
|
||||||
|
*out_actual_frequency_khz = 204'000;
|
||||||
|
n = 2;
|
||||||
|
break;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set frequencies in module info. */
|
||||||
|
g_module_infos[module].target_frequency_khz = target_frequency_khz;
|
||||||
|
g_module_infos[module].actual_frequency_khz = *out_actual_frequency_khz;
|
||||||
|
|
||||||
|
/* Check that we have registers we can write to. */
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Update the clock source. */
|
||||||
|
switch (module) {
|
||||||
|
case Module_Sdmmc1: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1, clk_m | static_cast<u32>(n)); break;
|
||||||
|
case Module_Sdmmc2: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2, clk_m | static_cast<u32>(n)); break;
|
||||||
|
case Module_Sdmmc3: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3, clk_m | static_cast<u32>(n)); break;
|
||||||
|
case Module_Sdmmc4: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4, clk_m | static_cast<u32>(n)); break;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureControl(Module module) {
|
||||||
|
/* Read from RST_DEVICES_* to be sure previous configuration takes. */
|
||||||
|
switch (module) {
|
||||||
|
case Module_Sdmmc1: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||||
|
case Module_Sdmmc2: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||||
|
case Module_Sdmmc3: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U); break;
|
||||||
|
case Module_Sdmmc4: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(Module module) {
|
||||||
|
/* Initialization isn't module specific. */
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
|
||||||
|
/* Acquire exclusive access to the initialization lock. */
|
||||||
|
AMS_SDMMC_LOCK_INIT_MUTEX();
|
||||||
|
|
||||||
|
/* If we've already initialized, we don't need to do anything. */
|
||||||
|
if (g_is_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear module infos. */
|
||||||
|
std::memset(g_module_infos, 0, sizeof(g_module_infos));
|
||||||
|
|
||||||
|
/* Get the registers address. */
|
||||||
|
g_clkrst_registers_address = dd::QueryIoMapping(ClockResetControllerRegistersPhysicalAddress, ClockResetControllerRegistersSize);
|
||||||
|
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||||
|
|
||||||
|
/* Perform register initialization. */
|
||||||
|
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||||
|
InitializePllc4();
|
||||||
|
#endif
|
||||||
|
InitializeLegacyTmClk();
|
||||||
|
|
||||||
|
/* Mark that we've initialized. */
|
||||||
|
g_is_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize(Module module) {
|
||||||
|
/* Nothing is needed for finalization. */
|
||||||
|
AMS_UNUSED(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsAvailable(Module module) {
|
||||||
|
return IsResetReleased(module) && IsClockEnabled(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetClockFrequencyKHz(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) {
|
||||||
|
/* If we're not changing the clock frequency, we don't need to do anything. */
|
||||||
|
if (target_frequency_khz == g_module_infos[module].target_frequency_khz) {
|
||||||
|
*out_actual_frequency_khz = g_module_infos[module].actual_frequency_khz;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Temporarily disable clock. */
|
||||||
|
const bool clock_enabled = IsClockEnabled(module);
|
||||||
|
if (clock_enabled) {
|
||||||
|
ClearClockEnable(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the clock source. */
|
||||||
|
SetClockSourceSdmmc(out_actual_frequency_khz, module, target_frequency_khz);
|
||||||
|
|
||||||
|
/* Re-enable clock, if we should. */
|
||||||
|
if (clock_enabled) {
|
||||||
|
SetClockEnable(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure that our configuration takes. */
|
||||||
|
EnsureControl(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssertReset(Module module) {
|
||||||
|
/* Set reset and disable clock. */
|
||||||
|
SetReset(module);
|
||||||
|
ClearClockEnable(module);
|
||||||
|
|
||||||
|
/* Ensure that our configuration takes. */
|
||||||
|
EnsureControl(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||||
|
/* Disable clock if it's enabled. */
|
||||||
|
if (IsClockEnabled(module)) {
|
||||||
|
ClearClockEnable(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set reset. */
|
||||||
|
SetReset(module);
|
||||||
|
|
||||||
|
/* Set the clock source. */
|
||||||
|
u32 actual_source_frequency_khz;
|
||||||
|
SetClockSourceSdmmc(std::addressof(actual_source_frequency_khz), module, target_frequency_khz);
|
||||||
|
|
||||||
|
/* Enable clock. */
|
||||||
|
SetClockEnable(module);
|
||||||
|
|
||||||
|
/* Ensure that our configuration takes. */
|
||||||
|
EnsureControl(module);
|
||||||
|
|
||||||
|
/* Wait 100 clocks. */
|
||||||
|
WaitClocks(100, actual_source_frequency_khz);
|
||||||
|
|
||||||
|
/* Clear reset. */
|
||||||
|
ClearReset(module);
|
||||||
|
|
||||||
|
/* Ensure that our configuration takes. */
|
||||||
|
EnsureControl(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
#include "sdmmc_clock_reset_controller.hpp"
|
||||||
|
|
||||||
|
namespace ams::sdmmc::impl::ClockResetController::reg {
|
||||||
|
|
||||||
|
void Initialize(Module module);
|
||||||
|
void Finalize(Module module);
|
||||||
|
bool IsAvailable(Module module);
|
||||||
|
|
||||||
|
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
|
||||||
|
void AssertReset(Module module);
|
||||||
|
void ReleaseReset(Module module, u32 target_frequency_khz);
|
||||||
|
|
||||||
|
}
|
|
@ -300,7 +300,7 @@ namespace ams::sdmmc::impl {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||||
constinit os::Mutex g_i2c_init_mutex(false);
|
constinit os::SdkMutex g_i2c_init_mutex;
|
||||||
|
|
||||||
#define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() std::scoped_lock lk(g_i2c_init_mutex)
|
#define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() std::scoped_lock lk(g_i2c_init_mutex)
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,7 @@ namespace ams::sdmmc::impl {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||||
constinit os::Mutex g_soc_mutex(false);
|
constinit os::SdkMutex g_soc_mutex;
|
||||||
|
|
||||||
#define AMS_SDMMC_LOCK_SOC_MUTEX() std::scoped_lock lk(g_soc_mutex)
|
#define AMS_SDMMC_LOCK_SOC_MUTEX() std::scoped_lock lk(g_soc_mutex)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue