u-boot-2010.06 源码分析<3>--第二阶段

http://blog.chinaunix.net/space.php?uid=20658254&do=blog&id=189395

 

上接第一阶段跳转语句

ldr  pc,  _start_armboot

第二阶段流程图

 

1。第二阶段的主线函数位于u-boot-2010.06/arch/arm/lib/board.c。

第二阶段的功能:

<1> 初始化本阶段要使用到的硬件设备。

设置时钟、初始化串口。

board_init函数设置MPLL、改变系统时钟,它是开发板相关的函数,在board/samsung/smdk2440/smdk2440.c中实现。值得注意的是board_init函数还保存了机器类型ID,这将在调用内核的时候传递给内核。代码如下:

gd->bd->bi_arch_number = MACH_TYPE_S3C2440;   //值为362

串口的初始化函数主要是serial_init,它设置UART控制器,是CPU相关的函数。

<2>检测系统内存映射(memory map)。

对于smdk2440的开发板,其内存分布是明确的,一般内存起始地址为0x3000 0000,大小为64M = 0x0400 0000。代码如下:

int dram_init(void)

{

     gd->bd->bi_dram[0] . start = PHYS_SDRAM_1;          //即0x3000 0000

     gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;  //即0x0400 0000

//这两个值都定义在include/configs/smdk2440.h中

}

<3>将内核映像和根文件系统映像从Flash上读到RAM空间中。

<4>为内核设置启动参数。

U-Boot 是通过标记列表向内核传递参数。

setup_memory_tags

setup_commandline_tag

这两个标记列表定义在arch/arm/lib/bootm.c中,需要在定义命令的文件include/configs/smdk2440.h中定义两个命令

#define CONFIG_SETUP_MEMORY_TAGS  1

#define CONFIG_CMDLINE_TAG                    1

对于ARM构架的CPU来说,都是通过arch/arm/lib/bootm.c中的do_bootm_linux函数来启动内核的。这个函数中,设置标记列表,最后通过 theKernel = (void (星)(int, int, uint))images->ep;调用内核。其中,theKernel 指向内核存放的地址(对于ARM构架的CPU,通常这个地址是0x3000 8000)。传递的3个参数如下:void (星theKernel)(int zero, int arch, uint params);

R0: 0

R1: 机器类型ID -- gd->bd->bi_arch_number = MACH_TYPE_S3C2440;   //值为362

R2: 启动参数标记列表在RAM中的起始地址 0x3000 0100

 

2。源码解析。

arch/arm/lib/board.c  

void start_armboot (void)
{
 init_fnc_t **init_fnc_ptr;
       //定义二级函数指针
 char *s;

 gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
/************************************************************************************************************

定义了全局数据指针,armboot_start的值位于0x3ff80000,且CONFIG_SYS_MALLOC_LEN在

include/configs/smdk2440.h中定义,

#define config_sys_malloc_len  (CONFIG_ENV_SIZE + 128 * 1024)

#define CONFIG_ENV_SIZE  0x20000   //128K为256M的Nandflash的块大小,block。

通过以上计算得出 config_sys_malloc_len 的大小为 256k = 0x4 0000。

再来看一下结构体gd_t,位于arch/arm/include/asm/global_data.h

typedef struct global_data {
 bd_t  *bd;
 unsigned long flags;
 unsigned long baudrate;
 unsigned long have_console; /* serial_init() was called */
 unsigned long env_addr; /* Address  of Environment struct */
 unsigned long env_valid; /* Checksum of Environment valid? */
 unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
     unsigned char vfd_type; /* display type */
#endif
#ifdef CONFIG_FSL_ESDHC
     unsigned long sdhc_clk;
#endif
#if 0
     unsigned long cpu_clk; /* CPU clock in Hz!  */
     unsigned long bus_clk;
     phys_size_t ram_size; /* RAM size */
     unsigned long reset_status; /* reset status register at boot */
#endif
 void  **jt;  /* jump table */
} gd_t;

sizeof(gd_t)=4 + 4 * 6 + 4 = 32 = 0x20。

故gd所指向地址 0x3ff80000 - 0x40000 -  0x20为 0x3FF3 FFE0

 

/*
 * Global Data Flags
 */
#define GD_FLG_RELOC 0x00001  /* Code was relocated to RAM  */
#define GD_FLG_DEVINIT 0x00002  /* Devices have been initialized */
#define GD_FLG_SILENT 0x00004  /* Silent mode    */
#define GD_FLG_POSTFAIL 0x00008  /* Critical POST test failed  */
#define GD_FLG_POSTSTOP 0x00010  /* POST seqeunce aborted  */
#define GD_FLG_LOGINIT 0x00020  /* Log Buffer has been initialized */
#define GD_FLG_DISABLE_CONSOLE 0x00040  /* Disable console (in & out)  */

 

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

