u-boot源码分析 --- 启动第二阶段 ,基于2410 启动代码 分析 收藏
我们先来看初始化函数表: init_sequence


lib_arm/board.c:


typedef int (init_fnc_t) (void);


init_fnc_t *init_sequence[] = {


    cpu_init,       /* basic cpu dependent setup */


    board_init,     /* basic board dependent setup */


    interrupt_init,    /* set up exceptions */


    env_init,       /* initialize environment */


    init_baudrate,     /* initialze baudrate settings */


    serial_init,       /* serial communications setup */


    console_init_f,    /* stage 1 init of console */


    display_banner,    /* say that we are here */


    dram_init,      /* configure available RAM banks */


    display_dram_config,


#if defined(CONFIG_VCMA9) || defined (CONFIG_CMC_PU2)


    checkboard,


#endif


    NULL,


};


这些初始化函数会依次执行,我们一个个的来看



arm920t/Cpu.c:


int cpu_init (void)


{


    /*


     * setup up stacks if necessary


     */


#ifdef CONFIG_USE_IRQ


    DECLARE_GLOBAL_DATA_PTR;


 


    IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;


    FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;


#endif


    return 0;


}


对于smdk2410来说这个宏CONFIG_USE_IRQ没定义,实际上就是把IRQ_STACK_START, FIQ_STACK_START指到RAM中的IRQ stuff区域。


在看board_init:


board/Smdk2410.c:


/*


 * Miscellaneous platform dependent initialisations


 */


int board_init (void)


{


    DECLARE_GLOBAL_DATA_PTR;


  //获取power和clock及GPIO方面的寄存器地址,稍后的操作会对这些寄存器操作,


  //需要看到的是,象S3C24X0_CLOCK_POWER里面的field对象都是按照实际寄存器的地址来安排的


    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();


    S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();


 



    /* to reduce PLL lock time, adjust the LOCKTIME register */


   //降低PLL的lock time的值,具体含义可参考data sheet


    clk_power->LOCKTIME = 0xFFFFFF;


 


    /* configure MPLL */


   //配置MPLL,同样可参考data sheet


    clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);


 


    /* some delay between MPLL and UPLL */


    delay (4000);


 


    /* configure UPLL */


    //配置UPLL


    clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);


 


    /* some delay between MPLL and UPLL */


    delay (8000);


 


    /* set up the I/O ports */


    //配置每个GPIO的功能,输入输出,等参数


    gpio->GPACON = 0x007FFFFF;


    gpio->GPBCON = 0x00044555;


    gpio->GPBUP = 0x000007FF;


    gpio->GPCCON = 0xAAAAAAAA;


    gpio->GPCUP = 0x0000FFFF;


    gpio->GPDCON = 0xAAAAAAAA;


    gpio->GPDUP = 0x0000FFFF;


    gpio->GPECON = 0xAAAAAAAA;


    gpio->GPEUP = 0x0000FFFF;


    gpio->GPFCON = 0x000055AA;


    gpio->GPFUP = 0x000000FF;


    gpio->GPGCON = 0xFF95FFBA;


    gpio->GPGUP = 0x0000FFFF;


    gpio->GPHCON = 0x002AFAAA;


    gpio->GPHUP = 0x000007FF;


 


    /* arch number of SMDK2410-Board */


    //保存arch number


    gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;


 


    /* adress of boot parameters */


    //保存启动参数的地址,运行时在linux内核之下


    gd->bd->bi_boot_params = 0x30000100;


 


    //使能指令cache和数据cache


    icache_enable();


    dcache_enable();


    return 0;


}


这个函数是和特定板子相关的,因此一般都是自己添加的,使能cache很简单,只要把协处理器15的相关位打开就行了,代码就不列出来了,可以参考datasheet。


接下来该看初始化函数: interrupt_init,我们的CPU是arm920t系列的s3c2410


 


cpu/arm920t/s3c24x0:


int interrupt_init (void)


{


    S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();


 


    /* use PWM Timer 4 because it has no output */


    /* prescaler for Timer 4 is 16 */


    timers->TCFG0 = 0x0f00;


    if (timer_load_val == 0)


    {


        /*


         * for 10 ms clock period @ PCLK with 4 bit divider = 1/2


         * (default) and prescaler = 16. Should be 10390


         * @33.25MHz and 15625 @ 50 MHz


         */


        timer_load_val = get_PCLK()/(2 * 16 * 100);


    }


    /* load value for 10 ms timeout */


    lastdec = timers->TCNTB4 = timer_load_val;


    /* auto load, manual update of Timer 4 */


    timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;


    /* auto load, start Timer 4 */


    timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;


    timestamp = 0;


 


    return (0);


}


