根据上面那篇PowerButton驱动分析的文章,我们应该清楚了,按下PowerButton就可以最后调用OEMPowerOff这个函数,其实也可以通过调用API PowerOffSystem来进入OEMPowerOff。我们看看这个函数实现:
这个函数在$(_WINCEROOT)\PLATFORM\smdk2410\KERNEL\HAL下的power.c文件中
VOID OEMPowerOff(void)
{
 volatile IOPreg *s2410IOP = (IOPreg *)IOP_BASE;
  volatile INTreg *s2410INT = (INTreg *)INT_BASE;
 volatile LCDreg *s2410LCD = (LCDreg *)LCD_BASE;   
    /* Save Current Important CPU Regs...   */
    CPUSaveRegs(CPUBackupRegs);//保存寄存器的值
    /* LCD Controller Disable               */
    CPULCDOff();//关闭LCD电源
 /* Stop all GPIO */
 ConfigStopGPIO();//停止IO口
 /* Set misc register for power off */
 ConfigMiscReg();

    /* Actual Power-Off Mode Entry          */
    CPUPowerOff();//调用CPUPowerOff,这个函数在fw.s里面,调用完之后会进入挂起状态
    /* Recover Process, Load CPU Regs       *///恢复过程
    CPULoadRegs(CPUBackupRegs);//恢复寄存器值
 /* Clear GSTATUS2 register : Write 1 to clear */
 s2410IOP->rGSTATUS2 = s2410IOP->rGSTATUS2;//清除GSTATUS2 寄存器,这里面存放的是CPU挂起时PC指针的地址
    /* Interrupt Clear                      *///清除中断
...
}
我们下面进入fw.s简单分析一下CPUPowerOff
   LEAF_ENTRY CPUPowerOff //CPUPowerOff 函数实现
    ; 1. 保存寄存器状态以及返回地址到栈里
    ;
    stmdb   sp!, {r4-r12}                  
    stmdb   sp!, {lr}

    ; 2. 保存MMU & CPU 寄存器到RAM.
    ;
    ldr     r3, =SLEEPDATA_BASE_VIRTUAL         ; base of Sleep mode storage
    ldr     r2, =Awake_address
    str     r2, [r3], #4                        ; save resume function address (virtual).
   
    mrc     p15, 0, r2, c1, c0, 0
    ldr     r0, =MMU_CTL_MASK
    bic     r2, r2, r0
    str     r2, [r3], #4                        ; save MMU control data.
    mrc     p15, 0, r2, c2, c0, 0
    ldr     r0, =MMU_TTB_MASK
    bic     r2, r2, r0
    str     r2, [r3], #4                        ; save TTB address.
    mrc     p15, 0, r2, c3, c0, 0
    str     r2, [r3], #4                        ; save domain access control.
    str     sp, [r3], #4                        ; save SVC mode stack pointer.
    mrs     r2, spsr
    str     r2, [r3], #4                        ; save 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}           ; save 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}                   ; save 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}                   ; save 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}                   ; save 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}                       ; save the SYS mode Registers.
    mov     r1, #Mode_SVC:OR:I_Bit:OR:F_Bit     ; back to SVC mode, no interrupts.
    msr     cpsr, r1

   ; 3. Compute the checksum on SleepData (verify integrity of data after resume).
   ;
    ldr     r3, =SLEEPDATA_BASE_VIRTUAL         ; get pointer to SLEEPDATA.
    mov     r2, #0
    ldr     r0, =SLEEPDATA_SIZE                 ; get size of data structure (in words).