/***************************************************************************************

这个声明告诉编译器使用寄存器r8来存储gd_t类型的指针gd,即这个定义声明了一个指针,并且指明了它的存储位置。
register表示变量放在机器的寄存器
volatile用于指定变量的值可以由外部过程异步修改

这个指针在board.c中初始化。

****************************************************************************************/
 __asm__ __volatile__("": : :"memory");
//分配一个存储全局数据的区域,地址给指针 gd

/****************************************************************************************************************

如同使用变量之前,需要声明定义一样,这里使用全局变量gd和bd之前,我们需要先设置它的地址,并用memset函数为它分配合适的空间。u-boot的注释告知我们,gd和bd是一个可写的指针,实际上不过是一个地址而已。
代码中的这句话:__asm__ __volatile__("": : :"memory");目的就是告诉编译器内存被修改过了。更详细的关于C程序中内嵌汇编的文档,请参考《ARM GCC 内嵌(inline)汇编手册》。

***************************************************************************************************************/ 

memset ((void*)gd, 0, sizeof (gd_t));   //分配空间并清0
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

/****************************************************************************************************************

bd_t结构体位于arch/arm/include/asm/u-boot.h

#ifndef _U_BOOT_H_
#define _U_BOOT_H_ 1

 

typedef struct bd_info {
    int   bi_baudrate; /* serial console baudrate */
    unsigned long bi_ip_addr; /* IP Address */
    struct environment_s        *bi_env;
    ulong         bi_arch_number; /* unique id for this board */
    ulong         bi_boot_params; /* where this board expects params */
    struct    /* RAM configuration */
    {
           ulong start;
           ulong size;
    }   bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;

 

#define bi_env_data bi_env->data
#define bi_env_crc  bi_env->crc

#endif /* _U_BOOT_H_ */

****************************************************************************************************************/
memset (gd->bd, 0, sizeof (bd_t));  
//分配空间并清0

gd->flags |= GD_FLG_RELOC;    //设置当前uboot标志位已经重定位

monitor_flash_len = _bss_start - _armboot_start;   //uboot镜像文件大小

//初始化函数循环

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
       if ((*init_fnc_ptr)() != 0)

      {
           hang ();
      }
 }

/***************************************************************************************************************
init_sequence 是一个初始化函数集的函数指针数组。

如果有任何一个函数失败就进入si循环。结构体如下:

init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
     arch_cpu_init,  /* basic arch cpu dependent setup */
#endif
     board_init,  /* basic board dependent setup */
#if defined(CONFIG_USE_IRQ)
     interrupt_init,  /* set up exceptions */
#endif
     timer_init,  /* initialize timer */
#ifdef CONFIG_FSL_ESDHC
     get_clocks,
#endif
     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 */
#if defined(CONFIG_DISPLAY_CPUINFO)
     print_cpuinfo,  /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
     checkboard,  /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
     init_func_i2c,
#endif
     dram_init,  /* configure available RAM banks */
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
     arm_pci_init,
#endif
     display_dram_config,
 NULL,
};

***************************************************************************************************************/

/****************************************************************************************************************

mem_malloc_init函数,这是非常关键的一步,请大家引起注意。我们必须配置好内存的起始地址和结束地址,然后把这块区域清零,以便后续来使用它。

void mem_malloc_init(ulong start, ulong size)
{
     mem_malloc_start = start;
     mem_malloc_end = start + size;
     mem_malloc_brk = start;

     memset((void *)mem_malloc_start, 0, size);
}

****************************************************************************************************************/

/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
   CONFIG_SYS_MALLOC_LEN);

/****************************************************************************************************************

Norflash初始化,返回的是size是大小。定义在board/samsung/smdk2440/flash.c

****************************************************************************************************************/

