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 "); reset_phy(); #endif return 0; } #endif
需要定义相关宏CONFIG_CMD_NET才会调用该函数,如果初始化成功的话,串口会输出对应的信息。
最后,运行run_main_loop()函数,主循环函数,用于处理输入的命令,该函数的实现过程分析另起文章分析。
3、小结
本篇文章主要是对board_init_r()函数进行简单分析,该函数用于板级后期的一些外设初始化和设置gd结构体的成员变量,函数的调用过程和board_init_f()函数类似。