diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index a6f664957..5823ba71d 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include "../secmon_cpu_context.hpp" #include "../secmon_error.hpp" #include "secmon_smc_power_management.hpp" @@ -21,8 +22,78 @@ namespace ams::secmon::smc { namespace { + constexpr inline uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress(); + constexpr inline uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress(); + constinit bool g_charger_hi_z_mode_enabled = false; + constinit const reg::BitsMask CpuPowerGateStatusMasks[NumCores] = { + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE0), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE1), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE2), + PMC_REG_BITS_MASK(PWRGATE_STATUS_CE3), + }; + + constinit const APBDEV_PMC_PWRGATE_TOGGLE_PARTID CpuPowerGateTogglePartitionIds[NumCores] = { + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3, + }; + + bool IsCpuPoweredOn(const reg::BitsMask mask) { + return reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, REG_BITS_VALUE_FROM_MASK(mask, APBDEV_PMC_PWRGATE_STATUS_STATUS_ON)); + } + + void PowerOnCpu(const reg::BitsMask mask, u32 toggle_partid) { + /* If the cpu is already on, we have nothing to do. */ + if (IsCpuPoweredOn(mask)) { + return; + } + + /* Wait until nothing is being powergated. */ + int timeout = 5000; + while (true) { + if (reg::HasValue(APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) { + break; + } + + util::WaitMicroSeconds(1); + if ((--timeout) < 0) { + /* NOTE: Nintendo doesn't do any error handling here... */ + return; + } + } + + /* Toggle on the cpu partition. */ + reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE), + PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, toggle_partid)); + + /* Wait up to 5000 us for the powergate to complete. */ + timeout = 5000; + while (true) { + if (IsCpuPoweredOn(mask)) { + break; + } + + util::WaitMicroSeconds(1); + if ((--timeout) < 0) { + /* NOTE: Nintendo doesn't do any error handling here... */ + return; + } + } + } + + void ResetCpu(int which_core) { + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */ + REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ + } + + void StartCpu(int which_core) { + reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */ + REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */ + } + } SmcResult SmcPowerOffCpu(const SmcArguments &args) { @@ -31,8 +102,30 @@ namespace ams::secmon::smc { } SmcResult SmcPowerOnCpu(const SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + /* Get and validate the core to power on. */ + const int which_core = args.r[1]; + if (!(0 <= which_core && which_core < NumCores)) { + return SmcResult::PsciInvalidParameters; + } + + /* Ensure the core isn't already on. */ + if (IsCoreOn(which_core)) { + return SmcResult::PsciAlreadyOn; + } + + /* Save the entry context. */ + SetEntryContext(which_core, args.r[2], args.r[3]); + + /* Reset the cpu. */ + ResetCpu(which_core); + + /* Turn on the core. */ + PowerOnCpu(CpuPowerGateStatusMasks[which_core], CpuPowerGateTogglePartitionIds[which_core]); + + /* Start the core. */ + StartCpu(which_core); + + return SmcResult::PsciSuccess; } SmcResult SmcSuspendCpu(const SmcArguments &args) { diff --git a/libraries/libexosphere/include/exosphere/reg.hpp b/libraries/libexosphere/include/exosphere/reg.hpp index a16d66193..aa0de0831 100644 --- a/libraries/libexosphere/include/exosphere/reg.hpp +++ b/libraries/libexosphere/include/exosphere/reg.hpp @@ -18,14 +18,18 @@ namespace ams::reg { - using BitsValue = std::tuple; - using BitsMask = std::tuple; + using BitsValue = std::tuple; + using BitsMask = std::tuple; - constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return std::get<0>(v); } - constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return std::get<0>(v); } - constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return std::get<1>(v); } - constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return std::get<1>(v); } - constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return std::get<2>(v); } + constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return static_cast(std::get<0>(v)); } + constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return static_cast(std::get<0>(v)); } + constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return static_cast(std::get<1>(v)); } + constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return static_cast(std::get<1>(v)); } + constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return static_cast(std::get<2>(v)); } + + constexpr ALWAYS_INLINE ::ams::reg::BitsValue GetValue(const BitsMask m, const u32 v) { + return ::ams::reg::BitsValue{GetOffset(m), GetWidth(m), v}; + } constexpr ALWAYS_INLINE u32 EncodeMask(const BitsMask v) { return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(v); @@ -138,6 +142,8 @@ namespace ams::reg { #define REG_BITS_MASK(OFFSET, WIDTH) ::ams::reg::BitsMask{OFFSET, WIDTH} #define REG_BITS_VALUE(OFFSET, WIDTH, VALUE) ::ams::reg::BitsValue{OFFSET, WIDTH, VALUE} + #define REG_BITS_VALUE_FROM_MASK(MASK, VALUE) ::ams::reg::GetValue(MASK, VALUE) + #define REG_NAMED_BITS_MASK(PREFIX, NAME) REG_BITS_MASK(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH) #define REG_NAMED_BITS_VALUE(PREFIX, NAME, VALUE) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, VALUE) #define REG_NAMED_BITS_ENUM(PREFIX, NAME, ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, PREFIX##_##NAME##_##ENUM) diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp index 1529b566f..3659194aa 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_clkrst.hpp @@ -71,6 +71,10 @@ DEFINE_CLK_RST_REG(MISC_CLK_ENB_CFG_ALL_VISIBLE, 28, 1); #define CLK_RST_CONTROLLER_CLK_ENB_UARTC_INDEX (0x17) #define CLK_RST_CONTROLLER_CLK_ENB_ACTMON_INDEX (0x17) +/* RST_CPUG_CMPLX_* */ +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET (0x450) +#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR (0x454) + DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_ARC_CLK_OVR_ON, 19, OFF, ON); DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSEC_CLK_OVR_ON, 20, OFF, ON); DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSECB_CLK_OVR_ON, 21, OFF, ON); diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp index ee10391fa..27a809567 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_pmc.hpp @@ -103,6 +103,41 @@ DEFINE_PMC_REG_BIT_ENUM(DPD_SAMPLE_ON, 0, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_ON, 0, DISABLE, ENABLE); DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_TSC_MULT_EN, 1, DISABLE, ENABLE); +DEFINE_PMC_REG_BIT_ENUM(PWRGATE_TOGGLE_START, 8, DISABLE, ENABLE); + +DEFINE_PMC_REG(PWRGATE_TOGGLE_PARTID, 0, 5); + +enum APBDEV_PMC_PWRGATE_TOGGLE_PARTID : u8 { + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CRAIL = 0, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VE = 2, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_PCX = 3, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_MPE = 6, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_SAX = 8, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1 = 9, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2 = 10, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3 = 11, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0 = 14, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_C0NC = 15, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_SOR = 17, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DIS = 18, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DISB = 19, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBA = 20, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBB = 21, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_XUSBC = 22, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VIC = 23, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_IRAM = 24, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_NVDEC = 25, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_NVJPG = 26, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_AUD = 27, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_DFD = 28, + APBDEV_PMC_PWRGATE_TOGGLE_PARTID_VE2 = 29, +}; + +enum APBDEV_PMC_PWRGATE_STATUS_STATUS { + APBDEV_PMC_PWRGATE_STATUS_STATUS_OFF = 0, + APBDEV_PMC_PWRGATE_STATUS_STATUS_ON = 1, +}; + DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_CRAIL, 0, OFF, ON); DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE, 2, OFF, ON); DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_PCX, 3, OFF, ON);