/********************************
uboot的第二阶段就是初始化剩下的还没在第一阶段初始化的硬件。主要是SoC外部硬件(譬如 iNand 网卡芯片....... )uboot本身的一些东西(uboot的命令 环境变量等.....)。然后最终初始化完必要的东西后进入到uboot的命令行准备接受命令。
***********************************/
void start_armboot (void) //这个函数构成了uboot启动的第二阶段
{
/************************************
typedef int (init_fnc_t) (void);这是一个函数类型,所以init_fnc_t_ptr是一个二重函数指针,二重指针的作用有两个,(一个是用来指向一重指针(指针的指针)),一个用来指向一个指针数组。因此这里的init_fnc_t_ptr可以用来指向一个函数指针数组。
****************************************/
init_fnc_t **init_fnc_ptr;
char *s;
int mmc_exist = 0;
#if !defined(CFG_NO_FLASH) || defined (CONFIG_VFD) || defined(CONFIG_LCD)
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
#if defined(CONFIG_BOOT_MOVINAND)
uint *magic = (uint *) (PHYS_SDRAM_1);
#endif
/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base; //gd在DDR内存分配中的起始地址
/************************gd(global data)的由来******************
每个用到文件的开头都有DECLARE_GLOBAL_DATA_PTR;这句,而这个宏在我们的uboot/include/asm-arm/Global_data.h中有定义(gd_t也定义在这个文件中)
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
定义了一个全局变量名字为gd,这个全局变量是一个指针类型,占4个字节,用volatile修饰表示可变的;用register修饰表示这个变量要尽量放到寄存器中;asm("r8")是gcc支持的一种语法,意思是要把gd放到寄存器r8中。
综合分析:DECLARE_GLOBAL_DATA_PTR就是定义了一个要放在寄存器r8中的全局变量,名字叫gd,类型是一个指向gd_t类型变量的指针。gd_t中定义了很多全局变量,都是整个uboot使用的,其中有一个bd_t类型的指针bd,指向结构体bd_info,这个结构体里面定义的是和开发板硬件相关的全局变量(譬如 ip地址 串口波特率 等)
全局变量gd是uboot中很重要的一个全局变量(准确的说这个全局变量是一个结构体,里面有很多内容,这些内容加起来构成的结构体就是uboot中常用的所有全局变量),由于gd经常被访问,因此放在寄存器中提升访问效率。
**************************/
/***************************************************
typedef struct global_data {
bd_t *bd; //指向bd_t的指针
unsigned long flags;
unsigned long baudrate; //波特率
unsigned long have_console; 是否有控制台(终端,或串口之类的)
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; // 环境变量的地址
unsigned long env_valid; //环境变量checksum是否有效
unsigned long fb_base; //frame buffer的基地址
void **jt; /* jump table */
} gd_t;
******************************************/
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
/******************************************
//uboot区:CFG_UBOOT_BASE (33e00000)+CFG_UBOOT_SIZE (uboot的实际大小,我们这里给了2M)
堆区:长度为CFG_MALLOC_LEN ,实际长度为912KB
栈区:长度为CFG_STACK_SIZE,实际长度为512KB
gd:长度为sizeof(gd_t),实际为36字节
bd:长度为sizeof(bd_t),实际长度为44字节左右
所以gd_base的大概位置是离uboot起始地址(33e00000)往上走624KB的位置处
**************************************/
#ifdef CONFIG_USE_IRQ
gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif //CONFIG_USE_IRQ
gd = (gd_t*)gd_base; //上面已经得到gd_base是一个内存地址,然后通过强制类型转换使其变为一个指针,并赋给gd,使得gd指向这段内存空间
#else //CONFIG_MEMORY_UPPER_CODE
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
#endif //CONFIG_MEMORY_UPPER_CODE
/* compiler optimization barrier needed for GCC >= 3.4 */ //避免高版本的gcc的过度优化造成错误
__asm__ __volatile__("": : :"memory"); //内嵌汇编
/*
__asm__ :用于指示编译器在此插入汇编语句
__volatile__:用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。
memory:强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。
*/
/*需要注意的是:
(1)这里的#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")只是定义了一个指针,也就说gd里的这些全局变量并没有分配内存,我们在使用gd之前要给他分配内存,否则gd只是一个野指针
(2)gd和bd需要内存,内存当前没有被人管理(因为没有操作系统统一管理内存),大片的DDR内存(0x30000000-0x4fffffff共512MB)散放着可以随意使用(只要使用内存地址直接去访问内存即可),但是因为后面的uboot还需要大片内存,所以这里要本着够用,紧凑排布的原则
****************************************************/
memset ((void*)gd, 0, sizeof (gd_t)); //申请的内存使用之前先进行清零
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); //根据bd的大小计算bd的起始地址,在gd下面紧挨着
//假如改为(int *)gd - sizeof(bd_t),则实际int * 类型指针-1相当于地址-4*1,这样就会浪费一部分内存(虽然很 //小,因为这里的sizeof(bd_t)里面的就是bd的大小,所以地址直接减去这段内存即可。所以是char * gd)
memset (gd->bd, 0, sizeof (bd_t)); //申请的内存使用之前先进行清零
monitor_flash_len = _bss_start - _armboot_start;
/********************************************************
前面已经讲到过init_fnc_ptr 是一个二重函数指针,可以指向init_sequence这个函数指针数组,而 init_sequence是一个函数指针数组,所以这个数组存放了很多函数指针,这些函数的类型都是init_fnc_ptr类型(typedef int (init_fnc_t) (void);)特殊是接收参数是void类型,返回值是int类型。这里面的内容都是函数名,也就是函数指针,这些函数都是板级初始化的代码。
*init_fnc_ptr:解引用,得到的就是一个函数指针
这个for循环的作用就是去遍历init_sequence这个函数指针数组,而遍历的目的就是去执行这个函数指针数组里面的全部函数。
和普通数组类似,函数指针数组也可以通过下标个数组里面的元素个数去遍历这个数组(for(i=0;i
*****************************************/
/*
用函数指针去调用函数(得到的是函数的返回值),数组里面的函数正确执行时返回值都是0,如果错误是则挂起(hang();)启动失败
*/
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
#ifndef CFG_NO_FLASH
/*
Nor_FLASH初始化,但是我们的开发板上面接的是NandFlash,所以这个是无效的,可以拿去
*/
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */
#ifdef CONFIG_VFD
/*
uboot中自带的LCD显示架构,但是我们没有使用uboot的LCD显示架构,而是自己添加了一套LCD显示架构
*/
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
/* board init may have inited fb_base */
if (!gd->fb_base) {
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = lcd_setmem (addr);
gd->fb_base = addr;
}
#endif /* CONFIG_LCD */
/* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
/*
(1)mem_malloc_init函数用来初始化uboot的堆管理器。
(2)uboot中自己维护了一段堆内存,肯定自己就有一套代码来管理这个堆内存。有了这些东西uboot中你也可以malloc、free这套机制来申请内存和释放内存。我们在DDR内存中给堆预留了896KB的内存。
*/
#else
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif
//******************************//
// Board Specific 开发板独有的配置
三星用一套uboot满足了多套开发板,开发板之间的差别就在这里分析
// #if defined(CONFIG_SMDKXXXX)
//******************************//
#if defined(CONFIG_SMDK6410)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
/***********************************************************
(1)mmc_initialize():MMC相关的一些基础的初始化,其实就是用来初始化SoC内部的SD/MMC控制器的。函数在uboot/drivers/mmc/mmc.c里。
(2)mmc_initialize():是具体硬件架构无关的一个MMC初始化函数,
所有的使用了这套架构的代码都掉用这个函数来完成MMC的初始化。
mmc_initialize中再调用board_mmc_init和cpu_mmc_init来完成具体的硬件的MMC控制器初始化工作。
****************************************************/
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#else
#if defined(CONFIG_MMC)
puts("SD/MMC: ");
if (INF_REG3_REG == 0)
movi_ch = 0;
else
movi_ch = 1;
movi_set_capacity();
movi_init();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
#endif
#endif
if (INF_REG3_REG == BOOT_ONENAND) {
#if defined(CONFIG_CMD_ONENAND)
puts("OneNAND: ");
onenand_init();
#endif
/*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
} else {
puts("NAND: ");
nand_init();
if (INF_REG3_REG == 0 || INF_REG3_REG == 7)
setenv("bootcmd", "movi read kernel c0008000;movi read rootfs c0800000;bootm c0008000");
else
setenv("bootcmd", "nand read c0008000 80000 380000;bootm c0008000");
}
#endif /* CONFIG_SMDK6410 */
#if defined(CONFIG_SMDKC100)
//210SD卡相关配置
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#endif
#if defined(CONFIG_CMD_ONENAND)
puts("OneNAND: ");
onenand_init();
#endif
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init();
#endif
#endif /* CONFIG_SMDKC100 */
#if defined(CONFIG_X210)
//结束
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
#ifdef CONFIG_CHECK_X210CV3
check_flash_flag=0;//check inand error!
#endif
}
#ifdef CONFIG_CHECK_X210CV3
else
{
check_flash_flag=1;//check inand ok!
}
#endif
#endif
#if defined(CONFIG_MTD_ONENAND)
puts("OneNAND: ");
onenand_init();
/*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
#else
//puts("OneNAND: (FSR layer enabled)\n");
#endif
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init();
#endif
#endif /* CONFIG_X210 */
#if defined(CONFIG_SMDK6440)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#else
#if defined(CONFIG_MMC)
if (INF_REG3_REG == 1) { /* eMMC_4.3 */
puts("eMMC: ");
movi_ch = 1;
movi_emmc = 1;
movi_init();
movi_set_ofs(0);
} else if (INF_REG3_REG == 7 || INF_REG3_REG == 0) { /* SD/MMC */
if (INF_REG3_REG & 0x1)
movi_ch = 1;
else
movi_ch = 0;
puts("SD/MMC: ");
movi_set_capacity();
movi_init();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
} else {
}
#endif
#endif
if (INF_REG3_REG == 2) {
/* N/A */
} else {
puts("NAND: ");
nand_init();
//setenv("bootcmd", "nand read c0008000 80000 380000;bootm c0008000");
}
#endif /* CONFIG_SMDK6440 */
#if defined(CONFIG_SMDK6430)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#else
#if defined(CONFIG_MMC)
puts("SD/MMC: ");
if (INF_REG3_REG == 0)
movi_ch = 0;
else
movi_ch = 1;
movi_set_capacity();
movi_init();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
#endif
#endif
if (INF_REG3_REG == BOOT_ONENAND) {
#if defined(CONFIG_CMD_ONENAND)
puts("OneNAND: ");
onenand_init();
#endif
/*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
} else if (INF_REG3_REG == BOOT_NAND) {
puts("NAND: ");
nand_init();
} else {
}
if (INF_REG3_REG == 0 || INF_REG3_REG == 7)
setenv("bootcmd", "movi read kernel c0008000;movi read rootfs c0800000;bootm c0008000");
else
setenv("bootcmd", "nand read c0008000 80000 380000;bootm c0008000");
#endif /* CONFIG_SMDK6430 */
#if defined(CONFIG_SMDK6442)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#else
#if defined(CONFIG_MMC)
puts("SD/MMC: ");
movi_set_capacity();
movi_init();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
#endif
#endif
#if defined(CONFIG_CMD_ONENAND)
if (INF_REG3_REG == BOOT_ONENAND) {
puts("OneNAND: ");
onenand_init();
}
#endif
#endif /* CONFIG_SMDK6442 */
#if defined(CONFIG_SMDK2416) || defined(CONFIG_SMDK2450)
#if defined(CONFIG_NAND)
puts("NAND: ");
nand_init();
#endif
#if defined(CONFIG_ONENAND)
puts("OneNAND: ");
onenand_init();
#endif
#if defined(CONFIG_BOOT_MOVINAND)
puts("SD/MMC: ");
if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) {
printf("Boot up for burning\n");
} else {
movi_init();
movi_set_ofs(MOVI_TOTAL_BLKCNT);
}
#endif
#endif /* CONFIG_SMDK2416 CONFIG_SMDK2450 */
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
/* initialize environment */
env_relocate ();
/******************************************
环境变量的重定位,将SD卡中的环境变量搬移到DDR中
****************************************/
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
#ifdef CONFIG_SERIAL_MULTI
serial_initialize();
#endif
/* IP Address IP地址设置 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* MAC 地址设置 */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
#ifdef CONFIG_HAS_ETH1
i = getenv_r ("eth1addr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
#endif
}
/*设备初始化,和内核类似*/
devices_init (); /* get the devices list going. */
#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
/*跳转表*/
jumptable_init ();
#if !defined(CONFIG_SMDK6442)
/********************************************
控制台初始化的第二阶段,之前讲过console_init_f是第一阶段初始化,实际上第一阶段没有做什么实际的事情(只是执行了gd->have_console = 1;将这个变量设置为1),而控制台的初始化代码是在第二阶段完成的
**********************************/
console_init_r (); /* fully init console as a device */
#endif
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif
/* enable exceptions CPSR中的总中断标志位使能,,通过条件编译来选择函数的实体,也就是说uboot中有多个enable_interrupts ();函数,然后通过宏来决定使用哪个*/
enable_interrupts ();
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_TI_EMAC
extern void dm644x_eth_set_mac_addr (const u_int8_t *addr);
if (getenv ("ethaddr")) {
dm644x_eth_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
if (getenv ("ethaddr")) {
smc_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
/* Initialize from environment loadaddr:内核启动有关的环境变量 */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if defined(CONFIG_CMD_NET)
/* bootfile:内核启动有关的环境变量 */
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif
#ifdef BOARD_LATE_INIT
board_late_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
#if defined(CONFIG_CMD_IDE)
puts("IDE: ");
ide_init();
#endif
/****************lxg added**************/
#ifdef CONFIG_MPAD
extern int x210_preboot_init(void);
x210_preboot_init();
#endif
/****************end**********************/
/* check menukey to update from sd */
/*****************************************************
(1)uboot启动的最后阶段设计了一个自动更新的功能。就是:我们可以将要升级的镜像放到SD卡的固定目录中,然后开机时在uboot启动的最后阶段检查升级标志(是一个按键。按键中标志为"LEFT"的那个按键,这个按键如果按下则表update mode,如果启动时未按下则表示boot mode)。如果进入update mode则uboot会自动从SD卡中读取镜像文件然后烧录到iNand中;如果进入boot mode则uboot不执行update,直接启动正常运行。
(2)这种机制能够帮助我们快速烧录系统,常用于量产时用SD卡进行系统烧录部署。
********************************************************/
extern void update_all(void);
if(check_menu_update_from_sd()==0)//update mode
{
puts ("[LEFT DOWN] update mode\n");
run_command("fdisk -c 0",0);
update_all();
}
else
puts ("[LEFT UP] boot mode\n");
/* 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 */
}