S3C2440休眠和唤醒流程解析(WinCE 6)

在WinCE中,有3种方式可以使系统进入休眠:
1、用户在开始菜单选择挂起
2、用户短按电源键,请求系统进入休眠(需要电源按键驱动的支持,比如设为长按关机,短按休眠)
3、用户长时间无操作,系统状态由POWER_STATE_ON转为POWER_STATE_USERIDLE,POWER_STATE_IDLE状态,最后进入POWER_STATE_SUSPEND即休眠状态 

休眠之前,对于支持电源管理的驱动,系统会调用此驱动的XXX_PowerDown接口关闭设备电源,之后系统会调用OEMPowerOff接口,进入休眠。 

OEMPowerOFF位于SMDK2440A/Src/Common/Power/off.c文件中,不要被它的名字迷惑了,它不仅负责休眠,唤醒后恢复也在它的内部实现,它的执行流程如下:

1、调用BSPPowerOFF,关闭休眠时不必要的设备电源,如USB
2、保存当前GPIO和LCD控制器等信息在内存中
3、设置GPIO为低功耗状态,关闭LCD等设备
4、调用OALCPUPowerOff,SDRAM进入自刷新模式,关闭CPU,等待中断唤醒
5、恢复保存在内存中的GPIO和LCD控制器等信息
6、调用BSPPowerOn打开相关设备电源,然后返回 

在这个过程中,最容易出问题的部分在OALCPUPowerOff部分,这要从2440的休眠模式说起。 

2440需要提供两路独立电源,一路电源给CPU及其内部逻辑供电,另一路电源单独给唤醒逻辑供电。当2440进入休眠时,CPU及其内部逻辑的这一路供电将会停止,仅保留唤醒逻辑的供电。唤醒逻辑支持EINT0-15和RTC中断,如果休眠期间产生这些中断,系统将恢复对CPU的供电并唤醒CPU。

由于CPU在休眠期间是掉电的,所以它唤醒后将会从地址0x00000000开始执行指令,也就是说,这时候CPU已经脱离了WinCE的执行空间,运行的是BootLoader! 

BootLoader执行后,首先执行一些必要的初始化工作,如设置系统时钟等,然后检查GSTATUS2[1]位,如果被设置为1,表明系统是从睡眠中恢复的,BootLoader将跳过通常的启动流程,恢复对SDRAM的供电,然后从SDRAM恢复睡眠前保存下来的数据,包括唤醒地址,然后转到唤醒地址执行,返回WinCE的执行空间。 

下面通过代码来详细这个过程(SMDK2440A/SRC/OAL/OALLIB/startup.s): 

