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()函數類似。
