• 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()函数类似。

  • 相关阅读:
    Ubuntu 16 安装redis客户端
    crontab 参数详解
    PHP模拟登录发送闪存
    Nginx配置端口访问的网站
    Linux 增加对外开放的端口
    Linux 实用指令之查看端口开启情况
    无敌的极路由
    不同的域名可以指向同一个项目
    MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error
    Redis 创建多个端口
  • 原文地址:https://www.cnblogs.com/Cqlismy/p/12194641.html
Copyright © 2020-2023  润新知