Skip to content

Instantly share code, notes, and snippets.

@TuxSH

TuxSH/sleep.cpp Secret

Last active February 23, 2024 18:49
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TuxSH/b4f91a84865febf5c5a5e2bec5d4228b to your computer and use it in GitHub Desktop.
Save TuxSH/b4f91a84865febf5c5a5e2bec5d4228b to your computer and use it in GitHub Desktop.
3DS kernel sleep code -- called with DMA stopped & L2C disabled
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