-
-
Save TuxSH/b4f91a84865febf5c5a5e2bec5d4228b to your computer and use it in GitHub Desktop.
3DS kernel sleep code -- called with DMA stopped & L2C disabled
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void __fastcall SleepSystemImpl(s32 srcCoreId) | |
{ | |
s32 currentCoreId; // r4 | |
u32 v3; // r0 | |
KInterruptControllerState interruptControllerState; // [sp+0h] [bp-40h] | |
KIrqAcknowledgmentInfo v5; // [sp+20h] [bp-20h] | |
// This function is executed on a special-purpose stack guaranteed not to live in FCRAM nor VRAM | |
// | |
// All cores execute this function at once, and do so in irq mode (called from SGI 6 handler) | |
currentCoreId = __mrc(15, 0, 0, 0, 5) & 3; | |
cpu::SynchronizeAllCores(); | |
KInterruptController::DumpAndDisableLocal(&interruptControllerState);// | |
// dump interrupt controller state & disable all interrupts (4 * 32 max) on the distributor: | |
// | |
// - SGIs: priority -> 15 (lowest, same as if they were disabled (can't actually disable them)) | |
// - Other interrupts (PPIs, SPIs): write to icenabler bits | |
if ( currentCoreId == srcCoreId ) | |
KInterruptController::DumpAndDisableShared(&interruptControllerState); | |
KPageTable::CleanInvalidateEntireDataCacheLocal(); | |
cpu::SynchronizeAllCores(); | |
KInterruptController::EndOfInterrupt(KINTNAME_SGI_CONTROL_SYSTEM, srcCoreId);// need to deactivate the SGI now as it hasn't been done yet | |
KInterruptController::SetInterruptPriority(KINTNAME_SGI_CONTROL_SYSTEM, 0);// highest prio | |
cpu::SynchronizeAllCores(); | |
if ( currentCoreId == srcCoreId ) | |
{ | |
KInterruptController::ClearInterruptPendingStatus(KINTNAME_PDN); | |
KInterruptController::EnableWithPriorityAndTarget(KINTNAME_PDN, currentCoreId, 0);// highest prio for the PDN interrupt | |
SetVramInSelfRefreshMode(); // it's the responsability of the gsp sysmodule to restore normal VRAM operation on wakeup | |
KPageTable::CleanInvalidateEntireDataCacheLocal(); | |
while ( LOBYTE(PXI.sync) != 1 ) // | |
// pdn has called pxi:mc Sleep(bool enabled) (cmd5) prior to this | |
// | |
// This makes: | |
// - the Arm11 pxi sysmodule not send any other commands to the Arm9 until disabled | |
// - Process9 call ControlSystem with "Sleep", which makes Kernel9 enter this below sync loop, | |
// disable all interrupts except "PXI cpu1 sync" and wfi | |
// | |
// NOTE: LGY_FIRM checks LGY_MODE (bits 15 (en), and bits1:0 (mode)) and doesn't do this sync for TWL_FIRM (makes sense). | |
; | |
SetFcramInSelfRefreshMode(); // Not done in LGY_FIRM | |
do | |
{ | |
PDN_CNT = 1; // Go to sleep | |
__wfi(); | |
} | |
while ( !KInterruptController::IsInterruptPending(KINTNAME_PDN) );// when we wake up, PDN asserts its interrupt (0x58) | |
SetFcramInNormalMode(); // skipped in LGY_FIRM | |
v3 = MPCORE.scu.cfgr; | |
MPCORE.gicd.sgir = (((1 << ((v3 & 3) + 1)) - 1) << 16) & 0xFF0000 | KINTNAME_SGI_CONTROL_SYSTEM;// resend the SGI as we will return to the irq handler | |
} | |
else | |
{ | |
do | |
__wfi(); | |
while ( !KInterruptController::IsInterruptPending(KINTNAME_SGI_CONTROL_SYSTEM) ); | |
} | |
KInterruptController::AcknowledgeIrq(&v5); // irq 6: pending -> active (because that's the expected state for where we're called from the irq handler) | |
cpu::SynchronizeAllCores(); | |
if ( currentCoreId == srcCoreId ) | |
KInterruptController::EnableAndRestoreShared(&interruptControllerState);// see comment about "disable" | |
KInterruptController::EnableAndRestoreLocal(&interruptControllerState); | |
cpu::SynchronizeAllCores(); | |
} | |
/////////////////////////////////////////////////////////////////////////// | |
void SetVramInSelfRefreshMode(void) | |
{ | |
vu16 v0; // r2 | |
v0 = PDN_CNT; | |
if ( !((v0 & 0x8000u) >> 15) ) | |
{ | |
GX_VRAM_BANKS_DISABLE |= 0xF00u; // disable all 8 VRAM banks | |
PDN_GPU_CNT = ~0x10000u; // disable GPU clock, this also controls the VRAM and LCD clocks | |
while ( !((PDN_CNT & 0x8000u) >> 15) ) // wait for VRAM to be in self-refresh mode | |
; | |
} | |
} | |
void SetFcramInSelfRefreshMode(void) | |
{ | |
vu16 v0; // r1 | |
__dummy_read(0xE0000000); // dummy fcram+0 read | |
KPageTable::InvalidateDataCacheRangeLocal(0xE0000000, 0x20u);// | |
// invalidate following the last read (because it might have allocated a cache line) | |
cpu::WaitCycles(0xB80u); | |
v0 = PDN_FCRAM_CNT; | |
PDN_FCRAM_CNT = v0 & ~2; // disable FCRAM clock | |
while ( !(~((unsigned int)PDN_FCRAM_CNT >> 2) & 1) )// wait for ack | |
; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment