mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-11 10:32:33 +00:00
exo2: implement SmcPowerCpuOn
This commit is contained in:
parent
ab703646d5
commit
3d6baf96a3
4 changed files with 147 additions and 9 deletions
|
@ -14,6 +14,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <exosphere.hpp>
|
#include <exosphere.hpp>
|
||||||
|
#include "../secmon_cpu_context.hpp"
|
||||||
#include "../secmon_error.hpp"
|
#include "../secmon_error.hpp"
|
||||||
#include "secmon_smc_power_management.hpp"
|
#include "secmon_smc_power_management.hpp"
|
||||||
|
|
||||||
|
@ -21,8 +22,78 @@ namespace ams::secmon::smc {
|
||||||
|
|
||||||
namespace {
|
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 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) {
|
SmcResult SmcPowerOffCpu(const SmcArguments &args) {
|
||||||
|
@ -31,8 +102,30 @@ namespace ams::secmon::smc {
|
||||||
}
|
}
|
||||||
|
|
||||||
SmcResult SmcPowerOnCpu(const SmcArguments &args) {
|
SmcResult SmcPowerOnCpu(const SmcArguments &args) {
|
||||||
/* TODO */
|
/* Get and validate the core to power on. */
|
||||||
return SmcResult::NotImplemented;
|
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) {
|
SmcResult SmcSuspendCpu(const SmcArguments &args) {
|
||||||
|
|
|
@ -18,14 +18,18 @@
|
||||||
|
|
||||||
namespace ams::reg {
|
namespace ams::reg {
|
||||||
|
|
||||||
using BitsValue = std::tuple<u32, u32, u32>;
|
using BitsValue = std::tuple<u16, u16, u32>;
|
||||||
using BitsMask = std::tuple<u32, u32>;
|
using BitsMask = std::tuple<u16, u16>;
|
||||||
|
|
||||||
constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return std::get<0>(v); }
|
constexpr ALWAYS_INLINE u32 GetOffset(const BitsMask v) { return static_cast<u32>(std::get<0>(v)); }
|
||||||
constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return std::get<0>(v); }
|
constexpr ALWAYS_INLINE u32 GetOffset(const BitsValue v) { return static_cast<u32>(std::get<0>(v)); }
|
||||||
constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return std::get<1>(v); }
|
constexpr ALWAYS_INLINE u32 GetWidth(const BitsMask v) { return static_cast<u32>(std::get<1>(v)); }
|
||||||
constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return std::get<1>(v); }
|
constexpr ALWAYS_INLINE u32 GetWidth(const BitsValue v) { return static_cast<u32>(std::get<1>(v)); }
|
||||||
constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return std::get<2>(v); }
|
constexpr ALWAYS_INLINE u32 GetValue(const BitsValue v) { return static_cast<u32>(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) {
|
constexpr ALWAYS_INLINE u32 EncodeMask(const BitsMask v) {
|
||||||
return (~0u >> (BITSIZEOF(u32) - GetWidth(v))) << GetOffset(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_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(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_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_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)
|
#define REG_NAMED_BITS_ENUM(PREFIX, NAME, ENUM) REG_BITS_VALUE(PREFIX##_##NAME##_OFFSET, PREFIX##_##NAME##_WIDTH, PREFIX##_##NAME##_##ENUM)
|
||||||
|
|
|
@ -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_UARTC_INDEX (0x17)
|
||||||
#define CLK_RST_CONTROLLER_CLK_ENB_ACTMON_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_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_TSEC_CLK_OVR_ON, 20, OFF, ON);
|
||||||
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSECB_CLK_OVR_ON, 21, OFF, ON);
|
DEFINE_CLK_RST_REG_BIT_ENUM(LVL2_CLK_GATE_OVRD_TSECB_CLK_OVR_ON, 21, OFF, ON);
|
||||||
|
|
|
@ -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_ON, 0, DISABLE, ENABLE);
|
||||||
DEFINE_PMC_REG_BIT_ENUM(DPD_ENABLE_TSC_MULT_EN, 1, 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_CRAIL, 0, OFF, ON);
|
||||||
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE, 2, OFF, ON);
|
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_VE, 2, OFF, ON);
|
||||||
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_PCX, 3, OFF, ON);
|
DEFINE_PMC_REG_BIT_ENUM(PWRGATE_STATUS_PCX, 3, OFF, ON);
|
||||||
|
|
Loading…
Reference in a new issue