#ifndef CONFIG_SYS_NO_FLASH
 /* configure available FLASH banks */
        display_flash_config (flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */

/****************************************************************************************************************

Nandflash初始化,定义在drivers/mtd/nand/nand.c

****************************************************************************************************************/

#if defined(CONFIG_CMD_NAND)
     puts ("NAND:  ");
     nand_init();  /* go init the NAND */
#endif

#if defined(CONFIG_CMD_ONENAND)
     onenand_init();
#endif

/****************************************************************************************************************

初始化环境变量,如果认为没有找到存储其中的,就用默认值并打印:“*** Warning - bad CRC, using default environment”。这是我们常看到的。在终端输入saveenv 即可。
****************************************************************************************************************/

 /* initialize environment */
      env_relocate ();

//多串口配置,跳过。 

#ifdef CONFIG_SERIAL_MULTI
      serial_initialize();
#endif

//IP地址获取。 

 /* IP Address */
     gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

//标准的输入输出 

    stdio_init (); /* get the devices list going. */

//初始化外设列表

    jumptable_init ();

//u-boot的应用函数集,跳过

#if defined(CONFIG_API)
 /* Initialize API */
     api_init ();
#endif

//初始化控制台(console),平台无关,不一定是串口哦,如果把标准输出设为vga,字符会显示在LCD上。   

     console_init_r (); /* fully init console as a device */

//平台相关的其他设备初始化,这些s3c2440都没有。跳过

#if defined(CONFIG_ARCH_MISC_INIT)
 /* miscellaneous arch dependent initialisations */
     arch_misc_init ();
#endif

#if defined(CONFIG_MISC_INIT_R)
 /* miscellaneous platform dependent initialisations */
     misc_init_r ();
#endif

//允许中断,这里还没移植USB下载,所以直接return。

 /* enable exceptions */
     enable_interrupts ();

//TI平台的网卡驱动,直接跳过

 /* Perform network card initialisation if necessary */

#ifdef CONFIG_DRIVER_TI_EMAC
 /* XXX: this needs to be moved to board init */
extern void davinci_eth_set_mac_addr (const u_int8_t *addr);
     if (getenv ("ethaddr"))

    {
        uchar enetaddr[6];
        eth_getenv_enetaddr("ethaddr", enetaddr);
        davinci_eth_set_mac_addr(enetaddr);
     }
#endif

//其他网卡的设置,直接跳过。 

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
 /* XXX: this needs to be moved to board init */
     if (getenv ("ethaddr"))

{
     uchar enetaddr[6];
     eth_getenv_enetaddr("ethaddr", enetaddr);
     smc_set_mac_addr(enetaddr);
 }
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

 

/* Initialize from environment */
 if ((s = getenv ("loadaddr")) != NULL)

{
       load_addr = simple_strtoul (s, NULL, 16);
}

//获取bootfile参数

#if defined(CONFIG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL)

{
       copy_filename (BootFile, s, sizeof (BootFile));
}
#endif

 

#ifdef BOARD_LATE_INIT
      board_late_init ();
#endif

//MMC卡的控制器初始化

#ifdef CONFIG_GENERIC_MMC
      puts ("MMC:   ");
      mmc_initialize (gd->bd);
#endif

 

#ifdef CONFIG_BITBANGMII
      bb_miiphy_init();
#endif

 

#if defined(CONFIG_CMD_NET)
//多网卡配置

#if defined(CONFIG_NET_MULTI)
      puts ("Net:   ");
#endif

//网卡初始化
      eth_initialize(gd->bd);

//允许网卡复位
#if defined(CONFIG_RESET_PHY_R)
      debug ("Reset Ethernet PHY\n");
      reset_phy();
#endif
#endif

//进入主循环。其中会读取bootdelay和bootcmd。在bootdelay时间内按下键进入命令行,否则执行bootcmd的命令。

/* main_loop() can return to retry autoboot, if so just run it again. */
 for (;;)

{
        main_loop ();
}

 /* NOTREACHED - no way out of command loop except booting */
}

//出错提示

void hang (void)
{
     puts ("### ERROR ### Please RESET the board ###\n");
     for (;;);
}

 

3。初始化列表具体分析。

void jumptable_init(void)
{

     //为gd->jt是这个函数指针数组分配空间。
     gd->jt = malloc(XF_MAX * sizeof(void *));
     #include <_exports.h>
}

 

init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
     arch_cpu_init,  /* basic arch cpu dependent setup */
#endif

    board_init,  /* basic board dependent setup */

/****************************************************************************************************************

int board_init (void)
{
 struct s3c24x0_clock_power * const clk_power =
     s3c24x0_get_base_clock_power();
 struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();

 /* to reduce PLL lock time, adjust the LOCKTIME register */
 clk_power->LOCKTIME = 0xFFFFFF;

 /* configure MPLL */
 clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);

 /* some delay between MPLL and UPLL */
 delay (4000);

 /* configure 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->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 */
     gd->bd->bi_arch_number = MACH_TYPE_S3C2440;

 /* adress of boot parameters */
     gd->bd->bi_boot_params = 0x30000100;

 icache_enable(); //关闭指令cache
 dcache_enable(); //关闭数据cache

 return 0;
}
***************************************************************************************************************/ 
#if defined(CONFIG_USE_IRQ)
     interrupt_init,  /* set up exceptions */
#endif
     timer_init,  /* initialize timer */         //定时器初始化

