uboot第二阶段应该做什么?
uboot的第二阶段就是要初始化剩下的还没被初始化的硬件,主要是SOC外部硬件(譬如inand、网卡芯片)、uboot本身的一些东西(uboot的命令、环境变量等),然后最终初始化完必要的东西后进入uboot的命令行准备接受命令。
uboot第二阶段完结于何处?
uboot启动后自动运行打印出很多信息,这些信息就是uboot第一和第二阶段不断进行初始化时,打印出来的信息,然后uboot进入了bootdelay然后执行bootcmd对应的启动命令,如果这时候用户不干涉,会执行bootcmd进入自动启动内核的流程了。(uboot的生命周期就结束了);所以uboot完结于命令行下,读取命令,解析命令,执行命令。命令行死循环是uboot的最终归宿
void start_armboot (void) { 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_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t); #ifdef CONFIG_USE_IRQ gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); #endif gd = (gd_t*)gd_base; #else gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); #endif /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); memset ((void*)gd, 0, sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); memset (gd->bd, 0, sizeof (bd_t)); monitor_flash_len = _bss_start - _armboot_start; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } } #ifndef CFG_NO_FLASH /* configure available FLASH banks */ size = flash_init (); display_flash_config (size); #endif /* CFG_NO_FLASH */ #ifdef CONFIG_VFD # 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); #else mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); #endif //******************************// // Board Specific // #if defined(CONFIG_SMDKXXXX) //******************************// #if defined(CONFIG_SMDK6410) #if defined(CONFIG_GENERIC_MMC) puts ("SD/MMC: "); mmc_exist = mmc_initialize(gd->bd); if (mmc_exist != 0) { puts ("0 MB "); } #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) #if defined(CONFIG_GENERIC_MMC) puts ("SD/MMC: "); mmc_exist = mmc_initialize(gd->bd); if (mmc_exist != 0) { puts ("0 MB "); } #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 "); #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) "); #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 "); } #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 "); } #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 "); } #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 "); } 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 (); #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 */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); /* MAC Address */ { 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_r (); /* fully init console as a device */ #endif #if defined(CONFIG_MISC_INIT_R) /* miscellaneous platform dependent initialisations */ misc_init_r (); #endif /* enable exceptions */ 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 */ if ((s = getenv ("loadaddr")) != NULL) { load_addr = simple_strtoul (s, NULL, 16); } #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 #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 "); 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 */ extern void update_all(void); if(check_menu_update_from_sd()==0)//update mode { puts ("[LEFT DOWN] update mode "); run_command("fdisk -c 0",0); update_all(); } else puts ("[LEFT UP] boot mode "); /* 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 */ }
start_armboot分析(ubootuboot_jiudingubootlib_arm 444行):
① init_fnc_t **init_fnc_ptr;
解析:
typedef int (init_fnc_t) (void);
这是一个函数类型,init_fnc_ptr就是一个二重函数指针,这里是用来指向一个函数指针数组。
②
这个文件开头有一个宏: DECLARE_GLOBAL_DATA_PTR;
跳到定义处:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
定义了一个全局变量gd,这个变量是一个指针类型,占四个字节,用volatile和register修饰,后面asm ("r8"),是gcc支持的;把gd放到r8中;
将这些全局变量定义成结构体gd,然后放到寄存器中,定义为register变量完全是为了访问效率的提高。
gd_t定义在include/asm-arm
typedef struct global_data { bd_t *bd; unsigned long flags; unsigned long baudrate; unsigned long have_console; /* serial_init() was called */ unsigned long reloc_off; /* Relocation Offset */ 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 void **jt; /* jump table */ } gd_t; /* * 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 DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
#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 */ unsigned char bi_enetaddr[6]; /* Ethernet adress */ 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]; #ifdef CONFIG_HAS_ETH1 /* second onboard ethernet port */ unsigned char bi_enet1addr[6]; #endif } bd_t; #define bi_env_data bi_env->data #define bi_env_crc bi_env->crc #endif /* _U_BOOT_H_ */
总结:gd_t定义了很多全局变量,都是整个uboot使用的,其中一个bd_t类型指针,指向了一个bd_t类型的变量,这个bd是开发板的板级信息的结构体,里面有不少硬件相关的参数,譬如波特率、IP地址、机器码、DDR内存的分布等
③gd和bd变量的内存排布
gd的定义本身只是一个指针而已;
为什么分布内存?
(1)DECLARE_GLOBAL_DATA_PTR;只是定义了一个指针,只是分配在寄存器里面了,也就是说gd里的这些全局变量并没有被分配内存,我们在使用gd之前要给它分配内存,否则gd也只是一个野指针而已。
(2)gd和bd需要内存,内存当前没人管理(因为没有操作系统统一管理内存)大片的DDR内存可以散放着可以随意使用(只要使用内存地址去访问内存即可);但是因为uboot中后续很多操作还需要大片的连着的内存块,所以这里使用内存要本着够用就好,紧凑排布的原则,我们就需要有一个规划了。这里就要研究一个内存排布的问题
内存排布:
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
(1)uboot区 长度为CFG_UBOOT_BASE + CFG_UBOOT_SIZE
(2)堆区 长度为CFG_MALLOC_LEN 实际912KB
(3)栈区 长度为CFG_STACK_SIZE 实际512KB
(4)gd 长度为sizeof(gd_t),实际36个字节,
(5)bd 长度为sizeof(bd_t) 实际为44个字节左右
(6)内存间隔 /* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
避免 gcc版本高于3.4的优化造成的错误
实例化:
gd = (gd_t*)gd_base;
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
拿到一片内存后实例化。同时使用memset进行清零内存。
④init_sequence
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
(1)它是一个函数指针数组,这个数组存放了很多函数指针,这些指针指向的函数都是init_fnc_t类型,这个类型是接收的参数是void类型,返回int类型,
(2)init_sequence定义时就给了初始化,初始化的函数指针都是一些函数名。函数名就是一个函数指针,这是基础知识。
(3)init_fnc_ptr是一个二重函数指针,可以指向init_sequence这个函数指针数组。
(4)用一个for循环,是想要遍历这个函数指针数组,我们遍历他的目的也是去依次执行这个函数指针数组中的所有函数;思考:如何遍历一个函数指针数组?
两种方法:第一种:用下标去遍历,用数组的元素个数来截止。
第二种:不常用,但是也可以,在数组有效元素的末尾放一个标志,依次遍历到标志处,这个时候来截止。这种思路有点像字符串的思路。
这里使用的是第二种,因为数组里存放的全是函数指针,因此我们选用了NULL来作为标志,知道看到NULL来截止,这样做的好处是不用事先统计数组元素个数。可以灵活的添加或者删除。
(5)init_fnc_t这些函数的特点是正确时返回0,不正确时返回-1,所以我们在遍历时去检查函数返回值,如果遍历中,有一个函数返回值不为0,那么就调用hang();挂起函数,
void hang (void) { puts ("### ERROR ### Please RESET the board ### "); for (;;); }
所以uboot启动时,初始化板级硬件不能出任何错误,只要有一个错误就终止整个启动,只能重启。
(6)init_sequence中的这些函数,都是board级别的初始化
init_fnc_t *init_sequence[] = { cpu_init, /* basic cpu dependent setup */ #if defined(CONFIG_SKIP_RELOCATE_UBOOT) reloc_init, /* Set the relocation done flag, must do this AFTER cpu_init(), but as soon as possible */ #endif 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 */ #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 */ display_dram_config, NULL, };
一:cpu_init
真正的CPU初始化已经在之前结束了,所以这里是空的
board_init
它在/uboot/board/samsung/x210/x210.c中,
int board_init(void) { DECLARE_GLOBAL_DATA_PTR; #ifdef CONFIG_DRIVER_SMC911X smc9115_pre_init(); #endif #ifdef CONFIG_DRIVER_DM9000 dm9000_pre_init(); #endif gd->bd->bi_arch_number = MACH_TYPE; gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100); return 0; }
DECLARE_GLOBAL_DATA_PTR;在这里声明是为了在后面访问gd,因此这个gd定义成一个宏,这样比较方便,这样的方式跟头文件包含一样,但是没有使用头文件包含。
这里面有两个初始化
网卡初始化
dm9000_pre_init();这个函数就是对应的DM9000网卡初始化的函数,这个函数是网卡gpio和端口的配置,而不是驱动,因为驱动都是现成的正确的,移植的时候驱动是不需要更改的。
DDR配置的背景知识:
初始化DDR,这里的初始化DDR和之前在汇编阶段的lowlevel_inti中初始化ddr是不同的,当时时,硬件初始化,让DDR可以工作,现在是软件结构中DDR相关的属性配置和一些地址设置的初始化,是纯软件层面的,不是硬件的初始化,为什么要在软件层次初始化DDR,因为对uboot来说,它怎么知道我们的开发板上到底有几片DDR内存,每一片的起始地址和长度这些信息呢?在uboot的设计中,采用了一种简单直接有效的方式,那就是程序员移植uboot到一个开发板中,程序员自己在x210_sd.h中使用宏定义去配置出来板子上DDR的信息,uboot只需要读取这些信息即可,实际上我们还有另外一条思路,uboot通过代码读取硬件的一些信息来知道DDR的配置,但是uboot没有采用这样的方式,实际上PC的BIOS采用的是这种。
在x210_sd.h的496行到501行中使用了标准的宏定义来配置DDR相关的参数,主要这么几个信息:有几片DDR内存,每一片DDR内存的起始地址、长度。这里的配置信息,在整个uboot代码中使用到内存时,可以从这里提取使用。uboot中使用到内存的地方都不是用地址数字的,都是用宏定义的。
#define PHYS_SDRAM_1 MEMORY_BASE_ADDRESS /* SDRAM Bank #1 */ #define PHYS_SDRAM_1_SIZE SDRAM_BANK_SIZE #define PHYS_SDRAM_2 MEMORY_BASE_ADDRESS2 /* SDRAM Bank #2 */ #define PHYS_SDRAM_2_SIZE SDRAM_BANK_SIZE #define CFG_FLASH_BASE 0x80000000
gd->bd->bi_arch_number = MACH_TYPE;
开发板的 机器码;就是uboot给开发板定义的唯一编号,
机器码的主要作用就是在uboot和Linux内核之间进行比对和适配,主要原因是嵌入式设备中,每一个设备的硬件都是定制化的,不能通用。这就告诉我们,这个开发板移植的内核镜像绝对不能下载到另一个开发板去,否则也不能启动,就算启动也不能正常工作,有很多隐患,因此Linux做了个设置:给每个开发板做唯一编号(机器码),然后在uboot中、Linux内核中,都有一个软件维护的机器码编号,然后开发板 uboot Linux三者之间去比对机器码,若果机器码对上了,就启动,否则就不启动。
MACH_TYPE在x210_sd.h中定义 值为2456,这个编号代表了,x210开发板的机器码,将来这个开发版的机器码上面移植的机器码也是2456,不然就启动不起来
uboot配置的机器码,会作为uboot给Linux内核传参的一部分传给Linux内核,内核启动过程中,会对这个接收到的机器码和自身的机器码相比对,如果相等就启动,如果不想等就不启动,
理论上来说一个开发板的机器码不能自己随便定。有权利去发放机器码的只有uboot官方,所以我们做好一个开发板并且移植了uboot之后,理论上应该提交给uboot官方审核并发放机器码(好像是免费的),但是国内的开发板基本都没有申请,主要原因是因为国内开发者英文都不行,和国外开源社区的接触比较少,都是自己随便编号的,随便编号的问题就是有可能和别人的编号冲突,但是只要保证自己的uboot和kernel中保持一致,就不影响自己的开发板冲突。
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
(1)bd_info中另一个主要的元素bi_boot_params表示uboot给Linux内核启动时传参的内存地址,uboot事先将准备好的传参bootargs放置到内存的一个地址处,然后uboot就启动了内核(uboot在启动内核时,真正是通过寄存器r0 r1 r2来直接传递参数的,其中有一个寄存器就是bi_boot_params,内核启动后从寄存器中读取bi_boot_params就知道了uboot给我传递的参数到底放在内存哪里,然后自己去内存那个地方找bootargs)
经过计算得知x210中,(PHYS_SDRAM_1+0x100)为0x30000100这个地址将来就被分配来做内核传参了,所以在uboot中其他地方使用内存时要注意,千万不要把这里淹没了。
二:interrupt_init
int interrupt_init(void) { S5PC11X_TIMERS *const timers = S5PC11X_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 @ 66 MHz */ timer_load_val = get_PCLK() / (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 & ~0x00700000) | TCON_4_AUTO | TCON_4_UPDATE; /* auto load, start Timer 4 */ timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON; timestamp = 0; return (0); }
typedef struct { S5PC11X_REG32 TCFG0; S5PC11X_REG32 TCFG1; S5PC11X_REG32 TCON; S5PC11X_TIMER ch[4]; S5PC11X_REG32 TCNTB4; S5PC11X_REG32 TCNTO4; } /*__attribute__((__packed__))*/ S5PC11X_TIMERS;
/* return PCLK frequency */ ulong get_PCLK(void) { ulong hclk; uint div = CLK_DIV0_REG; uint pclk_msys_ratio = ((div>>12) & 0x7); hclk = get_HCLK(); return hclk/(pclk_msys_ratio+1); }
S5PV210共有五个寄存器,timer0-timer4。timer0-timer3都有输出引脚,timer4没有输出引脚,没法输出pwm波形。这个timer4设计的时候就不是用来设计输出pwm波形的,这个定时器被设计用来做计时。它用来做计时时,要使用两个寄存器TCNTB4,TCNTO4,一个是用来定时长,一个是用来观察,TCNTB4存了一个数,就是定时的次数,每一次的时间就是由两级时钟分频器决定的,我们定时时,只需要把定时时间/基准时间=数,再将这个数放到TCNTB4中,我们通过读取TCNTO4可以看到计数有没有减到0,读取到0后,就知道计数器的时间已经到了。
使用timer4时,没有中断,所以CPU只能通过轮询的方式来不断的查看TCNTO4寄存器,才能知道时间到了没。没有实现微观上的并行,在操作系统中就不可以通过timer4来进行定时了。(bootdelay就是轮询的方式实现的)
总结:这里需要学习的是通过定义结构体的方式来访问寄存器,通过函数来自动计算设置值以设置定时器
三:env_init
有很多env_init函数,主要是因为uboot支持很多启动方式,我们一般从哪种启动介质就会把环境变量放在哪种,各种介质操作env_init不一样,实际使用的是哪一个要根据自己开发板来定(这些env_xx.c同时只有一个会起作用,其他是不能被链接的,通过x210_sd.h中配置的宏来决定是谁被包含的,)对于inand版本的x210来说,我们应该看env_movi.c中的,
int env_init(void) { #if defined(ENV_IS_EMBEDDED) ulong total; int crc1_ok = 0, crc2_ok = 0; env_t *tmp_env1, *tmp_env2; total = CFG_ENV_SIZE; tmp_env1 = env_ptr; tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE); crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); if (!crc1_ok && !crc2_ok) gd->env_valid = 0; else if(crc1_ok && !crc2_ok) gd->env_valid = 1; else if(!crc1_ok && crc2_ok) gd->env_valid = 2; else { /* both ok - check serial */ if(tmp_env1->flags == 255 && tmp_env2->flags == 0) gd->env_valid = 2; else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) gd->env_valid = 1; else if(tmp_env1->flags > tmp_env2->flags) gd->env_valid = 1; else if(tmp_env2->flags > tmp_env1->flags) gd->env_valid = 2; else /* flags are equal - almost impossible */ gd->env_valid = 1; } if (gd->env_valid == 1) env_ptr = tmp_env1; else if (gd->env_valid == 2) env_ptr = tmp_env2; #else /* ENV_IS_EMBEDDED */ gd->env_addr = (ulong)&default_environment[0]; gd->env_valid = 1; #endif /* ENV_IS_EMBEDDED */ return (0); }
这个函数只对内存里维护的那一份env做了基本的判定(判定里面有没有能用的环境变量),当前因为还没进行环境变量从SD卡到DDR的重定位,因此当前的环境变量是不可以用的,在start_armboot的函数中,776行才调用了env_relocate函数才进行环境变量从SD卡中到DDR中的重定位,重定位之后需要环境变量时,才可以从DDR中去取,重定位之前如果需要环境变量,需要从SD卡中去读取。
四:init_baudrate
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); }
int getenv_r (char *name, char *buf, unsigned len) { int i, nxt; for (i=0; env_get_char(i) != '