uboot.2017uboot啓動及時鐘系統分析

前言

2017.01 UBoot包含兩個階段的啓動,一個是SPL啓動,一個是正常的啓動我們稱爲第二階段Uboot。當然,我們也可以選擇使用SPL和不使用。
在編譯的過程中,是先編譯第二階段Uboot,然後在編譯SPL的。這兩個階段的編譯時分離的。
擁有不同的配置,所以許多地方的宏是和SPL的不一樣。而且鏈接的文件也不一致。
所以接下來,我們也會分爲兩個部分進行分析。
  • 1
  • 2
  • 3
  • 4
  • 5

SPL啓動

在AM437X平臺中,實際的SPL啓動就是MLO啓動階段。其運行空間爲片內DRAM,所以對其大小有較爲嚴格的限制。
通常使用SPL初始化基本設備後,將第二階段UBoot加載進行SDRAM中執行。
  • 1
  • 2
  • 3

第一階段:和架構相關的初始化

其源碼位於arch/arm/cpu/armv7/start.s中。
其中關鍵的調用關係如下:
調用:cpu_init_crit
        b   lowlevel_init
            ==>arch/arm/cpu/armv7/lowlevel_init.S
                b s_init 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

第二階段:和SoC底層相關的初始化

在第一階段初始化完成後,便調用了s_init函數,其實現的源碼是arch/arm/mach-omap2/an33xx/Borad.c。 
該函數就調用了一個函數rtc_only();然後便返回到lowlevel_init函數中。 
此時,lowlevel_init也已經完成他的任務,就返回到cpu_init_crit中。 
然而,cpu_init_crit這個函數的工作也全都幹完了,便回到start.S中,然後便是 
b _main

而這個_main函數位於: 
arch/arm/lib/crt0.S中 
在_main函數中,就調用了一個跨時代的函數。 
board_init_f (arch/arm/mach-omap2/am33xx/Borad.c)

第三階段:板級初始化

在上面,我們可以知道 _main函數會調用board_init_f。實際上,這個函數便是和板級相關連的函數了。 
然而,在SPL啓動中,他指向了下面的函數:

