在uboot上添加suspend命令

给uboot添加一个休眠的命令,uboot实际上就是一个裸板程序。先忽略linux复杂的软件框架,在裸板上实现休眠的过程。

1. 配置GPIO,比如想维持led亮或灭,用于唤醒CPU的引脚要设为中断功能

 从原理图上可以看出,

JZ2440上面有4个按键,只有S2,S3,S4可以用作唤醒源,设置它们对应的GPIO用作中断模式。
在S3c24x0.h/include目录下,打开rGPFCON、rGPGCON所对应的宏
rGPFCON寄存器对应着EINT0,EINT2,需要将bit[5:4]=10,bit[1:0]=10.
rGPGCON寄存器对应着EINT11需要将bit[7:6]=10.

rGPFCON & =~((3<<0) |(3<<4)) ;
rGPFCON | = ((2<<0) | (2<<4));

rGPGCON & = ~(3<<6);
rGPGCON | =(2<<6);

2.设置INTMSK屏蔽所有中断:在sleep模式下,这些引脚只是用于唤醒系统,当CPU正常运行时可以重新设置INTMSK,让这些引脚用于中断功能。

 rINTMASK = ~0;

3.配置唤醒源,设置中断的触发方式。在这里设为上升沿和下降沿触发。

 

 rEXTINT0 | =(6<<0) | (6<<8); // EINT0、EINT2双边沿触发
 rEXTINT1 | =(6 << 12); //EINT11双边沿触发

4.设置MISCCR[13:12]=11b,使得USB模块进入休眠

  rMISCCR | =(3<<12);

5.在GSTATUS[4:3] 中保存某值,它们可以在系统被唤醒时使用

rGSTATUS3 = /*一般来说是在GSTATUS3这个寄存器中存放唤醒时首先执行函数的地址*/
rGSTATUS4 = /*想写入什么就写什么,*/
写入rGSTATUS3、rGSTATUS4这两个寄存器的值,在系统休眠的过程中这些值保持不变。因此可以在系统唤醒的过程中,根据里面的值做一些事情。
比如说可以把一些函数的地址放在里面,当唤醒时,就去执行这个函数

6.配置MISCCR[1:0]使能数据总线的上拉电阻

 rMISCCR  & =~(3<<0);

 7.清除LCDCON1.ENVID以停止LCD

rLCDCON1  &= ~(1<<0);

注意8~12使用汇编来实现。

8.读这2个寄存器: rREFRESH and rCLKCON以便填充TLB。如果不使用MMU的话,这一步可以忽略。这里没有使用MMU,看下面的分析就知道了。

参考内核源码:

arch\arm\mach-s3c2410\sleep.S

在boot目录cpu\arm920t\下,创建suspend.S

9.设置REFRESH[22]=1b,让SDRAM进入self-refresh mode

10.等待SDRAM进入self-refresh mode

11.设置 MISCCR[19:17]=111b以保护SDRAM signals(SCLK0,SCLK1 and SCKE)
12.设置 CLKCON register的SLEEP mode位,让系统进入sleep mode

对于2440的Arm开发板,里面有一个CPU,访问SDRAM,指令和数据都存在SDRAM里面。
现在想让系统休眠,过程是先让SDRAM进入自刷新模式(它可以保持里面的数据不被损坏,但是CPU就不能再去访问它了)

休眠时:
1)让SDRAM自刷新,意味着CPU无法访问它;
2)自刷新后,设置CLK以进入sleep
在1)和2)之间应该有代码执行,本来代码是存在SDRAM上的,代码应该去哪里取呢?
如果内核启动了MMU,需要有页表。如果使用的是虚拟地址,CPU需要根据页表找到物理地址,页表就在SDRAM上。现在SDRAM没法访问,怎么办?
因此需要页表以支持MMU进行地址映射;

看一下在内核里面的代码:

ldr r4, =S3C2410_REFRESH
ldr r5, =S3C24XX_MISCCR
ldr r6, =S3C2410_CLKCON
ldr r7, [ r4 ] @ get REFRESH (and ensure in TLB)
ldr r8, [ r5 ] @ get MISCCR (and ensure in TLB)
ldr r9, [ r6 ] @ get CLKCON (and ensure in TLB)

orr r7, r7, #S3C2410_REFRESH_SELF @ SDRAM sleep command
orr r8, r8, #S3C2410_MISCCR_SDSLEEP @ SDRAM power-down signals
orr r9, r9, #S3C2410_CLKCON_POWER @ power down command

teq pc, #0 @ first as a trial-run to load cache
bl s3c2410_do_sleep
teq r0, r0 @ now do it for real
b s3c2410_do_sleep @

@@ align next bit of code to cache line
.align 5
s3c2410_do_sleep:
streq r7, [ r4 ] @ SDRAM sleep command
streq r8, [ r5 ] @ SDRAM power-down config
streq r9, [ r6 ] @ CPU sleep
1: beq 1b
mov pc, r14

S3C2410_REFRESH、S3C24XX_MISCCR、S3C2410_CLKCON这三个都是虚拟地址,先故意去读一下虚拟地址中的内容,那么这个映射关系就会存在TLB中,也就是说把地址的映射关系存在TLB中临时使用。
在关闭SDRAM后,再去访问相关的寄存器,可以从TLB中得到映射关系。不需要去SDRAM上读取页表了。