对着datasheet来看这个函数, 实际上这个函数使用timer 4来作为系统clock, 即时钟滴答, 10ms一次,到点就产生一个中断,但由于此时中断还没打开所以这个中断不会响应。


 


接着看env_init: 由于我们在inculde/configs/Smdk2410.h下定义了CFG_ENV_IS_IN_FLASH,因此该函数位于common/Env_flash.c下


 


common/Env_flash.c:


int  env_init(void)


{


    DECLARE_GLOBAL_DATA_PTR; /*这个还记得吗?*/


#ifdef CONFIG_OMAP2420H4


    int flash_probe(void);


 


    if(flash_probe() == 0)


        goto bad_flash;


#endif


    if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {


        gd->env_addr  = (ulong)&(env_ptr->data);


        gd->env_valid = 1;


        return(0);


    }


#ifdef CONFIG_OMAP2420H4


bad_flash:


#endif


    gd->env_addr  = (ulong)&default_environment[0];


    gd->env_valid = 0;


    return (0);


}


这个函数主要是在gd里保存环境变量的存放地址。一般使用默认的环境变量值即default_environment数组,


uchar default_environment[] = {


#ifdef  CONFIG_BOOTARGS


    "bootargs=" CONFIG_BOOTARGS        "\0"


#endif


#ifdef  CONFIG_BOOTCOMMAND


    "bootcmd="  CONFIG_BOOTCOMMAND     "\0"


#endif


   ……


}


可见环境变量以如下的方式存放在数组中


   Name=value


并且以”\0”结束, 而类似CONFIG_BOOTARGS的宏都定义在板子自己的配置文件中即smdk2410.h里。


 


接下来看init_baudrate


 


lib_arm/Board.c:


static int init_baudrate (void)


{


    DECLARE_GLOBAL_DATA_PTR;


  


    //从环境变量中获取波特率值


    uchar tmp[64];  /* long enough for environment variables */


    int i = getenv_r ("baudrate", tmp, sizeof (tmp));


    gd->bd->bi_baudrate = gd->baudrate = (i > 0)


            ? (int) simple_strtoul (tmp, NULL, 10)


            : CONFIG_BAUDRATE;


 


    return (0);


}


该函数从上面刚初始化好的环境变量列表里找波特率值,如果没有就赋初始值为CONFIG_BAUDRATE。


 


继续往下看serial_init:


 


cpu/arm920t/s3c24x0:


/*


 * Initialise the serial port with the given baudrate. The settings


 * are always 8 data bits, no parity, 1 stop bit, no start bits.


 *


 */


int serial_init (void)


{


    serial_setbrg ();   //设置波特率,停止位等


 


    return (0);


}


 


cpu/arm920t/s3c24x0:


void serial_setbrg (void)


{


    DECLARE_GLOBAL_DATA_PTR;


    S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);//UART寄存器地址


    int i;


    unsigned int reg = 0;


 


    /* value is calculated so : (int)(PCLK/16./baudrate) -1 */


    reg = get_PCLK() / (16 * gd->baudrate) - 1;


 


    /* FIFO enable, Tx/Rx FIFO clear */


    uart->UFCON = 0x07; //Rx,Tx FIFO reset, 使能FIFO。


    uart->UMCON = 0x0;


    /* Normal,No parity,1 stop,8 bit */


    uart->ULCON = 0x3;


    /*


     * tx=level,rx=edge,disable timeout int.,enable rx error int.,


     * normal,interrupt or polling


     */


    uart->UCON = 0x245;


    uart->UBRDIV = reg;


 


#ifdef CONFIG_HWFLOW


    uart->UMCON = 0x1; /* RTS up */


#endif


    for (i = 0; i < 100; i++);


}


上面这个函数对着datasheet看,无非是设置波特率,起始位,检验中断类型等等。


 


接着看初始化函数:console_init_f


 


common/Console.c:


/* Called before relocation - use serial functions */


int console_init_f (void)


{


    DECLARE_GLOBAL_DATA_PTR;


 


    gd->have_console = 1;


 


#ifdef CONFIG_SILENT_CONSOLE


    if (getenv("silent") != NULL)


        gd->flags |= GD_FLG_SILENT; //设置控制台模式


#endif


 


    return (0);


}


该函数初始化了几个控制台相关的标记。


 