;; OALCPUPowerOff调用入口    LEAF_ENTRY OALCPUPowerOff      ;       1. Push SVC state onto our stack    stmdb   sp!, {r4-r12}                       stmdb   sp!, {lr}      ;       2. Save MMU & CPU Register to RAM   ;; 注意:这里SLEEPDATA_BASE_VIRTUAL使用的是虚拟地址,其映射的物理地址必须和BootLoader中设定的一致           ldr     r3, =SLEEPDATA_BASE_VIRTUAL     ; base of Sleep mode storage       ldr     r2, =Awake_address              ; store Virtual return address    str     r2, [r3], #4       mrc     p15, 0, r2, c1, c0, 0           ; load r2 with MMU Control    ldr     r0, =MMU_CTL_MASK               ; mask off the undefined bits    bic     r2, r2, r0    str     r2, [r3], #4                    ; store MMU Control data       mrc     p15, 0, r2, c2, c0, 0           ; load r2 with TTB address.    ldr     r0, =MMU_TTB_MASK               ; mask off the undefined bits    bic     r2, r2, r0    str     r2, [r3], #4                    ; store TTB address       mrc     p15, 0, r2, c3, c0, 0           ; load r2 with domain access control.    str     r2, [r3], #4                    ; store domain access control       str     sp, [r3], #4                    ; store SVC stack pointer       mrs     r2, spsr    str     r2, [r3], #4                    ; store SVC status register       mov     r1, #Mode_FIQ:OR:I_Bit:OR:F_Bit ; Enter FIQ mode, no interrupts    msr     cpsr, r1    mrs     r2, spsr    stmia   r3!, {r2, r8-r12, sp, lr}       ; store the FIQ mode registers       mov     r1, #Mode_ABT:OR:I_Bit:OR:F_Bit ; Enter ABT mode, no interrupts    msr     cpsr, r1    mrs  r0, spsr    stmia   r3!, {r0, sp, lr}               ; store the ABT mode Registers       mov     r1, #Mode_IRQ:OR:I_Bit:OR:F_Bit ; Enter IRQ mode, no interrupts    msr     cpsr, r1    mrs     r0, spsr    stmia   r3!, {r0, sp, lr}               ; store the IRQ Mode Registers       mov     r1, #Mode_UND:OR:I_Bit:OR:F_Bit ; Enter UND mode, no interrupts    msr     cpsr, r1    mrs     r0, spsr    stmia   r3!, {r0, sp, lr}               ; store the UND mode Registers       mov     r1, #Mode_SYS:OR:I_Bit:OR:F_Bit ; Enter SYS mode, no interrupts    msr     cpsr, r1    stmia   r3!, {sp, lr}                   ; store the SYS mode Registers       mov     r1, #Mode_SVC:OR:I_Bit:OR:F_Bit ; Back to SVC mode, no interrupts    msr     cpsr, r1      ;       3. do Checksum on the Sleepdata    ldr     r3, =SLEEPDATA_BASE_VIRTUAL ; get pointer to SLEEPDATA    ldr     r2, =0x0    ldr     r0, =(SLEEPDATA_SIZE-1)  ; get size of data structure (in words)   30    ldr     r1, [r3], #4    and     r1, r1, #0x1    mov     r1, r1, ROR #31    add     r2, r2, r1    subs    r0, r0, #1    bne     %b30      ;; 保存数据的校验和放在GSTATUS3中,BootLoader中需要进行校验,只有校验正确才会返回WinCE,否则按冷启动的流程走    ldr     r0, =vGPIOBASE    str     r2, [r0, #oGSTATUS3]  ; Store in Power Manager Scratch pad register         ;       4. Interrupt Disable        ldr     r0, =vINTBASE       mvn     r2, #0    str     r2, [r0, #oINTMSK]    str     r2, [r0, #oSRCPND]    str     r2, [r0, #oINTPND]      ;;       5. Cache Flush    bl  OALClearUTLB    bl  OALFlushICache    ldr     r0, = (DCACHE_LINES_PER_SET - 1)        ldr     r1, = (DCACHE_NUM_SETS - 1)        ldr     r2, = DCACHE_SET_INDEX_BIT        ldr     r3, = DCACHE_LINE_SIZE         bl  OALFlushDCache      ;       6. Setting Wakeup External Interrupt(EINT0,1) Mode    ldr     r0, =vGPIOBASE       ldr     r1, =0x550a    str     r1, [r0, #oGPFCON]      ;       7. Go to Power-Off Mode    ldr  r0, =vMISCCR   ; hit the TLB    ldr  r0, [r0]    ldr  r0, =vCLKCON    ldr  r0, [r0]      ;; 内存进入自刷新模式,CPU掉电后保持内容    ldr     r0, =vREFRESH      ldr     r1, [r0]  ; r1=rREFRESH     orr     r1, r1, #(1 << 22)       ldr  r2, =vMISCCR    ldr  r3, [r2]    orr  r3, r3, #(7<<17)        ; Make sure that SCLK0:SCLK->0, SCLK1:SCLK->0, SCKE=L during boot-up     bic  r3, r3, #(7<<20)    orr  r3, r3, #(6<<20)       ldr     r4, =vCLKCON    ldr     r5, =0x1ffff8            ; Power Off Mode      ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;   ; Sometimes it is not working in cache mode. So I modify to jump to ROM area.   ;   ;;; ldr  r6, =0x92000000  ; make address to 0x9200 0020   ;;; add  r6, r6, #0x20  ;    ;;; mov     pc, r6    ; jump to Power off code in ROM   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;       b       SelfRefreshAndPowerOff       ALIGN   32                      ; for I-Cache Line(32Byte, 8 Word)      ;; 关闭CPU   SelfRefreshAndPowerOff  ; run with Instruction Cache's code    str     r1, [r0]  ; Enable SDRAM self-refresh    str     r3, [r2]  ; MISCCR Setting    str     r5, [r4]  ; Power Off !!    b       .  

下面是BootLoader中唤醒部分的代码:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wysfzd.html