I'm trying to figure out how suspend/wakeup is implemented for mach-omap2
, specifically in Linux 2.6.37 targeted for TI OMAP3530/DM3730.
Here's some related code:
http://lxr.free-electrons.com/source/arch/arm/mach-omap2/pm34xx.c?v=2.6.37
static int omap3_pm_suspend(void)
{
struct power_state *pwrst;
int state, ret = 0;
if (wakeup_timer_seconds || wakeup_timer_milliseconds)
omap2_pm_wakeup_on_timer(wakeup_timer_seconds,
wakeup_timer_milliseconds);
/* Read current next_pwrsts */
list_for_each_entry(pwrst, &pwrst_list, node)
pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
/* Set ones wanted by suspend */
list_for_each_entry(pwrst, &pwrst_list, node) {
if (omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state))
goto restore;
if (pwrdm_clear_all_prev_pwrst(pwrst->pwrdm))
goto restore;
}
omap_uart_prepare_suspend();
omap3_intc_suspend();
omap_sram_idle();
restore:
/* Restore next_pwrsts */
list_for_each_entry(pwrst, &pwrst_list, node) {
state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
if (state > pwrst->next_state) {
printk(KERN_INFO "Powerdomain (%s) didn't enter "
"target state %d\n",
pwrst->pwrdm->name, pwrst->next_state);
ret = -1;
}
omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
}
if (ret)
printk(KERN_ERR "Could not enter target state in pm_suspend\n");
else
printk(KERN_INFO "Successfully put all powerdomains "
"to target state\n");
return ret;
}
I'm really struggling to understand how it works.
It seems like, when the suspend procedure runs to after omap_sram_idle();
, system is already in suspend mode and everything just freezes there, in the context of this function. When it wakes up, it simply continues from restore:
and restores everything. Is this correct?
System suspend is happening in the middle of omap_sram_idle()
. The second part of omap_sram_idle()
is actually restoring code. More precisely, actual sleeping is done by wfi
ARM instruction in omap34xx_cpu_suspend()
assembler function. Read further for details.
_omap_sram_idle()
call (here)_omap_sram_idle()
points to omap34xx_cpu_suspend() assembler function, which copied previously to SRAM (so it will not be lost during RAM power-off); take a look at next lines of code:
omap34xx_cpu_suspend
function address)omap_sram_ceil
is start address of SRAM memory, start
is address of omap34xx_cpu_suspend() function
omap34xx_cpu_suspend() implementation; it's the actual function being executed at this line (instead of _omap_sram_idle()
). Take a look at comment above this function:
/*
* Forces OMAP into idle state
*
* omap34xx_suspend() - This bit of code just executes the WFI
* for normal idles.
*
* Note: This code get's copied to internal SRAM at boot. When the OMAP
* wakes up it continues execution at the point it went to sleep.
*/
actual sleeping is happening in wfi (Wait For Interrupt) ARM instruction (in omap34xx_cpu_suspend()
function); the processor will sleep and it will wake up only when some IRQ will happen
omap34xx_cpu_suspend()
when wfi
can be executed:
Once some wake-up signal happened, CPU will continue to execute instructions just after wfi
instruction (that have put it to the sleep in the first place). So your system wakes up in omap34xx_cpu_suspend()
assembler function (starting from wait_sdrk_ok:
label), then returns back to omap_sram_idle()
(to this line), and then returns back to omap3_pm_suspend()
, to restore:
label.