标准linu休眠和唤醒机制分析(四)

来源:互联网 发布:知乎怎么私信匿名用户 编辑:程序博客网 时间:2024/06/06 01:13


suspend第三、四、五阶段:platformprocessorcore

static intsuspend_enter(suspend_state_t state)

{

      int error;

 

      if (suspend_ops->prepare) {

 //平台特定的函数,mtkpm.c,有定义,对pmiccpu 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))      // suspend3阶段到此为止

             goto Platform_wake;

 

      error = disable_nonboot_cpus(); // disable nonboot cpus

      if (error || suspend_test(TEST_CPUS))  // suspend4阶段到此为止

             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))              // suspend5阶段到此为止

                    error = suspend_ops->enter(state);           

//真正才进入suspend,调用的函数时平台特定的suspend enter函数,//  mtkpm.c, 在下面列出mtk平台的该函数实现,供分析:

                    //  如果有唤醒源被操作,那么处理将会被wakeup,先做一些平台相                        //  关的动作,最后从函数suspend_ops->enter()中返回,这之后的唤                         // 醒操作实际上是按照suspend流程的相反顺序的来走的。

sysdev_resume();        // resuem system devices

//跳到本文档最后面,将会有一个总结,这里会展示出正常的suspendresume的时候函数调用

      }

 

      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;

}

 

voidmt6516_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

 

 

六、系统正常suspendresume时函数调用和配对

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

0 0