void board_init_f(ulong dummy)
{
    early_system_init();     -->系統時鐘初始化()    ------------------------  
    board_early_init_f();    -->主頻配置()             ==》 prcm_init()             ==>void prcm_init(void)
          {
	scale_vcores();查看相應主頻配置電壓
	setup_dplls();配置dpll            ==》get_dpll_mpu_params ==》{    const struct dpll_params *get_dpll_mpu_params(void)
{
	int opp = get_opp_offset(DEV_ATTR_MAX_OFFSET, DEV_ATTR_MIN_OFFSET);
	u32 ind = get_sys_clk_index();


	return &dpll_mpu[ind][opp];
}
}
          }                             |
    sdram_init();  ---->初始化SDRAM
    /* dram_init must store complete ramsize in gd->ram_size */         |
    gd->ram_size = get_ram_size(
            (void *)CONFIG_SYS_SDRAM_BASE,                              |
            CONFIG_MAX_RAM_BANK_SIZE);                                  |
}------------------------------------------------------------------------|                                                                       \|/
void early_system_init(void)
{
	/*
	 * The ROM will only have set up sufficient pinmux to allow for the
	 * first 4KiB NOR to be read, we must finish doing what we know of
	 * the NOR mux in this space in order to continue.
	 */
#ifdef CONFIG_NOR_BOOT
	enable_norboot_pin_mux();
#endif
	watchdog_disable();
	set_uart_mux_conf();
	setup_early_clocks();                    -------->初始化時鐘                                   ==》{    在u-boot-2017.01\arch\arm\mach-omap2\am33xx\clock.c                                        void setup_early_clocks(void)
                                        {
	                                setup_clocks_for_console();
	                                enable_basic_clocks();------》使能相應時鐘                                             {u-boot-2017.01\arch\arm\mach-omap2\am33xx\clock_am437x.c                                            void enable_basic_clocks(void)
                                            {
                                            	u32 *const clk_domains[] = {
                                            		&cmper->l3clkstctrl,
                                            		&cmper->l3sclkstctrl,
                                            		&cmper->l4lsclkstctrl,
                                            		&cmwkup->wkclkstctrl,
                                            		&cmper->emifclkstctrl,
                                            		0
                                            	};


                                            	u32 *const clk_modules_explicit_en[] = {
                                            		&cmper->l3clkctrl,
                                            		&cmper->l4lsclkctrl,
                                            		&cmper->l4fwclkctrl,
                                            		&cmwkup->wkl4wkclkctrl,
                                            		&cmper->l3instrclkctrl,
                                            		&cmper->l4hsclkctrl,
                                            		&cmwkup->wkgpio0clkctrl,
                                            		&cmwkup->wkctrlclkctrl,
                                            		&cmper->timer2clkctrl,
                                            		&cmper->gpmcclkctrl,
                                            		&cmper->elmclkctrl,
                                            		&cmper->mmc0clkctrl,
                                            		&cmper->mmc1clkctrl,
                                            		&cmwkup->wkup_i2c0ctrl,
                                            		&cmper->gpio1clkctrl,
                                            		&cmper->gpio2clkctrl,
                                            		&cmper->gpio3clkctrl,
                                            		&cmper->gpio4clkctrl,
                                            		&cmper->gpio5clkctrl,
                                            		&cmper->i2c1clkctrl,
                                            		&cmper->cpgmac0clkctrl,
                                            		&cmper->emiffwclkctrl,
                                            		&cmper->emifclkctrl,
                                            		&cmper->otfaemifclkctrl,
                                            		&cmper->qspiclkctrl,
                                            		&cmper->spi0clkctrl,
                                            		0
                                            	};


                                            	do_enable_clocks(clk_domains, clk_modules_explicit_en, 1);


                                            	/* Select the Master osc clk as Timer2 clock source */
                                            	writel(0x1, &cmdpll->clktimer2clk);


                                            	/* For OPP100 the mac clock should be /5. */
                                            	writel(0x4, &cmdpll->clkselmacclk);
                                            }


                                            void rtc_only_enable_basic_clocks(void)
                                            {
                                            	u32 *const clk_domains[] = {
                                            		&cmper->emifclkstctrl,
                                            		0
                                            	};


                                            	u32 *const clk_modules_explicit_en[] = {
                                            		&cmper->gpio5clkctrl,
                                            		&cmper->emiffwclkctrl,
                                            		&cmper->emifclkctrl,
                                            		&cmper->otfaemifclkctrl,
                                            		0
                                            	};


                                            	do_enable_clocks(clk_domains, clk_modules_explicit_en, 1);


                                            	/* Select the Master osc clk as Timer2 clock source */
                                            	writel(0x1, &cmdpll->clktimer2clk);
                                            }
                                                                }
	                                 timer_init();  
                                        }
	uart_soft_reset();
#ifdef CONFIG_SPL_BUILD
	/*
	 * Save the boot parameters passed from romcode.
	 * We cannot delay the saving further than this,
	 * to prevent overwrites.
	 */
	save_omap_boot_params();
#endif
#ifdef CONFIG_TI_I2C_BOARD_DETECT
	do_board_detect();
#endif
#if defined(CONFIG_SPL_AM33XX_ENABLE_RTC32K_OSC)
	/* Enable RTC32K clock */
	rtc32k_enable();
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

其中的sdram_ini(); 便是初始化SDRAM的關鍵代碼,所以,如果我們有自己的板子,我們也可以實現sdram_init函數來完成我們板子的SDRAM的初始化。

其調用關係如下: 
board_init_f 
==>early_system_init() 
==>set_uart_mux_conf(); // 設置相應的PIN爲串口 
==>board_early_init_f(); // 不同的板子可以實現這個函數 
==> 
==>sdram_init(); // 初始化SDRAM

執行完borad_init_f後,回到_main 
這個時候,會去執行spl_relocate_stack_gd 
重新定位在棧中的global_data。

如果定義了CONFIG_SPL_STACK_R,那麼就將之前在片內內存中的global_data對象重定位到SDRAM中,並且將棧指針指向SDRAM中的某一地址 
最後,就會調用borad_init_r函數:

borad_init_r(common/spl/spl.c)
    ==>spl_board_init();    // 初始化串口
    ==> if (boot_from_devices(&spl_image, spl_boot_list,
                  ARRAY_SIZE(spl_boot_list))) {
                puts("SPL: failed to boot from all boot devices\n");
                hang();
            }
            =>遍歷loader,看哪個loader和設備匹配。
            ==> spl_load_image
                ==>spl_mmc_load_image
                    ==>spl_mmc_do_fs_boot
                        ==>spl_load_image_fat(uboot.img)    // 將uboot.img加載到內存,並進行解析
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

將鏡像加載到內存的時候,此時SPL進行判斷,加載的鏡像是uboot還是kernel然後便跳轉到鏡像的入口中進行執行。此時,SPL的使命便完成了 
==>jump_to_image_no_args(&spl_image);

第二階段Uboot

此階段的UBoot是在內存中的了,內存已經初始化完畢,並且許多的引腳複用在上一個階段就已經複用完畢了。

在編譯的過程中,是先編譯第二階段Uboot,然後在編譯SPL的。這兩個階段的編譯時分離的。擁有不同的配置,所以許多地方的宏是和SPL的不一樣。而且
鏈接的文件也不一致。

通過驗證,就board_init_r函數而言,在SPL中是鏈接向spl.c中的board_init_r函數,而在Uboot中,是鏈接向board_r.c中的borad_init_r函數。

那麼,我們就從borad_init_r函數入手,看看第二階段的Uboot是怎麼啓動的。

在分析的過程中,
我們直接在board_init_r函數實現成一個死循環,但是發現還是有信息能夠輸出,說明在board_init_r還進行了初始化的工作。

    U-Boot 2017.01-g03781bc-dirty (Jul 14 2017 - 13:55:58 +0800)

    CPU  : AM437X-GP rev 1.2
    Model: TI AM437x GP EVM
    I2C:   ready
    DRAM:  1 GiB

我們發現,其實在board_init_f中都已經鏈接在別的文件中了,他是位於board_f.c文件中
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
    void board_init_f(ulong boot_flags)
    {
        #ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA
            /*
             * For some architectures, global data is initialized and used before
             * calling this function. The data should be preserved. For others,
             * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
             * here to host global data until relocation.
             */
            gd_t data;

            gd = &data;

            /*
             * Clear global data before it is accessed at debug print
             * in initcall_run_list. Otherwise the debug print probably
             * get the wrong value of gd->have_console.
             */
            zero_global_data();
        #endif

            gd->flags = boot_flags;
            gd->have_console = 0;

            if (initcall_run_list(init_sequence_f))
                hang();

        #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
                !defined(CONFIG_EFI_APP)
            /* NOTREACHED - jump_to_copy() does not return */
            hang();
        #endif
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
其中關鍵的函數就是 initcall_run_list(init_sequence_f);
我們來看看init_sequence_f是個什麼東西。
  • 1
  • 2
  • 3
static init_fnc_t init_sequence_f[] = {
            #ifdef CONFIG_SANDBOX
                setup_ram_buf,
            #endif
                setup_mon_len,
            #ifdef CONFIG_OF_CONTROL
                fdtdec_setup,   // 初始化FDT
            #endif
            #ifdef CONFIG_TRACE
                trace_early_init,
            #endif
                initf_malloc,
                initf_console_record,
            #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx)
                /* TODO: can this go into arch_cpu_init()? */
                probecpu,
            #endif
            #if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP)
                x86_fsp_init,
            #endif
                arch_cpu_init,      /* basic arch cpu dependent setup */
                mach_cpu_init,      /* SoC/machine dependent CPU setup */
                initf_dm,
                arch_cpu_init_dm,
                mark_bootstage,     /* need timer, go after init dm */
            #if defined(CONFIG_BOARD_EARLY_INIT_F)
                board_early_init_f,
            #endif
                /* TODO: can any of this go into arch_cpu_init()? */
            #if defined(CONFIG_PPC) && !defined(CONFIG_8xx_CPUCLK_DEFAULT)
                get_clocks,     /* get CPU and bus clocks (etc.) */
            #if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \
                    && !defined(CONFIG_TQM885D)
                adjust_sdram_tbs_8xx,
            #endif
                /* TODO: can we rename this to timer_init()? */
                init_timebase,
            #endif
            #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
                    defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
                    defined(CONFIG_SH) || defined(CONFIG_SPARC)
                timer_init,     /* initialize timer */
            #endif
            #ifdef CONFIG_SYS_ALLOC_DPRAM
            #if !defined(CONFIG_CPM2)
                dpram_init,
            #endif
            #endif
            #if defined(CONFIG_BOARD_POSTCLK_INIT)
                board_postclk_init,
            #endif
            #if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
                get_clocks,
            #endif
                env_init,       /* initialize environment */
            #if defined(CONFIG_8xx_CPUCLK_DEFAULT)
                /* get CPU and bus clocks according to the environment variable */
                get_clocks_866,
                /* adjust sdram refresh rate according to the new clock */
                sdram_adjust_866,
                init_timebase,
            #endif
                init_baud_rate,     /* initialze baudrate settings */
                serial_init,        /* serial communications setup */
                console_init_f,     /* stage 1 init of console */
            #ifdef CONFIG_SANDBOX
                sandbox_early_getopt_check,
            #endif
                display_options,    /* say that we are here */
                display_text_info,  /* show debugging info if required */
            #if defined(CONFIG_MPC8260)
                prt_8260_rsr,
                prt_8260_clks,
            #endif /* CONFIG_MPC8260 */
            #if defined(CONFIG_MPC83xx)
                prt_83xx_rsr,
            #endif
            #if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_SH)
                checkcpu,
            #endif
                print_cpuinfo,      /* display cpu info (and speed) */
            #if defined(CONFIG_MPC5xxx)
                prt_mpc5xxx_clks,
            #endif /* CONFIG_MPC5xxx */
            #if defined(CONFIG_DTB_RESELECT)
                embedded_dtb_select,
            #endif
            #if defined(CONFIG_DISPLAY_BOARDINFO)
                show_board_info,
            #endif
                INIT_FUNC_WATCHDOG_INIT
            #if defined(CONFIG_MISC_INIT_F)
                misc_init_f,
            #endif
                INIT_FUNC_WATCHDOG_RESET
            #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
                init_func_i2c,
            #endif
            #if defined(CONFIG_HARD_SPI)
                init_func_spi,
            #endif
                announce_dram_init,
                /* TODO: unify all these dram functions? */
            #if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
                    defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || \
                    defined(CONFIG_SH)
                dram_init,      /* configure available RAM banks */ // 設置一些gd相關的參數
            #endif
            #if defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_M68K)
                init_func_ram,
            #endif
            #ifdef CONFIG_POST
                post_init_f,
            #endif
                INIT_FUNC_WATCHDOG_RESET
            #if defined(CONFIG_SYS_DRAM_TEST)
                testdram,
            #endif /* CONFIG_SYS_DRAM_TEST */
                INIT_FUNC_WATCHDOG_RESET

            #ifdef CONFIG_POST
                init_post,
            #endif
                INIT_FUNC_WATCHDOG_RESET
                /*
                 * Now that we have DRAM mapped and working, we can
                 * relocate the code and continue running from DRAM.
                 *
                 * Reserve memory at end of RAM for (top down in that order):
                 *  - area that won't get touched by U-Boot and Linux (optional)
                 *  - kernel log buffer
                 *  - protected RAM
                 *  - LCD framebuffer
                 *  - monitor code
                 *  - board info struct
                 */
                setup_dest_addr,
            #if defined(CONFIG_BLACKFIN) || defined(CONFIG_XTENSA)
                /* Blackfin u-boot monitor should be on top of the ram */
                reserve_uboot,
            #endif
            #if defined(CONFIG_SPARC)
                reserve_prom,
            #endif
            #if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR)
                reserve_logbuffer,
            #endif
            #ifdef CONFIG_PRAM
                reserve_pram,
            #endif
                reserve_round_4k,
            #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
                    defined(CONFIG_ARM)
                reserve_mmu,
            #endif
            #ifdef CONFIG_DM_VIDEO
                reserve_video,
            #else
            # ifdef CONFIG_LCD
                reserve_lcd,
            # endif
                /* TODO: Why the dependency on CONFIG_8xx? */
            # if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
                    !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
                    !defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
                reserve_legacy_video,
            # endif
            #endif /* CONFIG_DM_VIDEO */
                reserve_trace,
            #if !defined(CONFIG_BLACKFIN) && !defined(CONFIG_XTENSA)
                reserve_uboot,
            #endif
            #ifndef CONFIG_SPL_BUILD
                reserve_malloc,
                reserve_board,
            #endif
                setup_machine,
                reserve_global_data,
                reserve_fdt,
                reserve_arch,
                reserve_stacks,
                setup_dram_config,
                show_dram_config,
            #if defined(CONFIG_M68K) || defined(CONFIG_MIPS) || defined(CONFIG_PPC) || \
                defined(CONFIG_SH)
                setup_board_part1,
            #endif
            #if defined(CONFIG_PPC) || defined(CONFIG_M68K)
                INIT_FUNC_WATCHDOG_RESET
                setup_board_part2,
            #endif
                display_new_sp,
            #ifdef CONFIG_SYS_EXTBDINFO
                setup_board_extra,
            #endif
                INIT_FUNC_WATCHDOG_RESET
                reloc_fdt,
                setup_reloc,
            #if defined(CONFIG_X86) || defined(CONFIG_ARC)
                copy_uboot_to_ram,
                clear_bss,
                do_elf_reloc_fixups,
            #endif
            #if defined(CONFIG_XTENSA)
                clear_bss,
            #endif
            #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)
                jump_to_copy,
            #endif
                NULL,
        };
發佈了24 篇原創文章 · 獲贊 8 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章