zoukankan      html  css  js  c++  java
  • 标准linu休眠和唤醒机制分析(四)【转】

    转自:http://blog.csdn.net/lizhiguo0532/article/details/6453552

    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

  • 相关阅读:
    实战DeviceIoControl 之五:列举已安装的存储设备
    在NT中直接访问物理内存
    实战DeviceIoControl 之三:制作磁盘镜像文件
    实战DeviceIoControl 之六:访问物理端口
    程序员的十层楼(转)
    Vista + VS2005 源代码绑定的问题
    敬告天下IT业主
    手动卸载windows服务
    古墓丽影9的截屏
    白领饮食十大“夺命”恶习(转)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/4981686.html
Copyright © 2011-2022 走看看