linuxlinux-kernelarmomap

How linux suspend/wakeup works for mach-omap2?


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?


Solution

  • 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.

    Suspend path

    Restore path

    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.