同理,在uboot中需要执行1)和2)之间的指令时,也可以先去读一次这些指令,这些指令就会存入Icache中。当把SDRAM关闭后,再次执行前面的那些指令时,它就会从ICache中得到那些指令,不需要去访问SDRAM。这个设计是非常巧妙的。

代码如下:

 1.在common/目录下,添加文件cmd_suspend.c

#include <common.h>
#include <command.h>
#include <def.h>
#include <nand.h>
#include <s3c24x0.h>

extern void s3c2440_cpu_suspend(void);

static void delay(volatile int d)
{
    while(d--);
}

int do_suspend (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
    /* 休眠: */
    
    /* 1. 配置GPIO: 比如想维持LED亮或灭, 用于唤醒CPU的引脚要设为中断功能 */
    /* 对于NAND启动: 要设置EINT23,22,21为输入引脚 */
    rGPGCON &= ~((3<<30) | (3<<28) | (3<<26));

    /* JZ2440只有S2/S3/S4可用作唤醒源,设置它们对应的GPIO用于中断模式 */
    rGPFCON &= ~((3<<0) | (3<<4));
    rGPFCON |= ((2<<0) | (2<<4));

    rGPGCON &= ~(3<<6);
    rGPGCON |= (2<<6);

    /* 2. 设置INTMSK屏蔽所有中断: 在sleep模式下,这些引脚只是用于唤醒系统,当CPU正常运行时可以重新设置INTMSK让这些引脚用于中断功能 */    
    rINTMSK = ~0;

    /* 3. 配置唤醒源 */
    rEXTINT0 |= (6<<0) | (6<<8); /* EINT0,2双边沿触发 */
    rEXTINT1 |= (6<<12);   /* EINT11双边沿触发 */

    /* 4. 设置MISCCR[13:12]=11b, 使得USB模块进入休眠 */
    rMISCCR |= (3<<12);

    /* 5. 在GSTATUS[4:3]保存某值, 它们可以在系统被唤醒时使用 */
    //rGSTATUS3 = ;  /* 唤醒时首先执行的函数的地址 */
    //rGSTATUS4 = ;  /*  */

    /* 6. 设置 MISCCR[1:0] 使能数据总线的上拉电阻 */
    rMISCCR &= ~(3);

    /* 7. 清除 LCDCON1.ENVID 以停止LCD */
    rLCDCON1 &= ~1;

    /* 8~12使用汇编来实现,参考内核源码:
     *    arch\arm\mach-s3c2410\sleep.S
    */

    /* 8. 读这2个寄存器: rREFRESH and rCLKCON, 以便填充TLB
     *    如果不使用MMU的话,这个目的可以忽略 
     */

    /* 9. 设置 REFRESH[22]=1b,让SDRAM进入self-refresh mode */

    /* 10. 等待SDRAM成功进入self-refresh mode  */

    /* 11.设置 MISCCR[19:17]=111b以保护SDRAM信号(SCLK0,SCLK1 and SCKE) */

    /* 12. 设置CLKCON的SLEEP位让系统进入sleep mode */    
    printf("suspend ...");
    delay(1000000);
    s3c2440_cpu_suspend();  /* 执行到这里就不会返回,直到CPU被唤醒 */

    /* 恢复运行: 重新初始化硬件 */
    serial_init();
    printf("wake up\n");
    
    
    return 0;
}

U_BOOT_CMD(
suspend,    1,    0,    do_suspend,
"suspend - suspend the board\n",
" - suspend the board"
);

2.在cpu/arm920t目录下,添加文件suspend.S

/* s3c2440_cpu_suspend
     *
     * put the cpu into sleep mode
    */

#define S3C2440_REFRESH_SELF        (1<<22)
#define S3C2440_MISCCR_SDSLEEP        (7<<17)
#define S3C2440_CLKCON_POWER         (1<<3)

#define REFRESH (0x48000024)
#define MISCCR (0x56000080)
#define DCLKCON (0x56000084)

.globl s3c2440_cpu_suspend
    @@ prepare cpu to sleep
s3c2440_cpu_suspend:

    ldr    r4, =REFRESH
    ldr    r5, =MISCCR
    ldr    r6, =CLKCON
    ldr    r7, [ r4 ]        @ get REFRESH 
    ldr    r8, [ r5 ]        @ get MISCCR 
    ldr    r9, [ r6 ]        @ get CLKCON 

    orr    r7, r7, #S3C2440_REFRESH_SELF    @ SDRAM sleep command
    orr    r8, r8, #S3C2440_MISCCR_SDSLEEP @ SDRAM power-down signals
    orr    r9, r9, #S3C2440_CLKCON_POWER    @ power down command

    teq    pc, #0            @ first as a trial-run to load cache
    bl    s3c2410_do_sleep
    teq    r0, r0            @ now do it for real
    b    s3c2410_do_sleep    @

    @@ align next bit of code to cache line
    .align    5
s3c2410_do_sleep:
    streq    r7, [ r4 ]            @ SDRAM sleep command
    streq    r8, [ r5 ]            @ SDRAM power-down config
    streq    r9, [ r6 ]            @ CPU sleep
1:    beq    1b
    mov    pc, r14


猜你喜欢

转载自www.cnblogs.com/-glb/p/12799696.html
今日推荐