接着看display_banner:


lib_arm/Board.c:


static int display_banner (void)


{


    printf ("\n\n%s\n\n", version_string); //打印U-BOOT的版本信息


    printf ("U-Boot code: %08lX -> %08lX  BSS: -> %08lX\n",


        _armboot_start, _bss_start, _bss_end); //打印U-BOOT代码位置


#ifdef CONFIG_MODEM_SUPPORT


    puts ("Modem Support enabled\n");


#endif


#ifdef CONFIG_USE_IRQ


    printf ("IRQ Stack: %08lx\n", IRQ_STACK_START);


    printf ("FIQ Stack: %08lx\n", FIQ_STACK_START);


#endif


    return (0);


}


这个函数就是在控制台上打印一些系统信息。


 


接着看dram_init:


board/smdk2410/Smdk2410.c:


int dram_init (void)


{


    DECLARE_GLOBAL_DATA_PTR;


 


    gd->bd->bi_dram[0].start = PHYS_SDRAM_1;      //RAM起始地址


    gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;  //RAM大小


 


    return 0;


}


RAM的起始地址和大小都是和特定板子相关的,因此这两个宏都在Smdk2410.h中根据实际情况定义的,


 


再看display_dram_config


lib_arm/Board.c:


/*


 * WARNING: this code looks "cleaner" than the PowerPC version, but


 * has the disadvantage that you either get nothing, or everything.


 * On PowerPC, you might see "DRAM: " before the system hangs - which


 * gives a simple yet clear indication which part of the


 * initialization if failing.


 */


static int display_dram_config (void)


{


    DECLARE_GLOBAL_DATA_PTR;


    int i;


 


    puts ("RAM Configuration:\n");


 


    for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) {


        printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);


        print_size (gd->bd->bi_dram[i].size, "\n");


    }


 



    return (0);


}


呵呵仅仅是打印系统RAM的信息。


接着说初始化函数, 对于Smdk2410来说不存在checkboard这个函数,


 


这样整个初始化函数表的函数都看完了,总结一下主要做了如下过程:


<!--[if !supportLists]-->1.   <!--[endif]-->cpu, borad, interrupt的初始化,包括cache等,这些都于特定板子的配置有关。


<!--[if !supportLists]-->2.   <!--[endif]-->环境变量的初始化,


<!--[if !supportLists]-->3.   <!--[endif]-->串口,控制台,RAM的初始化,


<!--[if !supportLists]-->4.   <!--[endif]-->在控制台上实时的显示系统配置等相关参数。


 


最后需要说明的是,大部分的配置参数都是预先在include/configs/board_name.h下定义的,因此如果我们要移植我们自己的板子的话,这个文件必不可少,它描述了我们板子的配置情况如CPU型号,RAM大小等。


//---------------------------------------------------------------------------------------


/*配置可用的Flash */
size = flash_init ();
……
/* 初始化堆空间 */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);


#if (CONFIG_COMMANDS & CFG_CMD_NAND)
    puts ("NAND:  ");
    nand_init();        /* go init the NAND */
#endif


/* 重新定位环境变量, */
env_relocate ();
/* 从环境变量中获取IP地址 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* 以太网接口MAC 地址 */
  ……
devices_init ();      /* 设备初始化 */
jumptable_init ();  //跳转表初始化
console_init_r ();    /* 完整地初始化控制台设备 */
enable_interrupts (); /* 使能中断处理 */
/* 通过环境变量初始化 */
if ((s = getenv ("loadaddr")) != NULL) {
     oad_addr = simple_strtoul (s, NULL, 16);
}
/* main_loop()循环不断执行 */
for (;;) {
      main_loop ();      /* 主循环函数处理执行用户命令 -- common/main.c */
}


//----------------------


之后就进入到了main_loop()延时3秒,检测用户的输入,如果没有用户的输入,那么就直接
run_command()运行bootcmd中的bootm命令,这时就进入到了common/cmd_bootm.c中的do_bootm(),在 do_bootm中判断是哪种内核,如果是linux内核则调用lib_mips/mips_linux.c中do_bootm_linux()函数把参数传给内核,当然这期间还有很多工作要做的比如:验证Magic Number,验证Header Checksum,判断是不是initrd image,解压缩,等等工作,之后就是theKernel (linux_argc, linux_argv, linux_env, 0);传参数给内核,这样u-boot的任务就完成了。接下来就是内核启动的过程了。



本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/lanmanck/archive/2009/05/19/4201481.aspx

arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()