这里让UBOOT使用串口作为控制台输出,必须要保证串口功能被完善的支持。不过在启动到初始化串口功能之前,我们必须保证前面的初始化功能能顺利走完。
先来看看UBOOT的一个启动流程:
UBOOT起始代码所在的文件一般命名为start.S。这是一段汇编代码,一般位于各个CPU体系的文件夹下,为使用该CPU体系的芯片提供基本的初始化功能。
S3C24X0芯片属于ARM 920T的CPU体系。其start.S的路径位于arch\arm\cpu\arm920t\这段汇编代码一般被称作第一阶段初始化代码。主要作用是初始化运行环境;初始化内存;重新放置UBOOT代码到内存中;跳入到内存中执行第二段初始化代码。其流程大致如下:
在上面的流程中,有几点需要修改:
S3C2410在初始化的时候会关闭中断(主中断和子中断都要关掉),不过S3C2410和S3C2440的中断配置是相同的。所以将“# if defined(CONFIG_S3C2410)”修改成“# if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)”可以保证中断被正确的屏蔽。
cpu_init_crit函数的功能是关闭MMU、初始化内存。所以该函数是必须要执行的。至于lowlevel_init函数,其位于board\samsung\fzb2440\目录下,这个是需要用户自己编写的部分,内存的初始化可见《TQ2440的学习——将按键驱动LED灯的代码复制到SDRAM中运行》,这里S3C2410和S3C2440是相通的。要做的就是修改下内存的刷新率之类的参数。
执行完cpu_init_crit函数后,UBOOT就要将代码放置到内存中去。这部分代码是从NOR FLASH或者内部RAM将代码复制到内存中去。所以如果从NAND FLASH启动的话,注意不要开启这部分代码。TQ2440有一块NOR FLASH,所以本次UBOOT也就设计成从NOR FLASH启动。通过这部分代码熟悉下UBOOT在内存中被放置的位置是有好处的,可以得出UBOOT的内存分布图,方便调试。
首先,UBOOT要确定当前代码所在位置是FLASH还是RAM。方法是比较_start和_TEXT_BASE,前者代表代码起始的地址(一般为0x0),后者代表编译代码的时候指定的起始地址(这里指定为)。相同的话则跳入到栈设置,否则计算出_bss_start - _armboot_start的值。这个值表示的是UBOOT的大小。
接下来进入copy_loop循环,将以_start为起始地址的代码数据复制到以_TEXT_BASE为起始地址的内存中,长度为_bss_start - _armboot_start。
代码复制完后就是栈的设置,可以看到栈的起始地址是_TEXT_BASE - CONFIG_SYS_MALLOC_LEN - CONFIG_SYS_GBL_DATA_SIZE – 8。
最后将_bss_start到_bss_end之间的数据全部清0,就可以跳转到start_armboot函数中去了,也就是第二段初始化代码,位于arch\arm\cpu\arm92t\board.c这个文件当中。
对于S3C2440来说,这部分和S3C2410是相同的。所以一般不会有太大问题。
在第二段代码中,UBOOT会对一些设备进行初始化设置,其中也包括串口的设置。
UBOOT为了提高代码的简洁性,使用了一个函数指针数组init_fnc_ptr。一些初始化函数被放在这个数组当中。
01 init_fnc_t *init_sequence[] =
02 {
03 #if defined(CONFIG_ARCH_CPU_INIT)
04 arch_cpu_init, /* basic arch cpu dependent setup */
05 #endif
06 board_init, /* basic board dependent setup */
07 #if defined(CONFIG_USE_IRQ)
08 interrupt_init, /* set up exceptions */
09 #endif
10 timer_init, /* initialize timer */
11 #ifdef CONFIG_FSL_ESDHC
12 get_clocks,
13 #endif
14 env_init, /* initialize environment */
15 init_baudrate, /* initialze baudrate settings */
16 serial_init, /* serial communications setup */
17 console_init_f, /* stage 1 init of console */
18 display_banner, /* say that we are here */
19 #if defined(CONFIG_DISPLAY_CPUINFO)
20 print_cpuinfo, /* display cpu info (and speed) */
21 #endif
22 #if defined(CONFIG_DISPLAY_BOARDINFO)
23 checkboard, /* display board info */
24 #endif
25 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
26 init_func_i2c,
27 #endif
28 dram_init, /* configure available RAM banks */
29 #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
30 arm_pci_init,
31 #endif
32 display_dram_config,
33 NULL,
34 };
在执行这个初始化列表之前,UBOOT首先要为global_data(全局数据结构体)和bd_info(板子信息结构体)开辟内存空间并初始化。这两段数据可以说是整个UBOOT运行环境的一个展示。
01 /* Pointer is writable since we allocated a register for it */
02 gd = (gd_t *)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
03 /* compiler optimization barrier needed for GCC >= 3.4 */
04 __asm__ __volatile__("": : :"memory");
05
06 memset ((void *)gd, 0, sizeof (gd_t));
07 gd->bd = (bd_t *)((char *)gd - sizeof(bd_t));
08 memset (gd->bd, 0, sizeof (bd_t));
09
10 gd->flags |= GD_FLG_RELOC;
上述结构体初始化完成后,UBOOT就会顺序执行init_fnc_ptr中的函数。其中有一些函数使用宏在控制,可以根据自己的需要开启相关的宏。这里就按照SMDK2410的默认宏设置来配置这部分代码。首先执行的是board_init函数,其位于board\samsung\smdk2410\目录下。这个函数是用户自己实现的部分,SMDK2410在这里实现了CPU的设置和GPIO的设置,S3C2440和S3C2410在系统时钟和GPIO部分几乎相同,不过S3C2440比S3C2410在GPIO方面多扩展了几组,而且功能也有一定调整。可以将两者的芯片资料关于这部分的介绍对比一下,这里直接将SMDK2410的GPIO设置用来设置TQ2440(只需要引导LINUX的话,这样的配置无问题,如果要配置其他功能可自行修改)。
108 gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
这句需要修改下,毕竟不是2410了要改成MACH_TYPE_S3C2440。这样LINUX在引导的时候能正确识别CPU信号。
111 gd->bd->bi_boot_params = 0x30000100;
这句就是UBOOT指定的LINUX引导启动参数存放的地方。引导LINUX的时候,会从这个地方读取启动参数。所以到时候要保证两者的一致性。
下面执行timer_init函数,这个部分位于arch\arm\cpu\arm920t\s3c24x\timer.c文件里。初始化了定时器。时间控制是一个调度系统的核心之一,所以这部分是非常重要的。S3C2440和S3C2410在定时器部件上是完全一样的,所以可以将其应用到本次的移植之中。
timer_init执行完后是env_init,这个函数是环境的初始化函数。该函数的主要功能就是将存储在某个位置的环境参数读取到UBOOT中去,可以预先将环境变量保存到某个位置,然后让UBOOT在启动的时候去读取,这里也许是用于一些早已配置好环境变量的板子的功能。但是TQ2440没有将UBOOT的环境变量保存在任何地方,所以感觉现在使用CONFIG_ENV_IS_NOWHERE要好一点(将这个宏添加到fzb2440.h中去),表示环境变量没有保存在任何地方。
在前面的初始化执行完后,UBOOT就开始初始化串口了。不过这里要注意S3C2410和S3C2440在串口初始化可以共享一套代码,但是这套代码CONFIG_S3C2410也控制了一部分,所以在定义了CONFIG_S3C2410的地方也加上CONFIG_S3C2440这样就可以保证串口的代码被完整的使用。控制台的初始化函数console_init_f确定了串口1作为输出。当前面所有这些被正确初始化后,在display_banner函数里将会向串口控制台输出一些字符串: