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,
    initr_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,
#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,
#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的大小。

3、小结

猜你喜欢

转载自www.cnblogs.com/Cqlismy/p/12194641.html