Uboot啟動流程分析(六)


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM