• 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
    ");
        reset_phy();
    #endif
        return 0;
    }
    #endif

    需要定义相关宏CONFIG_CMD_NET才会调用该函数,如果初始化成功的话,串口会输出对应的信息。

    最后,运行run_main_loop()函数,主循环函数,用于处理输入的命令,该函数的实现过程分析另起文章分析。

    3、小结

    本篇文章主要是对board_init_r()函数进行简单分析,该函数用于板级后期的一些外设初始化和设置gd结构体的成员变量,函数的调用过程和board_init_f()函数类似。

  • 相关阅读:
    无法启动调试--未安装 Silverlight Developer 运行时。请安装一个匹配版本。
    jQuery导航菜单防刷新
    IE6下Png透明最佳解决方案(推荐) Unit PNG Fix
    每周进步要点(第50周12.4-12.11)
    学习笔记:重塑你的自我驱动力
    学习笔记之是什么决定我们的命运
    读书《万万没想到 3》
    人与人之间的鄙视链是如何形成的?
    第7本《万万没想到-用理工科思维理解世界2》
    中明写公众号的时候他在想什么
  • 原文地址:https://www.cnblogs.com/Cqlismy/p/12194641.html
Copyright © 2020-2023  润新知