/****************************************************************************************************************

int timer_init(void)
{
 struct s3c24x0_timers *timers = s3c24x0_get_base_timers();
 ulong tmr;

 /* use PWM Timer 4 because it has no output */
 /* prescaler for Timer 4 is 16 */
 writel(0x0f00, &timers->TCFG0);
 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);
  timer_clk = get_PCLK() / (2 * 16);
 }
 /* load value for 10 ms timeout */
 lastdec = timer_load_val;
 writel(timer_load_val, &timers->TCNTB4);
 /* auto load, manual update of Timer 4 */
 tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000;
 writel(tmr, &timers->TCON);
 /* auto load, start Timer 4 */
 tmr = (tmr & ~0x0700000) | 0x0500000;
 writel(tmr, &timers->TCON);
 timestamp = 0;

 return (0);
}

****************************************************************************************************************/
#ifdef CONFIG_FSL_ESDHC
     get_clocks,
#endif
     env_init,  /* initialize environment */  //环境变量

/***************************************************************************************************************

int env_init(void)
{
 /* use default */
 gd->env_addr = (ulong) & default_environment[0];
 gd->env_valid = 1;

 return 0;
}
****************************************************************************************************************/
     init_baudrate,  /* initialze baudrate settings */  //波特率获取

/****************************************************************************************************************

static int init_baudrate (void)
{
     char 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);
}

***************************************************************************************************************/
     serial_init,  /* serial communications setup */   //串口设置

/****************************************************************************************************************

#if !defined(CONFIG_SERIAL_MULTI)
/* Initialise the serial port. The settings are always 8 data bits, no parity,
 * 1 stop bit, no start bits.
 */
int serial_init(void)
{
        return serial_init_dev(UART_NR);  //UART_NR 就是串口号,在文件drivers/serial/serial_s3c24x0.c中有如下定义:

/******

#ifdef CONFIG_SERIAL1
#define UART_NR S3C24X0_UART0

#elif defined(CONFIG_SERIAL2)
# if defined(CONFIG_TRAB)
#  error "TRAB supports only CONFIG_SERIAL1"
# endif
#define UART_NR S3C24X0_UART1

#elif defined(CONFIG_SERIAL3)
# if defined(CONFIG_TRAB)
#  error "TRAB supports only CONFIG_SERIAL1"
# endif
#define UART_NR S3C24X0_UART2

#else
#error "Bad: you didn't configure serial ..."
#endif

******/

默认的输出为串口0 即定义在include/configs/smdk2440.h

#define CONFIG_S3C24X0_SERIAL
#define CONFIG_SERIAL1          1 /* we use SERIAL 1 on SMDK2410 */
}
#endif

|

|

|

static int serial_init_dev(const int dev_index)
{
 struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);

#ifdef CONFIG_HWFLOW
 hwflow = 0; /* turned off by default */
#endif

 /* FIFO enable, Tx/Rx FIFO clear */
 writel(0x07, &uart->UFCON);
 writel(0x0, &uart->UMCON);

 /* Normal,No parity,1 stop,8 bit */
 writel(0x3, &uart->ULCON);
 /*
  * tx=level,rx=edge,disable timeout int.,enable rx error int.,
  * normal,interrupt or polling
  */
 writel(0x245, &uart->UCON);

#ifdef CONFIG_HWFLOW
 writel(0x1, &uart->UMCON); /* RTS up */
#endif

 /* FIXME: This is sooooooooooooooooooo ugly */
#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)
 /* we need auto hw flow control on the gsm and gps port */
 if (dev_index == 0 || dev_index == 1)
  writel(0x10, &uart->UMCON);
#endif
 _serial_setbrg(dev_index);

 return (0);
}

****************************************************************************************************************/
     console_init_f,  /* stage 1 init of console */

/****************************************************************************************************************

/* Called before relocation - use serial functions */
int console_init_f(void)
{
     gd->have_console = 1;

#ifdef CONFIG_SILENT_CONSOLE
     if (getenv("silent") != NULL)
     gd->flags |= GD_FLG_SILENT;
#endif

     return 0;
}

****************************************************************************************************************/
     display_banner,  /* say that we are here */

/****************************************************************************************************************

测试串口,显示一些信息。

****************************************************************************************************************/
#if defined(CONFIG_DISPLAY_CPUINFO)
     print_cpuinfo,  /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
     checkboard,  /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
     init_func_i2c,
#endif
     dram_init,  /* configure available RAM banks */  //内存初始化

/****************************************************************************************************************

int dram_init (void)
{
      gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
      gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

      return 0;
}

****************************************************************************************************************/
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
     arm_pci_init,
#endif
     display_dram_config,  //显示内存信息

/****************************************************************************************************************

static int display_dram_config (void)
{
 int i;

#ifdef DEBUG
 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");
 }
#else
 ulong size = 0;

 for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
  size += gd->bd->bi_dram[i].size;
 }
 puts("DRAM:  ");
 print_size(size, "\n");
#endif

 return (0);
}

****************************************************************************************************************/
 NULL,
};

你可能感兴趣的:(源码分析)