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_STARTFIQ_STACK_START指到RAM中的IRQ stuff区域。

在看board_init:

board/Smdk2410.c:

/*

 * Miscellaneous platform dependent initialisations

 */

int board_init (void)

{

    DECLARE_GLOBAL_DATA_PTR;

  //获取powerclockGPIO方面的寄存器地址,稍后的操作会对这些寄存器操作,

  //需要看到的是,象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 */

   //降低PLLlock 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,我们的CPUarm920t系列的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这个函数,

 

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

1.   cpu, borad, interrupt的初始化,包括cache等,这些都于特定板子的配置有关。

2.   环境变量的初始化,

3.   串口,控制台,RAM的初始化,

4.   在控制台上实时的显示系统配置等相关参数。

 

最后需要说明的是,大部分的配置参数都是预先在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的任务就完成了。接下来就是内核启动的过程了。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值