start_armboot函数(三)
注:本次分析的u-boot是九鼎官方的u-boot代码
下载地址:链接:http://pan.baidu.com/s/1gfpDZqj 密码:7cqe
接着上一章的内容;
1.interrupt_init
这个函数看名字是中断初始化,其实是定时器的初始化,准确说是Timer4,代码如下:
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);
}
(1)x210中有5个PWM定时器,其中只有Timer4没有输出,所以这个定时器主要是用来做计时器的。
(2)Timer4用来做计时时要使用到2个寄存器:TCNTB4、TCNTO4。TCNTB中存了一个数,这个数就是定时次数(每一次时间是由时钟决定的,其实就是由2级时钟分频器决定的)。我们定时时只需要把定时时间/基准时间=数,将这个数放入TCNTB中即可;我们通过TCNTO寄存器即可读取时间有没有减到0,读取到0后就知道定的时间已经到了。
(3)使用Timer4来定时,因为没有中断支持,所以CPU不能做其他事情同时定时,CPU只能使用轮询方式来不断查看TCNTO寄存器才能知道定时时间到了没。因为Timer4的定时是不能实现微观上的并行。uboot中定时就是通过Timer4来实现定时的,所以uboot中定时时不能做其他事,比如bootdelay。
(4)interrupt_init函数将timer4设置为定时10ms。关键部位就是get_PCLK函数获取系统设置的PCLK_PSYS时钟频率,然后设置TCFG0和TCFG1进行分频,然后计算出设置为10ms时需要向TCNTB中写入的值,将其写入TCNTB,然后设置为auto reload模式,最后开定时器开始计时。
2.env_init
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);
}
这个函数 的内容主要是对内存里维护的那一份uboot的环境变量(env)的一个初始化或者说是判定里面有没有能用的环境变量。当前因为我们还没进行环境变量从SD卡到DDR中的relocate,因此当前环境变量是不能用的。
在start_armboot函数中(776行)调用env_relocate才进行环境变量从SD卡中到DDR中的重定位。重定位之后需要环境变量时才可以从DDR中去取,重定位之前如果要使用环境变量只能从SD卡中去读取。
3.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);
}
首先先用getenv_r读取环境变量中的baudrate,如果读取成功(i>0),则用simple_strtoul函数将读取的字符串转换为十进制(用getenv函数读取环境变量中“baudrate”的值不是int类型的,而是字符串类型);如果读取不成功则将CONFIG_BAUDRATE的值赋值过去,这个宏的大小115200。
从中我们可以看出,环境变量的优先级比较高。
4.serial_init
因为在start.S中已经初始化过了,所以这里函数为空。
5.console_init_f
(1)console_init_f是console(控制台)的第一阶段初始化。_f表示是第一阶段初始化,_r表示第二阶段初始化。有时候初始化函数不能一次一起完成,中间必须要夹杂一些代码,因此将完整的一个模块的初始化分成了2个阶段。(我们的uboot中start_armboot的826行进行了console_init_r的初始化)
(2)console_init_f在uboot/common/console.c中,仅仅是对gd->have_console设置为1而已,其他事情都没做。
6.display_banner
(1)diaplay_banner函数主要是用来打印u-boot的logo信息的,代码如下:
printf ("\n\n%s\n\n", version_string);
这里便出现了一个问题:display_banner中使用printf函数向串口输出了version_string这个字符串。那么上面的分析表示console_init_f并没有初始化好console怎么就可以printf了呢?
答:通过追踪printf函数是如何实现的可以解答这个问题。printf函数是通过puts函数实现的,而puts函数中会判断当前uboot中console有没有被初始化好。如果console初始化好了则调用fputs完成串口发送(这条线才是控制台);如果console尚未初始化好则会调用serial_puts函数,在serial_puts中再调用serial_putc直接操作串口寄存器进行内容发送;
(2)version_string定义如下:
const char version_string[] =
U_BOOT_VERSION" (" __DATE__ " - " __TIME__ ")"CONFIG_IDENT_STRING;
其中,U_BOOT_VERSION是在makefile中定义的,DATE__与__TIME 表示u-boot编译的日期与时间,CONFIG_IDENT_STRING是一个宏定义,内容是“for x210”;
7.print_cpuinfo
函数作用是打印cpu相关信息,比如在u-boot启动过程中会出现:
CPU: S5PV210@1000MHz(OK)
APLL = 1000MHz, HclkMsys = 200MHz, PclkMsys = 100MHz
MPLL = 667MHz, EPLL = 96MHz
HclkDsys = 166MHz, PclkDsys = 83MHz
HclkPsys = 133MHz, PclkPsys = 66MHz
SCLKA2M = 200MHz
Serial = CLKUART
这些信息都是print_cpuinfo打印出来的。(注意:上面是u-boot启动时显示的内容,不是代码)