您的位置:首页 > 运维架构 > Linux

linux休眠唤醒(四)

2016-09-24 21:32 106 查看
转自http://blog.csdn.net/dwyane_zhang/article/details/7099725

suspend第三、四、五阶段:platform、processor、core
static int suspend_enter(suspend_state_t state)
{
       int error;
 
       if (suspend_ops->prepare) {
 // 平台特定的函数,mtkpm.c, 有定义,对pmic和cpu dll的一些设置
              error = suspend_ops->prepare();
              if (error)
                     return error;
       }
 
       error = dpm_suspend_noirq(PMSG_SUSPEND);
// 对于一些non-sysdev devices,需要调用禁止中断的dpm_suspend函数来suspend那些设备
       if (error) {
              printk(KERN_ERR "PM: Some devices failed to power down/n");
              goto Platfrom_finish;
       }
 
       if (suspend_ops->prepare_late) { // 这里没定义
              error = suspend_ops->prepare_late();
              if (error)
                     goto Power_up_devices;
       }
 
       if (suspend_test(TEST_PLATFORM))       // suspend第3阶段到此为止
              goto Platform_wake;
 
       error = disable_nonboot_cpus();  // disable nonboot cpus
       if (error || suspend_test(TEST_CPUS))  // suspend第4阶段到此为止
              goto Enable_cpus;
 
       arch_suspend_disable_irqs();             // 中断禁止
       BUG_ON(!irqs_disabled());
 
       error = sysdev_suspend(PMSG_SUSPEND);    // kernel/driver/base/sys.c
  // suspend system devices
       if (!error) {
              if (!suspend_test(TEST_CORE))               // suspend第5阶段到此为止
                     error = suspend_ops->enter(state);           
// 真正才进入suspend,调用的函数时平台特定的suspend enter函数, //  mtkpm.c, 在下面列出mtk平台的该函数实现,供分析:
                     //  如果有唤醒源被操作,那么处理将会被wakeup,先做一些平台相                         //  关的动作,最后从函数suspend_ops->enter()中返回,这之后的唤                          // 醒操作实际上是按照suspend流程的相反顺序的来走的。
sysdev_resume();         // resuem system devices
// 跳到本文档最后面,将会有一个总结,这里会展示出正常的suspend和resume的时候函数调用
       }
 
       arch_suspend_enable_irqs();
       BUG_ON(irqs_disabled());
 
 Enable_cpus:
       enable_nonboot_cpus();
 
 Platform_wake:
       if (suspend_ops->wake)       // 平台无定义
              suspend_ops->wake();
 
 Power_up_devices:
       dpm_resume_noirq(PMSG_RESUME);
 
 Platfrom_finish:
       if (suspend_ops->finish) // 做和函数suspend_ops->prepare()相反的工作
              suspend_ops->finish();
 
       return error;
}
 
static int mtk_pm_enter(suspend_state_t state)
{
       _Chip_pm_enter(state);
       return 0;
}
 
int _Chip_pm_enter(suspend_state_t state)
{
       MSG_FUNC_ENTRY();
       printk("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/n");
       printk("_Chip_pm_enter @@@@@@@@@@@@@@@@@@@@@@/n");
       printk(" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@/n");
 
       /* ensure the debug is initialised (if enabled) */
       switch (state)
       {
           case PM_SUSPEND_ON:
                  MSG(SUSP,"mt6516_pm_enter PM_SUSPEND_ON/n/r");
                  break;
           case PM_SUSPEND_STANDBY:
                  MSG(SUSP,"mt6516_pm_enter PM_SUSPEND_STANDBY/n/r");       
                  break;
           case PM_SUSPEND_MEM:  // 只支持mem的系统省电模式
                  MSG(SUSP,"mt6516_pm_enter PM_SUSPEND_MEM/n/r");
            if (g_ChipVer == CHIP_VER_ECO_2)
                   mt6516_pm_SuspendEnter(); 
// 让cpu进入省电模式的函数,真正休眠之后,执行的代码会停在这个函数中,直到外部有EINT将其cpu唤醒,停下来的代码才继续执行,也就是正常按下了唤醒键的时候。
                  break;
           case PM_SUSPEND_MAX:
                  MSG(SUSP,"mt6516_pm_enter PM_SUSPEND_MAX/n/r");       
                  MSG(SUSP,"Not support for MT6516/n/r");                      
                  break;
                 
           default:
               MSG(SUSP,"mt6516_pm_enter Error state/n/r");
                  break;
       }
       return 0;
}
 
void mt6516_pm_SuspendEnter(void)
{
    UINT32 u32TCM = 0xF0400000;
    UINT32 u4SuspendAddr = 0;
    UINT32 u4Status, u4BATVol;
       UINT32 counter = 0;
 
    /* Check Chip Version*/
    if (g_ChipVer == CHIP_VER_ECO_1)
        u4SuspendAddr = u32TCM;
    else if(g_ChipVer == CHIP_VER_ECO_2)
        u4SuspendAddr = __virt_to_phys((unsigned long)MT6516_CPUSuspend);
 
       /*wifi low power optimization : shutdown MCPLL & VSDIO */
    wifi_lowpower_opt(TRUE);
 
    /* Check PM related register*/
    mt6516_pm_RegDump();
    //mt6326_check_power();
 
       DRV_WriteReg32(APMCUSYS_PDN_SET0,0x04200000);  
 
/* STEP7: Set AP_SM_CNF(DxF003C22C) to wanted wake-up source. 设置唤醒源*/
#if defined(PLATFORM_EVB)
              mt6516_pm_SetWakeSrc((1<< WS_KP)|(1<<WS_EINT)|(1<<WS_RTC));
#elif defined(PMIC_BL_SETTING)
              mt6516_pm_SetWakeSrc((1<<                      
WS_KP)|(1<<WS_EINT)|(1<<WS_CCIF)|(1<<WS_SM)|(1<<WS_RTC));
#else
       mt6516_pm_SetWakeSrc((1<<WS_EINT)|(1<<WS_CCIF)|(1<<WS_SM)|(1<<WS_RTC));
              //mt6516_pm_SetWakeSrc((1<<WS_SM));     
#endif
 
       /* Save interrupt masks*/
    irqMask_L = *MT6516_IRQ_MASKL;
    irqMask_H = *MT6516_IRQ_MASKH;
    mt6516_pm_Maskinterrupt(); // 20100316 James
       while(1)
       {
#ifdef AP_MD_EINT_SHARE_DATA
    /* Update Sleep flag*/
       mt6516_EINT_SetMDShareInfo();
       mt6516_pm_SleepWorkAround();
#endif
    /* Enter suspend mode, mt6516_slpctrl.s */
       if ( g_Sleep_lock <= 0 )
           u4Status = MT6516_CPUSuspend (u4SuspendAddr, u32TCM);
       else
        MSG(SUSP,"Someone lock sleep/n/r");
             
#ifdef AP_MD_EINT_SHARE_DATA
       mt6516_pm_SleepWorkAroundUp();
#endif
 
    /* Check Sleep status*/
    u4Status = mt6516_pm_CheckStatus();
       if (u4Status == RET_WAKE_TIMEOUT)
       {
#ifndef PLATFORM_EVB
              DRV_WriteReg32(APMCUSYS_PDN_CLR0,0x04200000);         
              u4BATVol = (mt6516_pm_GetOneChannelValue(VBAT_CHANNEL,VBAT_COUNT)/VBAT_COUNT);          
              DRV_WriteReg32(APMCUSYS_PDN_SET0,0x04200000);         
              MSG(SUSP,"counter = %d, vbat = %d/n/r",counter++, u4BATVol);
              if(u4BATVol <= LOW_BAT_ALARM)
        {     
            MSG(SUSP,"Battery Low!!Power off/n/r");     
                     bBAT_UVLO = TRUE;
            goto SLP_EXIT;
        }
#endif
       }
       else
       {
              MSG(SUSP,"leave sleep, wakeup!!/n/r");         
              goto SLP_EXIT;
              //break;
       }
       }
      
SLP_EXIT:   
       wifi_lowpower_opt(FALSE);
       /* Restore interrupt mask ;  */  
       *MT6516_IRQ_MASKL = irqMask_L;
       *MT6516_IRQ_MASKH = irqMask_H;
}
 
函数MT6516_CPUSuspend (u4SuspendAddr, u32TCM)是一段汇编代码,在文件:
Kernel/arch/arm/amch-mt6516/mt6516_slpctrl.S中。下面是这段汇编代码片段,看一看也蛮有意思,因为处理进入low power模式之后,是停留在该函数之中的。
 
ENTRY(MT6516_CPUSuspend)
           stmfd sp!, {r4-r12, lr}
 
    // r0 = MT6516_CPUSuspend physical address, 
    // r1 = TCM address
           mov r4, r0   
           mov r9, r1
 
    // Set SVC mode
           mrs r0, cpsr
           bic r0, r0, #MODE_MASK1
           orr r1, r0, #Mode_SVC
    // Set I/F bit, disable IRQ and FIQ
           orr r1, r1, #I_Bit|F_Bit
    // Update CPSR
           msr cpsr_cxsf, r1
 
    // calculate the physical address of instruction after disable MMU
           ldr r0, =PhysicalPart
           ldr r1, =MT6516_CPUSuspend
           sub r0, r0, r1
           mov r1, r4
    // Now r0 is the physical address of PhysicalPart
           add r0, r0, r1
