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

2024-04-22 17:48

本文主要是介绍标准linu休眠和唤醒机制分析(四),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

suspend第三、四、五阶段:platformprocessorcore

static int suspend_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;

}

 

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


这篇关于标准linu休眠和唤醒机制分析(四)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/926448

相关文章

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

JAVA实现Token自动续期机制的示例代码

《JAVA实现Token自动续期机制的示例代码》本文主要介绍了JAVA实现Token自动续期机制的示例代码,通过动态调整会话生命周期平衡安全性与用户体验,解决固定有效期Token带来的风险与不便,感兴... 目录1. 固定有效期Token的内在局限性2. 自动续期机制:兼顾安全与体验的解决方案3. 总结PS

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

详解Spring中REQUIRED事务的回滚机制详解

《详解Spring中REQUIRED事务的回滚机制详解》在Spring的事务管理中,REQUIRED是最常用也是默认的事务传播属性,本文就来详细的介绍一下Spring中REQUIRED事务的回滚机制,... 目录1. REQUIRED 的定义2. REQUIRED 下的回滚机制2.1 异常触发回滚2.2 回

Java中最全最基础的IO流概述和简介案例分析

《Java中最全最基础的IO流概述和简介案例分析》JavaIO流用于程序与外部设备的数据交互,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),处理... 目录IO流简介IO是什么应用场景IO流的分类流的超类类型字节文件流应用简介核心API文件输出流应用文

基于Redis自动过期的流处理暂停机制

《基于Redis自动过期的流处理暂停机制》基于Redis自动过期的流处理暂停机制是一种高效、可靠且易于实现的解决方案,防止延时过大的数据影响实时处理自动恢复处理,以避免积压的数据影响实时性,下面就来详... 目录核心思路代码实现1. 初始化Redis连接和键前缀2. 接收数据时检查暂停状态3. 检测到延时过