30
    ldr     r1, [r3], #4                        ; compute the checksum.
    and     r1, r1, #0x1
    mov     r1, r1, LSL #31
    orr     r1, r1, r1, LSR #1
    add     r2, r2, r1
    subs    r0, r0, #1
    bne     %b30

    ldr     r0, =vGPIOBASE
    str     r2, [r0, #oGSTATUS3]                ; save the checksum in the Power Manager Scratch pad register.
    ; 4. 屏蔽和清除所有中断.
    ;
    ldr     r0, =vINTBASE
    mvn     r2, #0
    str     r2, [r0, #oINTMSK]
    str     r2, [r0, #oSRCPND]
    str     r2, [r0, #oINTPND]
......
    ; 6. 设置外部唤醒中断(EINT0-2: power-button 和 keyboard).
    ;
    ldr     r0, =vGPIOBASE
    ldr     r1, =0x550a
    str     r1, [r0, #oGPFCON]
    ldr     r1, =0x55550100
    str     r1, [r0, #oGPGCON]
    ; 7. 转换到power-off 模式.
    ;
 ldr  r0, =vMISCCR   ; hit the TLB
 ldr  r0, [r0]
 ldr  r0, =vCLKCON
 ldr  r0, [r0]
    ; **These registers are used later during power-off.
    ;
    ldr     r0, =vREFRESH  
    ldr     r1, [r0]                            ; r1 = rREFRESH.
    orr     r1, r1, #(1 << 22)
    ; **These registers are used later during power-off.
    ;
    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.
    ; **These registers are used later during power-off.
    ;
    ldr     r4, =vCLKCON
    ldr     r5, =0x7fff8                        ; power-off mode.
 ldr  r8, =0xEA000000
 add  r8, r8, #0x3f0
 add  r8, r8, #0xe  ; make value to 0xEA0003FE
 ldr  r6, =0x92000000  ; make address to 0x9200 1004 or 0x9200 0004
 ldr     r7, [r6]   ; Check ROM Address data, if 0xEA0003FE, it is EBOOT
 cmp  r7, r8
 bne  %f50
 add  r6, r6, #0x1000  ; Because eboot startup code is located at 0x1000.
50
 add  r6, r6, #0x4  ;
 mov     pc, r6    ; jump to Power off code in ROM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 b       SelfRefreshAndPowerOff
       
 ALIGN   32                      ; for I-Cache Line(32Byte, 8 Word)
SelfRefreshAndPowerOff  ; run with Instruction Cache's code
 str     r1, [r0]  ; 使能SDRAM self-refresh模式,这样RAM中的数据不会丢失!!
 str  r3, [r2]  ; MISCCR Setting
 str     r5, [r4]  ; Power Off !!
 b       .
上面的代码就实现了wince的挂起,下面我们分析唤醒的流程,通过外部中断,其实是让CPU复位,这样系统又从0地址开始执行,但是,通过下面的代码我们可以知道是冷启动还是唤醒!!!
 ldr  r1, =GSTATUS2           ; Determine Booting Mode
 ldr  r10, [r1]
 tst  r10, #0x2
 beq  %F2          ; if not wakeup from PowerOffmode如果第2位不为1则表示是唤醒
                        ;    Skip MISCCR setting
 b  %F3      ; if wakeup from PowerOff mode
         ;    goto Power-up code.
下面还有一段
 tst  r10, #0x2
 beq  BringUpWinCE                    ; 如果GSTATUS2为1则正常启动,否则进入唤醒
; Recover Process : Starting Point
;  1. Checksum Calculation saved Data
 ....
40
 ldr  r1, [r3], #4   ; pointer to SLEEPDATA
 and  r1, r1, #0x1
 mov  r1, r1, LSL #31
 orr  r1, r1, r1, LSR #1
 add  r2, r2, r1
 subs r0, r0, #1    ; dec the count
 bne  %b40           ; loop till done 
 ldr  r0,=GSTATUS3
 ldr  r3, [r0]    ; get the Sleep data checksum from the Power Manager Scratch pad register
 teq  r2, r3           ; compare to what we saved before going to sleep
; bne  BringUpWinCE      ; bad news - do a cold boot - If emergency power off case, normal booting.
 bne  JumpToRAM    ; bad news - do a cold boot - If emergency power off case, normal booting.
 b  MMUENABLE
JumpToRAM
 ldr  r2, =0x201000     ; offset into the RAM
 ldr  r3, =0x30000000     ; add physical base
 add  r2, r2, r3
 mov     pc, r2       ;  & jump to StartUp address
MMUENABLE
;  2. MMU Enable
 ldr     r10, [r5, #SleepState_MMUDOMAIN] ; load the MMU domain access info
 ldr     r9,  [r5, #SleepState_MMUTTB]  ; load the MMU TTB info 
 ldr     r8,  [r5, #SleepState_MMUCTL]  ; load the MMU control info 
 ldr     r7,  [r5, #SleepState_WakeAddr ] ; load the LR address
 nop   
 nop
 nop
 nop
 nop
....
; 唤醒过程
1
 mcr  p15, 0, r10, c3, c0, 0  ; setup access to domain 0
 mcr  p15, 0, r9,  c2, c0, 0  ; PT address
 mcr  p15, 0, r0,  c8, c7, 0     ; flush I+D TLBs
 mcr  p15, 0, r8,  c1, c0, 0  ; restore MMU control
;  3. 跳到内核fw.s(Awake_address)唤醒地址处!!!!!1
 mov     pc, r7      ;  & jump to new virtual address (back up Power management stack)跳到挂起时的地址
 nop
唤醒时的执行地址如下:
Awake_address
;       1. Recover CPU Registers
 ldr     r3, =SLEEPDATA_BASE_VIRTUAL  ; Sleep mode information data structure
 add     r2, r3, #SleepState_FIQ_SPSR
 mov     r1, #Mode_FIQ:OR:I_Bit:OR:F_Bit ; Enter FIQ mode, no interrupts
 msr     cpsr, r1
 ldr     r0,  [r2], #4
 msr     spsr, r0
 ldr     r8,  [r2], #4
 ldr     r9,  [r2], #4
 ldr     r10, [r2], #4
 ldr     r11, [r2], #4
 ldr     r12, [r2], #4
 ldr     sp,  [r2], #4
 ldr     lr,  [r2], #4
; mov     r1, #Mode_ABT:OR:I_Bit:OR:F_Bit ; Enter ABT mode, no interrupts
 mov     r1, #Mode_ABT:OR:I_Bit   ; Enter ABT mode, no interrupts
 msr     cpsr, r1
 ldr     r0, [r2], #4
 msr     spsr, r0
 ldr     sp, [r2], #4
 ldr     lr, [r2], #4
; mov     r1, #Mode_IRQ:OR:I_Bit:OR:F_Bit ; Enter IRQ mode, no interrupts
 mov     r1, #Mode_IRQ:OR:I_Bit   ; Enter IRQ mode, no interrupts
 msr     cpsr, r1
 ldr     r0, [r2], #4
 msr     spsr, r0
 ldr     sp, [r2], #4
 ldr     lr, [r2], #4
; mov     r1, #Mode_UND:OR:I_Bit:OR:F_Bit ; Enter UND mode, no interrupts
 mov     r1, #Mode_UND:OR:I_Bit   ; Enter UND mode, no interrupts
 msr     cpsr, r1
 ldr     r0, [r2], #4
 msr     spsr, r0
 ldr     sp, [r2], #4
 ldr     lr, [r2], #4
; mov     r1, #Mode_SYS:OR:I_Bit:OR:F_Bit ; Enter SYS mode, no interrupts
 mov     r1, #Mode_SYS:OR:I_Bit   ; Enter SYS mode, no interrupts
 msr     cpsr, r1
 ldr     sp, [r2], #4
 ldr     lr, [r2]
; mov     r1, #Mode_SVC:OR:I_Bit:OR:F_Bit ; Enter SVC mode, no interrupts
 mov     r1, #Mode_SVC:OR:I_Bit   ; Enter SVC mode, no interrupts
 msr     cpsr, r1
 ldr     r0, [r3, #SleepState_SVC_SPSR]
 msr     spsr, r0
;       2. Recover Last mode's REG's, & go back to caller of CPUPowerOff()
 ldr     sp, [r3, #SleepState_SVC_SP]
 ldr     lr, [sp], #4
 ldmia   sp!, {r4-r12}
 mov     pc, lr                          ; and now back to our sponsors
最后一句就是把返回地址装入PC,这个返回地址是什么,哈哈,就是在power.c的OEMPowerOff函数 CPUPowerOff()后应该执行的指令,也就是需要执行CPULoadRegs(CPUBackupRegs)了!!这下大家都明白了吧!!