...
...   
    // Power down Cache and MMU, MCU_MEM_PDN
           ldr r0, =0xF0001308
           ldr r1, [r0]
           // ldr r1, =0xFFFFFFFF
           orr r1, r1, #0x0F
           str r1, [r0]
   
   
    // STEP1: Set AP SLEEP (IRQ CODE: 0x36) to level sensitive on CIRQ.
    // already done when system start.
   
    // STEP2: Unmask AP SLEEP CTRL interrupt.
    // already done at mt6516_pm_Maskinterrupt.
   
    // STEP3: EOI AP SLEEP interrupt.
    // already done at mt6516_pm_Maskinterrupt.
       
    // STEP4: Read clear AP_SM_STA (OxF003C21C).
    // already done at mt6516_pm_Maskinterrupt.
       
    // STEP5: Set AP_SM_PAUSE_M(0x8003C200) and AP_SM_PAUSE_L(0x8003C204) for sleep duration. 16 seconds as default
   ...
   
    // STEP6: Set AP_SM_CLK_SETTLE(0xF003C208) to 0x64. Must over 5ms
   ...
    // STEP7: Set AP_SM_CNF(DxF003C22C) to wanted wake-up source. (TP, GPT, MSDC, RTC, EINT, KP or SM)
    // already done at mt6516_pm_SuspendEnter   
    // STEP8: Set AP_SM_CON[1]:PAUSE_START to 1 to enable AP sleep controller.
    ...
    // STEP9: Execute the CP15 command(MCR p15, 0, r0, c7, c0, 4),
    // then ARM9 MCU enters low power state
    // and STANDBYWFI signal becomes HIGH. CLOCK_OFF signal is issued to Clock Management Unit,
    // and then AP MCU Sub-system clock is gated and VCXO OFF signal is issued to AP Sleep Controller.
    mov r0, #0
    mcr p15, 0, r0,c7,c0,4
 
    // wait till interrupt occurs
    // polling AP_SM_STA
    mov r2, #0
    mov r3, #0x10
 
15:   
    //mov r10, r1  
    // Power up I-Cache
    ...
    //delay
...
    // Power up I-Cache upper 16KB
    ...
//delay
       ...
    // Power up D-Cache
    ...
    //delay
       ...
    // Power up D-Cache upper 16KB
    ...
      //delay
       ...
       
    // Clean and invalid DCache
    // Invalidate instruction cache
    // TCM_START_UA saved in r9
    mov r2, r9
    add r1, r1, r2
 
    // make sure no stall on ¨mov pc,r0〃 below
    cmp r1, #0
   
    // restore MMU
    mov r4, #0
    // access domain 0
    // TTB   
    // flush TLBs   
    // Turn on MMU
//test
    mcr p15, 0, r6, c1, c0, 0
    //mov pc, r1
    nop
    nop
 
VirtualPart:
    nop
    nop
 
    mov r0, r10
    ldmia sp!, {r4-r12, lr}
 
    mov pc, lr
    Nop
 
 
六、系统正常suspend和resume时函数调用和配对
enter_state(state)
--> sys_sync()
--> suspend_prepare()
--> pm_prepare_console()
--> pm_notifier_call_chain(PM_SUSPEND_PREPARE)
--> usermodehelper_disable()
--> suspend_freeze_processes()
--> suspend_devices_and_enter(state)
--> suspend_ops->begin(state)
--> _Chip_pm_begin()
--> suspend_console()  // 此后串口无信息出来,缓存起来等后面resume打出
--> dpm_suspend_start(PMSG_SUSPEND)
--> dpm_prepare(state)
--> device_prepare(dev, state)
--> dpm_suspend(state)
--> device_suspend(dev, state)
--> suspend_enter(state)
--> suspend_ops->prepare()
--> _Chip_pm_prepare()
--> SetARM9Freq(DIV_4_104)
--> dpm_suspend_noirq(PMSG_SUSPEND)
--> suspend_ops->prepare_late() // 无定义
--> disable_nonboot_cpus()
--> arch_suspend_disable_irqs()
--> sysdev_suspend(PMSG_SUSPEND)
--> suspend_ops->enter(state)
--> _Chip_pm_enter(state)
--> mt6516_pm_SuspendEnter()
--> MT6516_CPUSuspend() // 汇编函数,suspend cpu
<-- MT6516_CPUSuspend() // 汇编函数,resume cpu
<-- mt6516_pm_CheckStatus()
<-- return 0
<-- return 0
<-- sysdev_resume()
<-- arch_suspend_enable_irqs()
<-- enable_nonboot_cpus()
<-- suspend_ops->wake() // 无定义
<-- dpm_resume_noirq(PMSG_RESUME)
<-- suspend_ops->finish()
<-- _Chip_pm_finish()
<-- SetARM9Freq(DIV_1_416)
<-- return 0
<-- return 0
<-- dpm_resume_end(PMSG_RESUME)
<-- dpm_resume(state)
<-- device_resume(dev, state)
<-- dpm_complete(state)
<-- device_complete(dev, state)
<-- resume_console()   // 打印出缓存中的信息
<-- suspend_ops->end()
<-- return 0
<-- suspend_finish()
<-- suspend_thaw_processes()
<-- usermodehelper_enable()
<-- pm_notifier_call_chain(PM_POST_SUSPEND)
<-- pm_restore_console()
<-- return 0




内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: