根据上面那篇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;
{
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);//保存寄存器的值
CPUSaveRegs(CPUBackupRegs);//保存寄存器的值
/* LCD Controller Disable */
CPULCDOff();//关闭LCD电源
CPULCDOff();//关闭LCD电源
/* Stop all GPIO */
ConfigStopGPIO();//停止IO口
ConfigStopGPIO();//停止IO口
/* Set misc register for power off */
ConfigMiscReg();
ConfigMiscReg();
/* Actual Power-Off Mode Entry */
CPUPowerOff();//调用CPUPowerOff,这个函数在fw.s里面,调用完之后会进入挂起状态
/* Recover Process, Load CPU Regs *///恢复过程
CPULoadRegs(CPUBackupRegs);//恢复寄存器值
CPULoadRegs(CPUBackupRegs);//恢复寄存器值
/* Clear GSTATUS2 register : Write 1 to clear */
s2410IOP->rGSTATUS2 = s2410IOP->rGSTATUS2;//清除GSTATUS2 寄存器,这里面存放的是CPU挂起时PC指针的地址
s2410IOP->rGSTATUS2 = s2410IOP->rGSTATUS2;//清除GSTATUS2 寄存器,这里面存放的是CPU挂起时PC指针的地址
/* Interrupt Clear *///清除中断
...
}
...
}
我们下面进入fw.s简单分析一下CPUPowerOff
LEAF_ENTRY CPUPowerOff //CPUPowerOff 函数实现
; 1. 保存寄存器状态以及返回地址到栈里
;
stmdb sp!, {r4-r12}
stmdb sp!, {lr}
;
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.
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.
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 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.
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.
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.
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.
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.
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.
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
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]
;
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 r0, =vGPIOBASE
ldr r1, =0x550a
str r1, [r0, #oGPFCON]
ldr r1, =0x55550100
str r1, [r0, #oGPGCON]
str r1, [r0, #oGPGCON]
; 7. 转换到power-off 模式.
;
ldr r0, =vMISCCR ; hit the TLB
ldr r0, [r0]
ldr r0, =vCLKCON
ldr r0, [r0]
;
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)
;
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.
;
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 r4, =vCLKCON
ldr r5, =0x7fff8 ; power-off mode.
ldr r8, =0xEA000000
add r8, r8, #0x3f0
add r8, r8, #0xe ; make value to 0xEA0003FE
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
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)
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 .
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.
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则正常启动,否则进入唤醒
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 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
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
; 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
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
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
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
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_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_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_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_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
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
ldr lr, [sp], #4
ldmia sp!, {r4-r12}
mov pc, lr ; and now back to our sponsors
最后一句就是把返回地址装入PC,这个返回地址是什么,哈哈,就是在power.c的OEMPowerOff函数 CPUPowerOff()后应该执行的指令,也就是需要执行CPULoadRegs(CPUBackupRegs)了!!这下大家都明白了吧!!
转载于:https://blog.51cto.com/buaadallas/80930