1、前言
在前面的文章《Uboot启动流程分析(三)》和《Uboot启动流程分析(四)》,链接分别如下:
https://www.cnblogs.com/Cqlismy/p/12006287.html
https://www.cnblogs.com/Cqlismy/p/12147411.html
已经对board_init_f函数进行了简单介绍,在这个函数当中,会调用一系列的函数去初始化一些早期的板子外设和gd结构体的成员变量,但是board_init_f函数并没有将所有的外设进行初始化,还有一些后续的工作需要完成,这些工作就是由board_init_r函数去完成。
在介绍board_init_r函数之前,先来回忆一下_main函数的简单调用流程,如下:
_main
|
board_init_f_alloc_reserve-->reserve gd and early malloc area
|
board_init_f_init_reserve-->initialize global data
|
board_init_f-->initialize ddr,timer...,and fill gd_t
|
relocate_code-->relocate uboot code
|
relocate_vectors-->relocate vectors
|
board_init_r-->calling board_init_r
可以看到board_init_r函数处于_main函数的最后阶段了,board_init_r函数的执行过程和board_init_f函数非常类似,因此,将会使用相同的方法对该函数过程进行分析。
2、board_init_r函数
在uboot源码中,board_init_r函数的定义在下面的文件中:
uboot/common/board_r.c
函数的定义如下所示:
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
int i;
#endif
#ifdef CONFIG_AVR32
mmu_init_r(dest_addr);
#endif
#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
gd = new_gd;
#endif
#ifdef CONFIG_NEEDS_MANUAL_RELOC
for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
init_sequence_r[i] += gd->reloc_off;
#endif
if (initcall_run_list(init_sequence_r)) /* 调用一系列初始化函数 */
hang();
/* NOTREACHED - run_main_loop() does not return */
hang();
}
和board_init_f函数类似,调用initcall_run_list()函数来进行初始化,init_sequence_r是一个数组,也就是函数初始化序列,为了兼容多款板子,里面包含了大量的条件编译函数,将一些无关条件编译代码去掉后,其定义如下:
init_fnc_t init_sequence_r[] = {
initr_trace, /* 初始化buffer跟踪相关 */
initr_reloc, /* 标记重定位完成 */
#ifdef CONFIG_ARM
initr_caches, /* 使能caches */
#endif
initr_reloc_global_data, /* 初始化gd的一些成员变量 */
initr_barrier,
initr_malloc, /* 初始化malloc区域 */
initr_console_record, /* 初始化控制台相关内容 */
bootstage_relocate, /* 启动状态重定位 */
initr_bootstage, /* 初始化bootstage */
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
board_init, /* Setup chipselects */
#endif
stdio_init_tables, /* stdio相关初始化 */
initr_serial, /* serial接口初始化 */
initr_announce,
INIT_FUNC_WATCHDOG_RESET
power_init_board,
#ifndef CONFIG_SYS_NO_FLASH
initr_flash,
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_NAND
initr_nand, /* 初始化nand flash */
#endif
#ifdef CONFIG_GENERIC_MMC
initr_mmc,
#endif
initr_env, /* 环境变量初始化 */
INIT_FUNC_WATCHDOG_RESET
initr_secondary_cpu,
stdio_add_devices,
initr_jumptable,
console_init_r, /* fully init console as a device */
interrupt_init,
#if defined(CONFIG_ARM) || defined(CONFIG_AVR32)
initr_enable_interrupts,
#endif
#ifdef CONFIG_CMD_NET
initr_ethaddr, /* 获取mac地址 */
#endif
#ifdef CONFIG_BOARD_LATE_INIT
board_late_init, /* 板子后期外设初始化 */
#endif
#ifdef CONFIG_CMD_NET
INIT_FUNC_WATCHDOG_RESET
initr_net, /* 初始化板子的网络设备 */
#endif
run_main_loop,
};
接下来,对init_sequence_r内定义的函数进行简要分析:
首先是initr_trace()函数,该函数的定义如下:
static int initr_trace(void)
{
#ifdef CONFIG_TRACE
trace_init(gd->trace_buff, CONFIG_TRACE_BUFFER_SIZE);
#endif
return 0;
}
该函数中,如果定义了CONFIG_TRACE宏的话,将调用trace_init()函数,是与初始化和调试跟踪相关的内容。
接下来,调用initr_reloc()函数,该函数定义如下:
static int initr_reloc(void)
{
/* tell others: relocation done */
gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;
return 0;
}
initr_reloc函数设置了gd->flags成员,标记uboot重定位完成。
接下来,判断是否定义了宏CONFIG_ARM,对于imx6ul定义了该宏,将调用initr_caches()函数,该函数定义如下:
static int initr_caches(void)
{
/* Enable caches */
enable_caches();
return 0;
}
从代码可以知道,该函数用于使能芯片的caches。
接下来调用initr_reloc_global_data()函数用于初始化重定为后gd的一些成员变量。
对于imx6ul,initr_barrier()为空函数,直接返回。
继续调用initr_console_record()函数,用于初始化控制台相关内容,对于imx6ul为空函数。
bootstage_relocate()函数用于重定位bootstage相关的东西。
接下来,继续调用initr_bootstage()函数,用于初始化bootstage相关的内容。
函数继续执行,接下来判断是否定义宏CONFIG_ARM,对于imx6ul,定义了该宏,所以会调用board_init()函数,该函数与板级相关,定义在下面文件:
uboot/board/freescale/mx6ul_comp6ul_nand/mx6ul_comp6ul_nand.c
该函数的定义如下所示:
int board_init(void)
{
/* Address of boot parameters */
gd->bd->bi_boot_params = PHYS_SDRAM + 0x100;
imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));
iox74lv_init(); /* 初始化74hc595芯片(串行输入、并行输出) */
#ifdef CONFIG_SYS_I2C_MXC
setup_i2c(0, CONFIG_SYS_I2C_SPEED, 0x7f, &i2c_pad_info1); /* 初始化I2C */
#endif
#ifdef CONFIG_FEC_MXC
setup_fec(CONFIG_FEC_ENET_DEV); /* 网络相关初始化 */
#endif
#ifdef CONFIG_USB_EHCI_MX6
setup_usb(); /* USB接口初始化 */
#endif
#ifdef CONFIG_FSL_QSPI
board_qspi_init();
#endif
#ifdef CONFIG_NAND_MXS
setup_gpmi_nand();
#endif
return 0;
}
从函数中可以看到,这是个板级初始化函数,主要是用来初始化了板子上的一些外设,例如GPIO、I2C接口和网络相关接口等。
接下来,继续调用stdio_init_tables()函数用于初始化stdio相关东西。
调用initr_serial()函数初始化串口相关东西。
调用initr_announce()函数,调试相关的内容,用于通知已经在DRAM中运行。
继续调用power_init_board()函数,用于初始化电源相关的芯片。
接下来,判断有没有定义宏CONFIG_SYS_NO_FLASH,如果没有定义该宏的话,调用函数initr_flash(),但是对于imx6ul中,定义了该宏,因此,该函数无效。
函数继续执行,接下来,判断是否定义了宏CONFIG_CMD_NAND,对于使用Nand Flash启动的,将会定义该宏,因此会调用initr_nand()函数初始化nand,该函数定义如下:
#ifdef CONFIG_CMD_NAND
/* go init the NAND */
static int initr_nand(void)
{
puts("NAND: ");
nand_init();
return 0;
}
#endif
函数执行后,会将nand flash进行初始化,并在串口以字符串的形式输出nand flash的大小。
接下来,判断是否定义了CONFIG_GENERIC_MMC宏,对于imx6ul,该宏定义在文件:
uboot/include/imx6_common.h
因此,函数initr_mmc()会执行,用来初始化和sd/mmc相关的接口。
initr_env()函数用来初始化环境变量。
initr_secondary_cpu()用来初始化其它的CPU核,但是对于imx6ul只有一个CPU核,因此此函数无效。
stdio_add_devices()函数用于初始化各种输入输出设备,例如LCD相关的设备。
initr_jumptable()函数用来初始化跳转表相关的内容。
console_init_r()函数用于完成控制器台的初始化,该函数调用后,uboot将输出如下:
In: serial
Out: serial
Err: serial
interrupt_init()函数用来初始化中断相关内容,initr_enable_interrupts()函数用来使能中断。
接下来,判断是否定义CONFIG_CMD_NET宏,对于imx6ul相关的板级配置文件,定义了该宏,因此会调用initr_ethaddr()函数初始化网络地址,用于获取网卡的MAC地址。
board_late_init()函数用于板级后续的一些外设初始化。
接下来,调用函数initr_net()初始化板子上的网络设备,该函数的定义如下:
#ifdef CONFIG_CMD_NET
static int initr_net(void)
{
puts("Net: ");
eth_initialize();
#if defined(CONFIG_RESET_PHY_R)
debug("Reset Ethernet PHY\n");
reset_phy();
#endif
return 0;
}
#endif
需要定义相关宏CONFIG_CMD_NET才会调用该函数,如果初始化成功的话,串口会输出对应的信息。
最后,运行run_main_loop()函数,主循环函数,用于处理输入的命令,该函数的实现过程分析另起文章分析。
3、小结
本篇文章主要是对board_init_r()函数进行简单分析,该函数用于板级后期的一些外设初始化和设置gd结构体的成员变量,函数的调用过程和board_init_f()函数类似。