uboot代码解析

#uboot分析

一、uboot编译过程生产的一些文件

1. u-boot-2011.12/u-boot.lds

此文件(我猜测)应该是由makefile创建,参考u-boot-2011.12\board\Realtek\rtl838x\u-boot.PUB.lds,作用是在GNU LD链接器合并由GNU GCC build出来的*.o文件传给GNU LD程序,告诉GNU LD程序如何组织输出的”elf32-tradbigmips”文件格式内部的各个段的顺序和地址,具体请参考附录链接1。

1.1 参考rtk datasheet可得知RTL8382M物理地址的存储器分布图,如下

存储器分布图

1.2 u-boot.lds分段包括:

.ucflash : 0xbfc0,0000 => 0x1fc0,0000 ;  sizeof = 0x4d0
.sram:0x9f0004d0 => 0x1f0004d0 ; sizeof = 0x795c
.sram.bss:0x9f000795c => 0x1f000795c ; sizeof = 0x14
.dram:0x83f00000 => 0x13f00000 ; sizeof = 0x42864
.sbss:不关心
.bss:不关心

2. u-boot-2011.12/u-boot.map

编译过程中elf32-tradbigmips文件格式内部结构分布图

3. u-boot-2011.12/system.map

编译过程中最终bin档内容,纯可执行内容

4. u-boot-2011.12/u-boot

编译过程中elf32-tradbigmips文件格式

5. u-boot-2011.12/u-boot.bin

二进制文件,由u-boot生成,通过GNU OBJCOPY程序,去掉了头部结构字段和其他非必要内容,可以直接烧录到flash执行

6. u-boot-2011.12/u-boot.code

由汇编器生成的汇编程序源码

附录:

二、.ucflash段执行过程

ucflash段所有函数

bfc00000 t _start
bfc00400 t reset
bfc00418 t romReserved
bfc00420 t romExcHandle
bfc00424 t intHandle
bfc0042c t ramExcHandle
bfc00440 t soc_init
bfc00440 t sram_init

2. 代码分析

2.1 入口点函数

根据u-boot-2011.12/u-boot.lds,入口点函数为bfc00000 T _start,在flash就代表0x0,由于cpu上电复位地址根据mips架构规定为0xbfc00000,而且RTL8382M也刚好将flash定位于0xbfc00000,因此,上电复位后就开始执行_start函数。

…………
 arch/mips/cpu/mips4kec/start.o(.text*)
 .text          0x00000000bfc00000      0x440 arch/mips/cpu/mips4kec/start.o
				0x00000000bfc00000                _start
 *(preloader.uc)
 preloader.uc   0x00000000bfc00440       0x90 arch/mips/cpu/mips4kec/rtl838x/librtl838x.o
				0x00000000bfc00440                soc_init
…………

由于mips规定0xbfc00000 - 0xbfc00400为异常向量入口,所以一下代码的作用是设定异常向量表,且将向量表的第一项0xbfc00000设定为reset函数。

#define RVECENT(f,n) \
   b f; nop
#define XVECENT(f,bev) \
   b f     ;           \
   li k0,bev

_start:
		RVECENT(reset,0)        /* U-boot entry point */
		RVECENT(reset,1)        /* software reboot */
		RVECENT(romReserved,2)
--------------------------------------------省略--------------------------------------------
		RVECENT(romReserved,125)
		RVECENT(romReserved,126)
		RVECENT(romReserved,127)
		/* We hope there are no more reserved vectors!
		 * 128 * 8 == 1024 == 0x400
		 * so this is address R_VEC+0x400 == 0xbfc00400
		 */

此段为设置异常向量,由于mips指令长度固定为32位,因此每条指令长度为4byte,两条为8byte

所以REVCEBT和XVECEBT函数每调用一次就在_start函数开头添加了8个byte长的两条指令,第一条为一条跳转指令,跳转到f指定的地址,第二条为nop

因此相当于在地址0xbfc00000处存储了128条跳转指令表,只要程序指针指向其中的某个地址,就会执行相应的函数。

问题: _start函数地址为0xbfc00000,加上向量表,reset地址为0xbfc00400

加上reset函数4条指令和reset后面的中断函数11条指令共15条,15*4 = 0x3c

而soc_init地址为0xbfc00440,还有4个byte去哪了?

2.2 reset函数

	.align 4
reset:
	li	sp, CONFIG_SYS_TEXT_BASE - 8
	la	t9, soc_init
	j		t9
	nop

此函数地址对应为0xbfc00400,执行地址还在flash里面。

由于下面有函数要调用,所以要设定栈空间地址。

CONFIG_SYS_TEXT_BASE=0x83F00000,代表uboot在DRAM里面的代码地址,u-boot.lds里将此地址作为.dram:0x83f00000 => 0x13f00000 ; sizeof = 0x42864,地址向上增长,而此地址CONFIG_SYS_TEXT_BASE-8指定为. ucflash函数的栈顶,栈向下增长,所以各不影响。

sp = $29 = $sp = 堆栈指针

sp栈地址为0x83F00000 - 8 = 0x83EFFFF8

t9寄存器对应$25,为临时寄存器,子程序使用时可以不必存储和恢复

reset函数调用soc_init函数,栈地址为0x83EFFFF8

2.3 soc_init函数

lplr_sram.H里面的sram_init函数的作用是设定:

    0xB8001300 Unmapped Memory Segment Address Register 0 (UMSAR0) (0xB800_1300) 
    0xB8001304 Unmapped Memory Segment Size Register 0 (UMSSR0) (0xB800_1304) 
    0xB8004000 SRAM Segment Address  Register (SRAMSAR) (0xB800_4000) 

preloader/platform/current/lplr_sram.H :

sram_init:
  la      t0, 0xB8001300
  li      t1, 0x1F000001
  sw      t1, 0(t0)
  li      t2, 0x0000000A
  sw      t2, 4(t0)
  la      t0, 0xB8004000
  sw      t1, 0(t0) //SRAMSAR0 for base address SRAM maps to
soc_init:
#include "preloader/platform/current/lplr_sram.H"
/* memcpy(SRAM_BASE, preloader_uclma, preloader_sz) */
  la      t0, preloader_sram_start /* t0 = preloader_sram_start = 0x9fc00000 */
  li      t1, 0x20000000 /* t1 = 0x20000000 */
  or      t0, t0, t1 /* t0 = t0 | t1 = 0x9fc00000 | 0x20000000 = 0xbfc00000 */
  la      t1, UCSRAM_BASE /* t1 = 0xbf000000 */
  la      t2, preloader_sram_size /* t2 = 0x795c */
  addu    t2, t0, t2 /* t2 = t0 + t2 = 0xbfc00000 + 0x795c = 0xbfc0795c */
1:
  lw      t3, 0(t0) /* t3 = *(t0) */
  addi    t0, t0, 4 /* t0 = t0 += 4 = 0xbfc00000 += 4 => 0xbfc00000 + 4*i */
  sw      t3, 0(t1) /* *(t1) = *(0xbf000000) = t3  */
  bne     t0, t2, 1b /* t0 > t2 => 0xbfc00000 + 4*i > 0xbfc0795c 则跳 */
  addi    t1, t1, 4 /* 分支延迟槽,跳转前继续执行完这句 */
  nop
  REBASED_CALL(cpu_init, UCSRAM_BASE) /* 跳转到UCSRAM_BASE */
  la      t0, soc_init_in_cache /*  */
  jr      t0
  nop
  .end    soc_init
#define REBASED_CALL(addr, base) \ 
	la   s0, addr;         \ /* s0 = addr ext:0xbfc00440 */
	li   s1, SRAM_SZ_MASK; \ /* s1 = 0x7fff 这是SRAM地址掩码,共16位 */
	and  s0, s0, s1;       \ /* s0 = s0 & s1, 超过16位也就是128kbyte的地址,则置0,只取偏移 */
	li   s1, base;         \ /* s1 = base = 0xbf000000 */
	or   s0, s0, s1;       \ /* s0 = s0 | s1 = 0x00000440 | 0xbf000000 = 0xbf000440 */
	jalr s0;               \
	nop

soc_init函数做了4件事情:

  • 第一,sram_init设定RTL8382M的SRAM相关的寄存器,配置系统片上SRAM的地址和大小
  • 第二,copy从0xbfc00000处开始到0xbfc0795c的大小为0x795c的flash上的code到0xbfc00000,也就是SRAM开始处
  • 第三,将程序指针从0xbfc0xxxx(flash)移动到0xbf00xxxx(sram),然后执行sram上面的cpu_init
  • 第四,由于执行cpu_init跳转代码之前,会保存返回地址,此返回地址为s0,(/* s0 = s0 s1 = 0x00000440 0xbf000000 = 0xbf000440 */),所以cpu_init执行完后的返回地址是0xbf00xxxx,也就是sram的地址,所以继续在sram上面执行soc_init_in_cache

三、.sram段执行过程

3.1 cpu_init函数

#include <preloader.h>
#include <mips-generic.h>
GFUNC(cpu_init)
/* Backup return address */
  move    s6, ra
/* Clear watch registers */
  mtc0    zero, CP0_WATCHLO
  mtc0    zero, CP0_WATCHHI
/* STATUS register */
  mfc0    k0, CP0_STATUS
  li      k1, ~CP0_STATUS_IE
  and     k0, k1
  mtc0    k0, CP0_STATUS
/* (Watch Pending), SW0/1 should be cleared */
  mtc0    zero, CP0_CAUSE
/* Timer */
  mtc0    zero, CP0_COUNT
  mtc0    zero, CP0_COMPARE
#if (CONFIG_STANDALONE_UBOOT == 1)
  REBASED_CALL(mips_cache_reset, UCSRAM_BASE)
#else
  REBASED_CALL(mips_cache_reset, UCFLASH_BASE)
#endif
/* Enable cache. However, one should NOT access cached SRAM and cached DRAM until they are initialized. */
  mfc0    t0, CP0_CONFIG
  li      t1, ~CP0_CONF_CACHE_MASK
  and     t0, t0, t1
  li      t1, CP0_CONF_CACHABLE_NC_WB_WA
  or      t0, t0, t1
  mtc0    t0, CP0_CONFIG
  jr      s6
END(cpu_init)

这段代码在sram上执行,包括清看门狗寄存器,状态寄存器,cause寄存器,定时器寄存器,初始化缓存存储器,启用缓存,具体细节参考mips体系结构上的CP0寄存器说明。

3.2 soc_init_in_cache函数

GFUNC(soc_init_in_cache)
  la      sp, c_sp_pos /* sp = c_sp_pos = STACK1_BASE */
  lw      sp, 0(sp)
  la      t9, c_start
  jal     t9
  nop
  li      sp, CONFIG_SYS_TEXT_BASE - 8
  la      t9, board_init_f
  j       t9
  nop
END(soc_init_in_cache)

这段代码同样在sram上执行,包括设定c_start的栈顶指针和c_start,board_init_f的栈顶指针和board_init_f。

注意:board_init_f并不是在sram上执行,具体原因见后面章节。

STACK1_BASE = (SRAM_BASE + SRAM_SIZE - CACHE_ALIGN(sizeof(parameter_to_bootloader_t)) - 8) = 0x9F000000 + 0x10000 - CACHE_ALIGN(sizeof(parameter_to_bootloader_t)) - 8

STACK1_BASE具体数值我没有计算过,但是通过此可以看出,是在sram最高地址去掉某个结构体的空间的地址为栈顶,sram内存布局如下:

sram内存布局

3.3 c_start函数

In SoC parts, the c_start() is used to initialize the peripherals without CPU and flash driver, such as UART, PLL and DDR2/3 initialization and DDR2/3 calibration algorithm. The c_start() flow chart is shown in Figure 3.3-1.

c_start

此函数继续在sram里面执行。

3.3.1 pblr_bzero函数(内联函数)

.sram.bss       0x000000009f00795c       0x18 load address 0x0000000000000000
 arch/mips/cpu/mips4kec/rtl838x/librtl838x.o(.sbss*)
				0x000000009f00795c                . = ALIGN (0x4)
 arch/mips/cpu/mips4kec/rtl838x/librtl838x.o(.bss*)
 .bss.s1.0      0x000000009f00795c        0x8 arch/mips/cpu/mips4kec/rtl838x/librtl838x.o
 .bss.s2.1      0x000000009f007964        0x8 arch/mips/cpu/mips4kec/rtl838x/librtl838x.o
 .bss.s3.2      0x000000009f00796c        0x8 arch/mips/cpu/mips4kec/rtl838x/librtl838x.o
				0x000000009f00795c                preloader_bss_start = ADDR (.sram.bss)
				0x0000000000000018                preloader_bss_size = SIZEOF (.sram.bss)

等同于清零sram中0x9f00795c起,大小为0x18的空间,示意图如下:

sram

3.3.2 parameters_setup函数

Boot Loader U-Boot-2011.12 User Guide:This API is used to get the SoC related parameters and fill them into the global data structure which will be used by later APIs.

static inline void parameters_setup(void) {
	memcpy((void *)(&parameters),
		   (void *)(&soc_in_flash),
		   sizeof(soc_t));
	parameters.soc_id  = *((volatile u32_t *)0xb80010fc) >> 12;
	parameters.bond_id = 0xDEADDEAD;
	parameters._nor_spi_erase = (void *)0xDEADDEAD;
	parameters._nor_spi_read  = (void *)0xDEADDEAD;
	parameters._nor_spi_write = (void *)0xDEADDEAD;
	parameters.dram_init_result  = INI_RES_UNINIT;
	parameters.flash_init_result = INI_RES_UNINIT;
	parameters._pblr_printf = (void *)0xDEADDEAD;
	parameters._udelay = (void *)0xDEADDEAD;
	parameters._uart_init = (void *)0xDEADDEAD;
	parameters._uart_putc = (void *)0xDEADDEAD;
	parameters._uart_getc = (void *)0xDEADDEAD;
	parameters._uart_tstc = (void *)0xDEADDEAD;
	parameters._uart_ctrl = (void *)0xDEADDEAD;
	parameters.dram_test=&soc_ramtest_init;
	parameters._uart_ctrl = (void *)0xDEADDEAD;
	parameters._dcache_writeback_invalidate_all = &writeback_invalidate_dcache_all;
	parameters._icache_invalidate_all           = &invalidate_icache_all;
	return;
}

此处填充的结构体是parameter_to_bootloader_t,由于此函数是在c_start函数内执行,属于c_start函数的下一级函数,而c_start函数的栈顶指针为STACK1_BASE = (SRAM_BASE + SRAM_SIZE - CACHE_ALIGN(sizeof(parameter_to_bootloader_t)) - 8) = 0x9F000000 + 0x10000 - CACHE_ALIGN(sizeof(parameter_to_bootloader_t)) - 8,参考SRAM内存布局图,在SRAM最高地址的一段内存就是全局结构体parameter_to_bootloader_t的地址,因此,此函数填充的内容都保存在sram的这段地址上。

保存的内存是当前初始化和未初始化的内部和外设的状态标志和callback,具体有复制soc_in_flash结构内容到parameters.soc,soc id 读取8382M寄存器填充,spi flash驱动未初始化,当前dram和flash初始化结果为未初始化,uart未初始化,dram test callback函数,由于之前cpu_init已经初始化并启用cache,此处填充cache的callback函数。

3.3.3 console_init函数

console_init() : This API will initialize the UART peripheral and hook console driver’s APIs.

void console_init(void) {
	if (parameters.soc.peri_info.baudrate_divisor == 0x0) { /* parameters_setup的时候,有copy过soc_in_flash,此结构设定此字段为0,参考soc_init_parameters.c line.87 */
		//parameters._uart_init = dummyv_i;
		parameters._uart_init = ((fpv_u32_u32_t*)assign_uart_fp);
		parameters._uart_putc = dummyv_c;
		parameters._uart_getc = dummyc_u;
		parameters._uart_tstc = dummyi_u;
		parameters._uart_ctrl = dummyi_ui;
	} else {
		otto_NS16550_init(CONSOLE_CH_INDEX,parameters.soc.peri_info.baudrate_divisor);
		assign_uart_fp();
	}
	return;
}

问题: 此函数只是hook uart的驱动到parameters,为什么接着直接就可以使用printf打印字符串到串口了?

3.3.4 pll_set_from_mhz函数

......
#elif (OTTO_PLL_GEN == 3)
	#include <plr_pll_gen3.h>
	#define pll_set_from_mhz pll_gen3_set_from_mhz
	typedef pll_gen3_mhz_t   pll_mhz_t;
#else
......

pll_gen3_mhz_t pll_mhz = {
	.mode = PLL_MODE_BY_SW,
	.cpu = 500,
	.lx  = 200,
	.mem = 300,
};

#if (defined(PLR_ENABLE_PLL_SET) ||	\
	 defined(CONFIG_STANDALONE_UBOOT))
SECTION_ON_FLASH pll_result_t
pll_gen3_set_from_mhz(pll_info_t *pll_reg,
					  pll_gen3_mhz_t *pll_mhz) {
	pll_result_t res;
	if (pll_reg == NULL) {
		pll_info_t tmp;
		res = _pll_gen3_set_from_mhz(&tmp, pll_mhz);
		otto_pll_gen3_set_to_reg(&tmp, (OTTO_PLL_CPU_SET|OTTO_PLL_MEM_SET));
	} else {
		res = _pll_gen3_set_from_mhz(pll_reg, pll_mhz);
	}
	return res;
}
#endif //if (defined(PLR_ENABLE_PLL_SET)||defined(CONFIG_STANDALONE_UBOOT)||defined(__OTTO_COMPOSER__))

res = pll_set_from_mhz((pll_info_t *)&parameters.soc.pll_info,
						   &pll_mhz);

SECTION_ON_FLASH pll_result_t
_pll_gen3_set_from_mhz(pll_info_t *pll_reg,
					   const pll_gen3_mhz_t *pll_mhz) {
	u32_t cmu_fcode_in, cmu_divn2,cmu_ncode_in,cmu_bypass_pi,cmu_sel_div4,cmu_sel_prediv;
	u32_t cmu_divn3_sel,cmu_divn2_selb,cmu_en_ssc,cmu_step_in,cmu_tbase_in;
	u32_t cmu_ssc_order,cmu_time2_rst_width, cmu_time0_ck, cmu_clkrdy,cmu_big_kvco, cmu_lpf_rs,
		cmu_en_center_in, cmu_en_wd, cmu_pi_i_sel, cmu_sel_cp_i, cmu_sel_cco, cmu_ldo_sel,
		cmu_lpf_cp, cmu_cp_new_en, cmu_ldo_en, cmu_vc_dly, cmu_en_ckoobs, test_en;
	pll_result_t res = PLL_RES_OK;
	if (pll_mhz->mode == PLL_MODE_BY_SW) {
		pll_reg->set_by = 1;
	} else if (pll_mhz->mode == PLL_MODE_BY_PIN) {
		pll_reg->set_by = 0;
	} else {
		res = PLL_RES_BAD_MODE;
		return res;
	}
	//Calculate OCP PLL Register -------------------------------
	pll_reg->pll_cpu_ctl0 = pll_reg->pll_cpu_ctl1 = pll_reg->pll_cpu_misc_ctrl = 0;
	cmu_bypass_pi  = 0x1;
	......
	test_en 	   = 0x0;
	switch (pll_mhz->cpu){
	case 500:
		cmu_sel_prediv = 0;
		......
		cmu_big_kvco   = 0x1;
		break;
	default:
		res = PLL_RES_FREQ_OUT_OF_RANGE;
	case 300:
		cmu_sel_prediv = 0;
		......
		cmu_big_kvco   = 0x0;
		break;
	}
	pll_reg->pll_cpu_ctl0 = (cmu_fcode_in<<20)|(cmu_divn2<<12)|(cmu_ncode_in<<4)|(cmu_bypass_pi<<3)|(cmu_sel_div4<<2)|(cmu_sel_prediv<<0);
	pll_reg->pll_cpu_ctl1 = (cmu_divn3_sel<<27)|(cmu_divn2_selb<<26)|(cmu_en_ssc<<25)|(cmu_step_in<<12)|(cmu_tbase_in<<0);
	pll_reg->pll_cpu_misc_ctrl = (cmu_ssc_order<<31)|(cmu_time2_rst_width<<29)|(cmu_time0_ck<<26)|(cmu_clkrdy<<24)|(cmu_big_kvco<<23)| \
		(cmu_lpf_rs<<20)|(cmu_en_center_in<<19)|(cmu_en_wd<<18)|(cmu_pi_i_sel<<14)|(cmu_sel_cp_i<<10)|(cmu_sel_cco<<9)|(cmu_ldo_sel<<6)| \
		(cmu_lpf_cp<<5)|(cmu_cp_new_en<<4)|(cmu_ldo_en<<3)|(cmu_vc_dly<<2)|(cmu_en_ckoobs<<1)|(test_en<<0);
	//Calculate MEM PLL Register -------------------------------
	pll_reg->pll_mem_ctl0 = pll_reg->pll_mem_ctl1 = pll_reg->pll_mem_misc_ctrl = 0;
	cmu_bypass_pi  = 0x1;
	......
	test_en 	   = 0x0;
	switch (pll_mhz->mem) {
	case 300:
		cmu_sel_prediv = 0;
		......
		cmu_big_kvco   = 0x0;
		break;
	default:
		res = PLL_RES_FREQ_OUT_OF_RANGE;
	case 192:
	case 193:
	case 194:
		cmu_sel_prediv = 0;
		......
		cmu_big_kvco   = 0x1;
		break;
	}
	pll_reg->pll_mem_ctl0 = (cmu_fcode_in<<20)|(cmu_divn2<<12)|(cmu_ncode_in<<4)|(cmu_bypass_pi<<3)|(cmu_sel_div4<<2)|(cmu_sel_prediv<<0);
	pll_reg->pll_mem_ctl1 = (cmu_divn3_sel<<27)|(cmu_divn2_selb<<26)|(cmu_en_ssc<<25)|(cmu_step_in<<12)|(cmu_tbase_in<<0);
	pll_reg->pll_mem_misc_ctrl = (cmu_ssc_order<<31)|(cmu_time2_rst_width<<29)|(cmu_time0_ck<<26)|(cmu_clkrdy<<24)|(cmu_big_kvco<<23)| \
		(cmu_lpf_rs<<20)|(cmu_en_center_in<<19)|(cmu_en_wd<<18)|(cmu_pi_i_sel<<14)|(cmu_sel_cp_i<<10)|(cmu_sel_cco<<9)|(cmu_ldo_sel<<6)| \
		(cmu_lpf_cp<<5)|(cmu_cp_new_en<<4)|(cmu_ldo_en<<3)|(cmu_vc_dly<<2)|(cmu_en_ckoobs<<1)|(test_en<<0);
	return res;
}

这段的功能是根据pll_mhz结构填充parameters.soc.pll_info,设定cpu和dram的分频或倍频频率(PLL为分频器),因为两者不一样,pll_mhz包括了pll是由sw函数hd定义,cpu频率,dram频率,lexra频率,parameters.soc.pll_info包括了由pll_mhz某些字段定义的一些其他参数,需要通过计算,所以并不能直接赋值,而是经过_pll_gen3_set_from_mhz函数的转换,两者结构定义如下:

pll_gen3_mhz_t pll_mhz = {
	.mode = PLL_MODE_BY_SW,
	.cpu = 500,
	.lx  = 200,
	.mem = 300,
};

typedef struct {
	u32_t set_by;   // 1-software or 0-pin
	u32_t pll_cpu_ctl0;
	u32_t pll_cpu_ctl1;
	u32_t pll_cpu_misc_ctrl;
	u32_t pll_mem_ctl0;
	u32_t pll_mem_ctl1;
	u32_t pll_mem_misc_ctrl;
} pll_gen3_info_t;

3.3.5 pll_setup函数

此函数通过parameters.soc.pll_info结构设定pll外设的参数,也就是设定分频或倍频频率。

PLL:全称PLL(Phase Locked Loop),为锁相回路或锁相环,用来统一整合时脉讯号,使内存能正确的存取资料。PLL用于振荡器中的反馈技术。 许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步,利用锁相环路就可以实现这个目的。

简单地说,在我们的机器中,pll设备的作用就是使作为输入的振荡频率降低或升高到需要的频率,以满足输出作为cpu、dram、uart、phy或其他需要时钟信号的设备的输入频率。

void pll_setup(void) {
	printf("\rII-dycc: PLL... ");
	parameters._pll_query_freq = pll_query_freq;
	parameters._udelay = udelay;
	pll_gen3_setup();
	printf("OK\n");
	return;
}

SECTION_ON_FLASH void 
pll_gen3_setup(void) {
	const pll_info_t *pll_param_p;
	/* Retrive PLL register value */
	pll_param_p = &(parameters.soc.pll_info);
//#define PLL_GEN3_DBG
#ifdef PLL_GEN3_DBG
	printf("set_by=%d\n", pll_param_p->set_by);
	printf("pll_cpu_ctl0=0x%08x\n",  pll_param_p->pll_cpu_ctl0);
	printf("pll_cpu_ctl1=0x%08x\n",  pll_param_p->pll_cpu_ctl1);
	printf("pll_cpu_misc_ctrl=0x%08x\n",  pll_param_p->pll_cpu_misc_ctrl);
	printf("pll_mem_ctl0 =0x%08x\n",  pll_param_p->pll_mem_ctl0);
	printf("pll_mem_ctl1 =0x%08x\n",  pll_param_p->pll_mem_ctl1);
	printf("pll_mem_misc_ctrl =0x%08x\n",  pll_param_p->pll_mem_misc_ctrl);
#endif    
	if (pll_param_p->set_by == 1) {
		/* 1-software or 0-pin */
		otto_pll_gen3_set_to_reg((pll_info_t *)pll_param_p,(OTTO_PLL_CPU_SET|OTTO_PLL_MEM_SET));
		printf("\rII-dycc: PLL is set by SW... ");
	} else {
		/* PLL is set by HW strapped-pin */
		printf("\rPLL is set by HW pin... ");
	}
	
	pll_query_freq(PLL_DEV_CPU);
}

/* Returns in MHz. */
u32_t
pll_query_freq(u32_t dev) {
	if (_pll_freq[dev] == PLL_MHZ_UNKNOWN) {
		pll_gen3_mhz_t pll_mhz;
		pll_result_t res;
		res = pll_gen3_get_to_mhz(NULL, &pll_mhz);
		if (res == PLL_RES_OK) {
			_pll_freq[PLL_DEV_CPU] = pll_mhz.cpu;
			_pll_freq[PLL_DEV_LX]  = pll_mhz.lx;
			_pll_freq[PLL_DEV_MEM] = pll_mhz.mem;
		} else {
			printf("EE: %s fails: %d\n", __func__, res);
			while (1);
		}
	}
	return _pll_freq[dev];
}

static u32_t _pll_freq[3] = {PLL_MHZ_UNKNOWN, PLL_MHZ_UNKNOWN, PLL_MHZ_UNKNOWN};

SECTION_ON_FLASH pll_result_t
pll_gen3_get_to_mhz(const pll_info_t *pll_reg,
					pll_gen3_mhz_t *pll_mhz) {
	pll_gen3_info_t tmp;
	if (pll_reg==NULL) {
		tmp.pll_cpu_ctl0 = REG32(REG_PLL_CPU_CTRL0);
		tmp.pll_cpu_ctl1 = REG32(REG_PLL_CPU_CTRL1);
		tmp.pll_mem_ctl0 = REG32(REG_PLL_MEM_CTRL0);
		tmp.pll_mem_ctl1 = REG32(REG_PLL_MEM_CTRL1);
		pll_reg=&tmp;
	}
	return _pll_gen3_get_to_mhz(pll_reg, pll_mhz);
}

SECTION_ON_FLASH pll_result_t
_pll_gen3_get_to_mhz(const pll_info_t *pll_reg,
					 pll_gen3_mhz_t *pll_mhz) {
	pll_mhz->mode = PLL_MODE_BY_SW;
	pll_mhz->cpu  = pll_gen3_query_ocp_freq(pll_reg);
	pll_mhz->lx   = LX_CLK_MHZ;
	pll_mhz->mem  = pll_gen3_query_mem_freq(pll_reg);
	return PLL_RES_OK;
}

#define pll_gen3_query_ocp_freq(i) pll_gen3_query_ocp_mem_freq(i, 1)
#define pll_gen3_query_mem_freq(i) pll_gen3_query_ocp_mem_freq(i, 0)

SECTION_ON_FLASH static u32_t
pll_gen3_query_ocp_mem_freq(const pll_gen3_info_t *info,
							int is_ocp) {
	const u8_t divn3[] = {2, 3, 4, 6};
	reg_pll_cpu_mem_ctrl0 reg0;
	reg_pll_cpu_mem_ctrl1 reg1;
	u32_t f;
	if (is_ocp) {
		// cpu
		f=2;
		reg0.regVal = info->pll_cpu_ctl0;
		reg1.regVal = info->pll_cpu_ctl1;
	} else {
		// memory
		f=4;
		reg0.regVal = info->pll_mem_ctl0;
		reg1.regVal = info->pll_mem_ctl1;
	}
	return (25 *
			(1 << reg0.bf.cmu_sel_prediv)*
			((reg0.bf.cmu_bypass_pi == 1) ? (reg0.bf.cmu_ncode_in + 4) : 0) *
			((reg0.bf.cmu_sel_div4 == 0) ? 1 : 4) /
			(((reg1.bf.cmu_divn2_selb == 0) ? (reg0.bf.cmu_divn2 + 4) : divn3[reg1.bf.cmu_divn3_sel])*f)
			);
}

/* otto_pll_gen3_set_to_reg() may change freq. of NOR SPI-F,
   therefore it must *NOT* run on flash! */
void
otto_pll_gen3_set_to_reg(pll_info_t *ptr,
						 unsigned int flag) {
	if( flag & OTTO_PLL_CPU_SET ) {
		/* Step1. Switch CPU PLL to LX Clock. [Bit12] =0*/
		REG32(REG_PLL_GLB_CTRL) &= ~PLL_GLB_CTRL_CPU_PLL_SRC_CPU;
		/* Step2. Disable CPU PLL [Bit0] =0 */
		REG32(REG_PLL_GLB_CTRL) &= ~PLL_GLB_CTRL_CPU_PLL_EN_MASK;
		/* Step3. Assigne value to OCP PLL */
		REG32(REG_PLL_CPU_MISC_CTRL) = ptr->pll_cpu_misc_ctrl;
		REG32(REG_PLL_CPU_CTRL0)     = ptr->pll_cpu_ctl0;
		REG32(REG_PLL_CPU_CTRL1)     = ptr->pll_cpu_ctl1;
		/* Step4. Enable (Reset) CPU PLL, in order to make sure the ready bit is effective [Bit0] =1*/
		REG32(REG_PLL_GLB_CTRL) |= PLL_GLB_CTRL_CPU_PLL_EN_MASK;
		/* Step5. Need to delay for a wihle, and then polling CPU_PLL_READY until it's 1 [Bit8]=1*/  
		while(!(REG32(REG_PLL_GLB_CTRL) & PLL_GLB_CTRL_OCP_PLL_RDY_MASK));    
		/* Step6. Switch CPU Clock to CPU PLL clock. [Bit12] =1 */
		REG32(REG_PLL_GLB_CTRL) |= PLL_GLB_CTRL_CPU_PLL_SRC_CPU;
	} 
	if( flag & OTTO_PLL_MEM_SET ) {
		/* Step1. Disable MEM PLL [Bit2]=0*/
		REG32(REG_PLL_GLB_CTRL) &= ~PLL_GLB_CTRL_MEM_PLL_EN_MASK;
		/* Step2. Assigne value to MEM PLL */
		REG32(REG_PLL_MEM_MISC_CTRL) = ptr->pll_mem_misc_ctrl;
		REG32(REG_PLL_MEM_CTRL0)     =  ptr->pll_mem_ctl0;  
		REG32(REG_PLL_MEM_CTRL1)     =  ptr->pll_mem_ctl1;
		/* Step3. Enable MEM PLL [Bit2]=1 */
		REG32(REG_PLL_GLB_CTRL) |= PLL_GLB_CTRL_MEM_PLL_EN_MASK;
		/* Step4. Need to delay for a wihle, and then polling MEM_PLL_READY until it's 1 [Bit10]=1*/  
		while(!(REG32(REG_PLL_GLB_CTRL) & PLL_GLB_CTRL_MEM_PLL_RDY_MASK));    
		/* Step5. Enable MEM Clock to DRAM chip is moved to dram calibration code */
	}
	_pll_freq[PLL_DEV_CPU] = PLL_MHZ_UNKNOWN;
	_pll_freq[PLL_DEV_LX]  = PLL_MHZ_UNKNOWN;
	_pll_freq[PLL_DEV_MEM] = PLL_MHZ_UNKNOWN;
	return;
}

首先通过otto_pll_gen3_set_to_reg设定pll_param_p的参数到pll设备寄存器,然后再读取pll频率参数填充_pll_freq结构体,次数pll设定完成。

3.3.6 dram_setup函数

代表有两个dram模块(这个应该不对,只有一个dram,128Mbyte)

#define SOC_NUM_DRAM_SPARE 2

定义DRAMI为dram_info

#define DRAMI (parameters.soc.dram_info)

dram_info的内容全部在此头文件

#include <DRAM_GEN2_SAMSUNG_K4B1G0846G_838x_DEMO_300MHZ.h>

下面是一些相关结构体和宏:

#define OTTO_DRAM_GEN 2
#elif (OTTO_DRAM_GEN == 2)
	typedef dram_gen2_info_t    dram_info_t;
#else

typedef struct {
	// the following fields are composed by the composer, soc.tcl doesn't fill in directly
	u32_t mcr;    //IPREF-23,DPREF-22
	u32_t dcr;    //BANKCNT-29:28,DBUSWID-25:24,ROWCNT-23:20,COLCNT-19:16,DCHIPSEL-15,FAST_RX-14,BSTREF-13
	u32_t dtr0;   //T_CAS-31:28,T_WR-27:24,T_CWL-23:20,T_RTP-19:16,T_WTR-15:12,T_REFI-11:8,T_REFI_UNIT-7:4
	u32_t dtr1;   //T_RP-28:24,T_RCD-20:16,T_RRD-12:8,T_FAWG-4:0
	u32_t dtr2;   //T_RFC-28:20,T_RAS-18:12
	u32_t mpmr0;  //PM_MODE-29:28,T_CKE-27:24,T_RSD-21:12,T_XSREF-9:0
	u32_t mpmr1;  //T_XARD-32:28,T_AXPD-27:24
	u32_t dider;  //DQS0_EN_HCLK-31,DQS0_EN_TAP-28:24,DQS1_EN_HCLK-23,DQS1_EN_TAP-20:16
	u32_t d23oscr;//ODT_ALWAYS_ON-31,TE_ALWAYS_ON-30
	u32_t daccr;  //AC_MODE-31,DQS_SE-30,DQS0_GROUP_TAP-20:16,DQS1_GROUP_TAP-12:8,AC_DYN_BPTR_CLR_EN-5,AC_BPTR_CLEAR-4,AC_DEBUG_SEL-3:0
	u32_t dacspcr;/* AC_SILEN_PERIOD_EN-31, AC_SILEN_TRIG-20, AC_SILEN_PERIOD_UNIT-19:16, AC_SILEN_PERIOD-15:8, AC_SILEN_LEN-7:0 */  
	u32_t dacspar;/* AC_SPS_DQ15R-31, AC_SPS_DQ14R-30, AC_SPS_DQ13R-29, AC_SPS_DQ12R-28, AC_SPS_DQ11R-27, AC_SPS_DQ10R-26, AC_SPS_DQ9R-25, AC_SPS_DQ8R-24, AC_SPS_DQ7R-23, AC_SPS_DQ6R-22, AC_SPS_DQ5R-21, AC_SPS_DQ4R-20, AC_SPS_DQ3R-19, AC_SPS_DQ2R-18, AC_SPS_DQ1R-17, AC_SPS_DQ0R-16, AC_SPS_DQ15F-15, AC_SPS_DQ14F-14, AC_SPS_DQ13F-13, AC_SPS_DQ12F-12, AC_SPS_DQ11F-11, AC_SPS_DQ10F-10, AC_SPS_DQ9F-9, AC_SPS_DQ8F-8, AC_SPS_DQ7F-7, AC_SPS_DQ6F-6, AC_SPS_DQ5F-5, AC_SPS_DQ4F-4, AC_SPS_DQ3F-3, AC_SPS_DQ2F-2, AC_SPS_DQ1F-1, AC_SPS_DQ0F-0 */ 
	// the following fields are composed by the composer, soc.tcl doesn't fill in directly
	/* Mode registers, follow JEDEC naming*/
	u32_t DDR1_mr;
	......
	u32_t DDR3_mr3;
	
	// the following fields are inputed by soc.tcl
	u32_t static_cal_data_0; //0xb8001510
	......
	u32_t static_cal_data_32;//0xb8001590
	
	u32_t zq_setting;    
	u8_t calibration_type;  //0-static, 1-software(static_cal_data is not used)
	u8_t tx_clk_phs_delay;
	u8_t clkm_delay;
	u8_t clkm90_delay;
	u8_t auto_calibration;  //0-disable, 1-enable. Calibration window auto-sliding. 
	u8_t drv_strength;      //0-normal, 1-reduced
} SOC_ALIGN dram_gen2_info_t;

主体代码如下:

#if SOC_NUM_DRAM_SPARE > 0
#define DRAMI (parameters.soc.dram_info)
	u32_t sel, di_num;
	dram_info_t *di;
	sel = dram_model_select();
	di = get_dram_spare(&di_num);
	if ((sel > 0) && (sel <= di_num)) {
		di = get_dram_spare(&di_num);
		di += (sel - 1);
		memcpy((void *)&DRAMI, (void *)di, sizeof(dram_info_t));
	}
	dram_setup();
	if (sel > di_num) {
		printf("EE: Bad DRAM model #%d from dram_model_select()."
			   " Only 0 ~ %d are configured in soc.tcl.\n",
			   sel, di_num);
		while (1);
	}
	printf("II: Selected DRAM model #%d.\n", sel);
#else
	dram_setup();
#endif

u32_t dram_model_select(void) {
	return 0;
}

static inline dram_info_t *get_dram_spare(u32_t *num_instances) {
	return (dram_info_t *)search_spare_by_type(SST_DRAM, num_instances);}

void *
search_spare_by_type(u32_t type, u32_t *num_instances) {
	const spare_header_t *h=parameters.soc.spare_headers;
	while (h!=NULL) {
		if (h->type==type) {
			*num_instances=h->num_instances;
			return h->spare;
		} else if (h->type==SST_END) {
			return (void*)0;
		}
		++h;
	}
	return (void*)0;
}

.spare_headers = (spare_header_p)spare_headers_in_flash,

const spare_header_t spare_headers_in_flash[] = {
	{
		SST_DRAM,
		SOC_NUM_DRAM_SPARE,
		(void *)dram_info_in_spare
	},
	{
		SST_FLASH,
		SOC_NUM_FLASH_SPARE,
		(void *)flash_info_in_spare
	},
	{
		SST_END,
		0,
		0
	}
};

// for spare structure 
typedef struct spare_header_s {
	u32_t type;
	u32_t num_instances;
	void_p spare;
} spare_header_t;
// spare structure types
#define SST_DRAM                    0x4452414D
#define SST_FLASH                   0x464C5348
#define SST_END                     0x454E4420

#define SOC_NUM_DRAM_SPARE 2

const dram_info_t dram_info_in_spare[SOC_NUM_DRAM_SPARE] = {
	{
		#include <DRAM_GEN2_NANYA_NT5TU128M8GE-AC_838x_DEMO_300MHZ.h>
	}, {
		#include <DRAM_GEN2_NANYA_NT5TU128M8GE-AC_ZQ-STATIC_838x_DEMO_300MHZ.h>
	}
};

dram init过程为:

先通过dram_model_select获得sel的值,永远为0,这个字表示当前dram模块的编号,从0开始,然后通过get_dram_spare获取此dram的相关结构,

在search_spare_by_type里面,遍历parameters.soc.spare_headers结构,也就是spare_headers_in_flash结构,此结构包含了dram和flash的相关驱动和参数信息,因为dram的类型为SST_DRAM,所以在search_spare_by_type里面匹配SST_DRAM类型,有则将parameters.soc.spare_headers.num_instances也就是SOC_NUM_DRAM_SPARE赋值给num_instances,也就是di_num,最后,返回parameters.soc.spare_headers.spare,也就是dram_info_in_spare包含的两个头文件的内容,给dram_info_t *di,然后执行dram_setup。

void dram_setup(void) {
	u32_t reg_val;
	volatile unsigned int delay_loop;
#ifdef DRAM_PARAM_DEBUG
	dram_display_param_info();
#endif //#ifdef DRAM_PARAM_DEBUG
	/* MCR is mixed with FLASH setting, using read-modify-write to update it */
	reg_val = REG32(MCR_A);
	reg_val &= ~(MCR_IPREF_MASK | MCR_DPREF_MASK);
	reg_val |= DRAMI.mcr;
	REG32(MCR_A) = reg_val;
	/* Has not been fully verified (suggested by YUMC) */
	//REG32(MPMR0_A)   = DRAMI.mpmr0;
	//REG32(MPMR1_A)   = DRAMI.mpmr1;
	REG32(DIDER_A)   = DRAMI.dider;
	//REG32(D23OSCR_A) = DRAMI.d23oscr;
	REG32(DACCR_A)   = DRAMI.daccr;
	REG32(DACSPCR_A) = DRAMI.dacspcr;
	REG32(DACSPAR_A) = DRAMI.dacspar;
	plat_mem_clk_rev_check();
	/* Enable DRAM clock */
	plat_memctl_dramclk_en();
	memctlc_config_DRAM_size();
	/* Configure DRAM timing parameters */
	memctlc_config_DTR();
	if(memctlc_ZQ_config() != INI_RES_OK) {
		parameters.dram_init_result = INI_RES_DRAM_ZQ_CALI_FAIL;
		goto done;
	}
	/* Set data buffer full mask, enabled when memclk > 200MHz */
	if(plat_memctl_MEM_clock_MHz() > 200) {
		memctlc_DBFM_enable();    
	}
	if(DRAMI.calibration_type == 0) { /* Static calibration */
		printf("II: DRAM is set by static calibration... ");
		dram_static_calibration();
	} else {/* software calibration */
		printf("II: DRAM is set by software calibration... ");
		if(dram_software_calibration() == 0) {
			printf("PASSED\n");
		} else {            
			printf("FAILED\n");
			parameters.dram_init_result = INI_RES_DRAM_SW_CALI_FAIL; //Add new enum for calibration failure
			goto done;
		}
	}
	memctlc_dram_phy_reset();
	memctlc_config_DRAM_size();
  
	delay_loop = 0x80000;
	while(delay_loop--);
	/* A simple test */
#if (SELFTEST == 1)
	printf("DD: Simple DRAM test... ");
	*((volatile u32_t *)0x80000000) = 0x5AFE5AFE;
	if (*((volatile u32_t *)0x80000000) != 0x5AFE5AFE) {
		parameters.dram_init_result = INI_RES_TEST_FAIL;
		goto done;
	}
	*((volatile u32_t *)0xA0000000) = 0x05D650C0;
	if (*((volatile u32_t *)0xA0000000) != 0x05D650C0) {
		parameters.dram_init_result = INI_RES_TEST_FAIL;
		goto done;
	} else {
		/* This could be kinda weird, but coming this far means both $ and un$ tests are passed. */
		printf("OK\n");
		parameters.dram_init_result = INI_RES_OK;
	}
#endif
	parameters.dram_init_result = INI_RES_OK;
	
done:
	 plat_memctl_show_dram_config();
	return;
}

dram_setup首先设定相关dram寄存器,然后测试dram是否init完成,最后show dram相关信息和寄存器设定值。

3.3.7 c_start_r函数

void c_start_r(void){
	memcpy((void *)(CONFIG_SYS_TEXT_BASE),
		   (void *)&uboot_start,
		   (u32_t)&uboot_size);
	plat_memctl_dcache_flush();
	printf("OK\n");
	return;
}

copy从flash地址uboot_start = 0x9fc0795c开始到结尾的0x3b8c4大小的到CONFIG_SYS_TEXT_BASE = 0x83F00000。

/* CPU related (cache flush) */
void plat_memctl_dcache_flush(void)
{
	sram_parameters._dcache_writeback_invalidate_all();
	return;
}
GFUNC(writeback_invalidate_dcache_all)
							mtc0	zero, CP0_TAGLO
							li		t0, CKSEG0
							li		t2, DCACHE_SIZE
							addu	t1, t0, t2
1:
							cacheop(Index_Writeback_Inv_D, t0)
							addiu	t0, t0, CACHELINE_SIZE
							bne		t0, t1, 1b
							jr		ra
END(writeback_invalidate_dcache_all)

设定所有cache,是dram能够通过缓存访问。

四、从sram跳转到dram的过程

GFUNC(soc_init_in_cache)
  la      sp, c_sp_pos
  lw      sp, 0(sp)
  la      t9, c_start
  jal     t9
  nop
  li      sp, CONFIG_SYS_TEXT_BASE - 8
  la      t9, board_init_f
  j       t9
  nop
END(soc_init_in_cache)

当c_start执行完之后,CONFIG_SYS_TEXT_BASE = 0x83F00000,从此地址开始已经存在了uboot的code,且第7行的执行地址为sram,第9行设定sp为uboot code下面的空间,由于board_init_f的执行地址为:

 .text.board_init_f
				0x0000000083f003d8      0x108 arch/mips/lib/libmips.o
				0x0000000083f003d8                board_init_f

此地址是第一章elf32文件格式里指定的virtual address (VMA),因此,t9 = 0x83f003d8,第11行则真正跳转到了dram地址的0x83f003d8开始执行board_init_f。

参考资料:

https://sourceware.org/binutils/docs/ld/Output-Section-LMA.html#Output-Section-LMA (Output-Section-LMA)

五、.dram段执行过程

board_init_f,此函数执行到board_init_r后不返回

1.1 之前因为有奖uboot从0x9fc0795copy到dram,这段刚好对应bin文件的.dram段,因为里面肯定有包含未初始化的数据段.bss,因此,需要清零dram上的.bss段做初始化。

/* set .bss to zero */
memset(&uboot_end_data, 0, ((void *)&uboot_end) - ((void *)&uboot_end_data));

1.2 gd_data为uboot的全局数据结构,将其地址保存在gd = k0 = $26寄存器

#define k0      $26     /* kernel scratch */

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("k0")

/* Pointer is writable since we allocated a register for it.
	 */
	gd = &gd_data;

1.3 The GCC manual says using ‘asm volatile’ prevents the asm instruction from being ‘movedsignificantly’ and adding ‘memory’ to the clobber list prevents GCC from keeping memory values cached in registers across the assembler instruction, but also says ‘GCC will perform some optimizations across a volatile asm instruction’ but doesn’t explain what.

1) __asm__用于指示编译器在此插入汇编语句。

2) __volatile_用于告诉编译器,严禁将此处的汇编语句与其它的语句重组合优化。即:原原本本按原来的样子处理这这里的汇编。

3) memory强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu又将registers,cache中的数据用于去优化指令,而避免去访问内存。

4) "":::表示这是个空指令。barrier()不用在此插入一条串行化汇编指令。

/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("" : : : "memory");

1.4 初始化环境

memset((void *)gd, 0, sizeof(gd_t));
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
	if ((*init_fnc_ptr)() != 0)
		hang();
}

typedef int (init_fnc_t)(void);
init_fnc_t *init_sequence[] = {
	board_early_init_f,
	timer_init,
	env_init,		/* initialize environment */
	//init_baudrate,		/* initialize baudrate settings */
	//serial_init,		/* serial communications setup */
	//console_init_f,
	display_banner,		/* say that we are here */
	checkboard,
	init_func_ram,
	NULL,
};

首先清空gd指向的全局结构,然后依次执行init_sequence里的函数。

1.4.1 board_early_init_f

int __board_early_init_f(void)
{
	/*
	 * Nothing to do in this dummy implementation
	 */
	return 0;
}
int board_early_init_f(void)
	__attribute__((weak, alias("__board_early_init_f")));

1.4.2 timer_init

int timer_init(void)
{	
	/* disable timer */
	REG32(BSP_TCCNR) = 0; /* disable timer before setting CDBR */
	/* initialize timer registers */
	REG32(BSP_CDBR)=(board_LX_freq_mhz()) << BSP_DIVF_OFFSET;
	REG32(BSP_TC0DATA) = (0xFFFFFFF) << BSP_TCD_OFFSET;
		
	REG32(BSP_TCCNR) = BSP_TC0EN | BSP_TC0MODE_TIMER;
	return 0;
}

重启定时器。

1.4.3 env_init

/*
 * Initialize Environment use
 *
 * We are still running from ROM, so data use is limited
 */
int env_init(void)
{
	gd->env_addr	= (ulong)&default_environment[0];
	gd->env_valid	= 0;
	return 0;
}

填充gd结构。

.4.4 display_banner

static int display_banner(void)
{
	printf("\n\n%s\n\n", version_string);
	return 0;
}

#define U_BOOT_VERSION_STRING U_BOOT_VERSION " (" U_BOOT_DATE " - " \
	U_BOOT_TIME ")" CONFIG_IDENT_STRING

1.4.5 checkboard

int checkboard (void) {
	printf("Board: RTL838x CPU:%dMHz LXB:%dMHz MEM:%dMHz\n",
		   board_CPU_freq_mhz(), board_LX_freq_mhz(), board_DRAM_freq_mhz());
	return 0;
}

1.4.6 init_func_ram

static int init_func_ram(void)
{
#ifdef	CONFIG_BOARD_TYPES
	int board_type = gd->board_type;
#else
	int board_type = 0;	/* use dummy arg */
#endif
	puts("DRAM:  ");
	gd->ram_size = initdram(board_type);
	if (gd->ram_size > 0) {
		print_size(gd->ram_size, "\n");
		return 0;
	}
	puts(failed);
	return 1;
}

phys_size_t initdram(int board_type __attribute__((unused))) {
#define DCR (*((volatile int *)(0xb8001004)))
	const unsigned char BNKCNTv[] = {1, 2, 3};
	const unsigned char BUSWIDv[] = {0, 1, 2};
	const unsigned char ROWCNTv[] = {11, 12, 13, 14, 15, 16};
	const unsigned char COLCNTv[] = {8, 9, 10, 11, 12};	
	return 1 << (BNKCNTv[(DCR >> 28) & 0x3] +
			BUSWIDv[(DCR >> 24) & 0x3] +
			ROWCNTv[(DCR >> 20) & 0xF] +
			COLCNTv[(DCR >> 16) & 0xF]);
}

读取dram信息填充gd结构,打印dram大小。

1.4.7 其他

CONFIG_SYS_MALLOC_LEN="(1 << 20)"

#define UNIT_SIZE 65536  //only for this file
#define LOADER_BDINFO_SIZE  (UNIT_SIZE*1)                               //0x00010000
CONFIG_ENV_SIZE="LOADER_BDINFO_SIZE"

/* Otto architecture the following is always used */
#define	TOTAL_MALLOC_LEN	(CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE)
mem_malloc_init((ulong)__builtin_alloca(TOTAL_MALLOC_LEN), TOTAL_MALLOC_LEN);

申请环境变量的空间。

typedef struct bd_info {
	int		bi_baudrate;	/* serial console baudrate */
	unsigned long	bi_ip_addr;	/* IP Address */
	unsigned long	bi_arch_number;	/* unique id for this board */
	unsigned long	bi_boot_params;	/* where this board expects params */
	unsigned long	bi_memstart;	/* start of DRAM memory */
	phys_size_t	bi_memsize;	/* size	 of DRAM memory in bytes */
	unsigned long	bi_flashstart;	/* start of FLASH memory */
	unsigned long	bi_flashsize;	/* size  of FLASH memory */
	unsigned long	bi_flashoffset;	/* reserved area for startup monitor */
} bd_t;

bd = (bd_t *)__builtin_alloca(sizeof(bd_t));
	gd->bd = bd;

申请gd->bd的空间。

id = (gd_t *)__builtin_alloca(sizeof(gd_t));

申请临时使用的gd结构。

bd->bi_boot_params = (unsigned long)__builtin_alloca(CONFIG_SYS_BOOTPARAMS_LEN);

申请并填充bd->bi_boot_params结构。

/*
	 * Save local variables to board info struct
	 */
	bd->bi_memstart	= CONFIG_SYS_SDRAM_BASE;	/* start of DRAM */
	bd->bi_memsize	= gd->ram_size;		/* size of DRAM in bytes */
	bd->bi_baudrate	= gd->baudrate;		/* Console Baudrate */

保存相关变量到gd结构。

memcpy(id, (void *)gd, sizeof(gd_t));

将全局gd复制给局部id。

/* Obtain kernel address from preloader. */ load_addr = parameters.soc.layout.kernel1_addr; load_addr += CONFIG_SYS_FLASH_BASE; asm volatile(“” : : : “memory”);

计算内核加载地址:0xb4100000。

board_init_r(id, CONFIG_SYS_TEXT_BASE); /* NOTREACHED - relocate_code() does not return */

执行board_init_r,这里不需要relocate_code。

2. board_init_r

void board_init_r(gd_t *id, ulong dest_addr)
{
#ifndef CONFIG_SYS_NO_FLASH
	ulong size;
#endif
	extern void malloc_bin_reloc(void);
	bd_t *bd;
	gd = id;
	gd->flags |= GD_FLG_RELOC;	/* tell others: relocation done */
	debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);
	gd->reloc_off = 0;
	monitor_flash_len = (ulong)&uboot_end_data - dest_addr;
	/* there are some other pointer constants we must deal with */
#ifndef CONFIG_ENV_IS_NOWHERE
	env_name_spec += gd->reloc_off;
#endif
	bd = gd->bd;
#ifndef CONFIG_SYS_NO_FLASH
	size = flash_init();
	display_flash_config(size);
	bd->bi_flashsize = size;
#endif
#ifdef CONFIG_CMD_SF
	puts("SPI-F: ");
	spi_flash_init();
#endif
	bd->bi_flashstart = CONFIG_SYS_FLASH_BASE;
	bd->bi_flashoffset = 0;
	/* relocate environment function pointers etc. */
	printf("Loading %dB env. variables from offset 0x%x\n",
		   CONFIG_ENV_SIZE, CONFIG_ENV_OFFSET);
	env_relocate();
	init_baudrate();	/* initialize baudrate settings */
	serial_init(CONSOLE_CH_INDEX,gd->baudrate);		/* serial communications setup */
	console_init_f();
	/* IP Address */
	bd->bi_ip_addr = getenv_IPaddr("ipaddr");
/** leave this here (after malloc(), environment and PCI are working) **/
	/* Initialize stdio devices */
	stdio_init();
	jumptable_init();
	/* Initialize the console (after the relocation and devices init) */
	console_init_r();
/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/
	/* Initialize from environment */
	load_addr = getenv_ulong("loadaddr", 16, load_addr);
#if defined(CONFIG_CMD_NET)
	{
		char *s = getenv("bootfile");
		if (s != NULL)
			copy_filename(BootFile, s, sizeof(BootFile));
	}
#endif
#if defined(CONFIG_MISC_INIT_R)
	/* miscellaneous platform dependent initialisations */
	misc_init_r();
#endif
	soc_post_init();
#if defined(CONFIG_CMD_NET)
	puts("Net:   ");
	eth_initialize(gd->bd);
#endif
	/* main_loop() can return to retry autoboot, if so just run it again. */
	for (;;)
		main_loop();
	/* NOTREACHED - no way out of command loop except booting */
}

以上代码经过删除,将未执行的部分去掉了

首先,设定gd->flags = GD_FLG_RELOC,完成重定位的flag,monitor_flash_len为除了.bss段的被copy到dram上的代码的长度,env_name_spec += gd->reloc_off定位当前存储器地址,bd = gd->bd。

2.1 spi_flash_init

void spi_flash_init(void) {
#if (CONFIG_STANDALONE_UBOOT == 1)
	/* When there is no Preloader, flash driver is bundled with
	   U-Boot flow, as a result, flash initialization should start
	   after U-Boot resets bss to zero. */
	flash_init();
#endif
	printf("%dx%d MB\n",
		   FLASHI.num_chips, (1<<FLASHI.size_per_chip)>>20);
	otto_sf.spi = &otto_slave;
	otto_sf.name = NULL;
	otto_sf.size = 1 << FLASHI.size_per_chip;
	otto_sf.page_size = (FLASHI.wr_boundary==0) ? (1<<FLASHI.size_per_chip) :
												  (1<<FLASHI.wr_boundary);
	otto_sf.sector_size = (1 << FLASHI.erase_unit);
	otto_sf.read  = otto_spi_flash_read;
	otto_sf.write = otto_spi_flash_write;
	otto_sf.erase = otto_spi_flash_erase;
	return;
}

初始化flash,hook flash驱动到otto_sf。

2.2 flash_init

#define SPIF_BASE 0xb8001200

SPI Flash Configuration Register (SFCR) (0xB800_1200)

#define SFCSR (*((volatile u32_t *)(SPIF_BASE + 0x8 )))

读取SFCR寄存器内容。

/* 1. The dumb 100-poll is to avoid a potential SPI-F CTRL bug.
   2. Experiment showed that CHECK_CTRL_READY() function call
	  conducts smaller code size than macro. */
static inline void CHECK_CTRL_READY(void) {
	u32_t i = 0, err = 0;
	for (i=0; i<100; i++) {
		while ((SFCSR & SFCSR_SPI_RDY) == 0) {
			i = 0;
			if ((err++) > 1000000) {
				pblr_puts("EE: NOR SPI flash controller does not respond.\n");
				while (1);
			}
		};
	}
	return;
}

检查spi总线是否准备好。

static u32_t _spi_cmd(const u8_t cid, const u32_t opcode, sffunc_ptr sffunc,
					  const u32_t data, u32_t wr_leng, void *wr_buf) {
	const u32_t sfcsr_act_cs[4] = {0x7FFFFFFF, 0xBFFFFFFF, 0xFFFF7FFF, 0xFFFFBFFF};
	u32_t sfcsr_val, ret = 0;
	CHECK_CTRL_READY();
	SFCSR = SF_CS_INIT; /* deactivate CS0 and CS1 */
	CHECK_CTRL_READY();
	SFCSR = 0;          /* activate CS0 and CS1 */
	udelay(3);
	SFCSR = SF_CS_INIT; /* deactivate CS0 and CS1 */
	sfcsr_val = (SFCSR & sfcsr_act_cs[cid]);
	CHECK_CTRL_READY();
	ret = sffunc(cid, sfcsr_val, opcode, data, wr_leng, wr_buf);
	CHECK_CTRL_READY();
	SFCSR = SF_CS_INIT; /* deactivate CS0 and CS1 */
	CHECK_CTRL_READY();
	SFCSR = 0;          /* activate CS0 and CS1 */
	udelay(3);
	SFCSR = SF_CS_INIT; /* deactivate CS0 and CS1 */
	CHECK_CTRL_READY();
	return ret;
}

spi总线下命令的函数,对spi很重要,具体参考spi总线通讯协议。

#define spi_cmd(cid, cmd, data, wr_leng, wr_buf) \
	_spi_cmd(cid, (cmd##_OP << 24), cmd##_FUNC, data, wr_leng, wr_buf)
#define SPI_CMD_SIMPLE(cid, cmd) spi_cmd(cid, cmd, 0, 0, NULL)
pblr_puts("searching flash parameters... ");
	flash_id = SPI_CMD_SIMPLE(0, SFCMD_RDID) >> 8;

获取spi flash的设备id,确定flash型号。

	fi = get_flash_spare(&fi_num);
	fi_end = fi + fi_num;
static inline plr_flash_info_t *get_flash_spare(u32_t *num_instances) {
	return (plr_flash_info_t *)search_spare_by_type(SST_FLASH, num_instances);}
void *
search_spare_by_type(u32_t type, u32_t *num_instances) {
	const spare_header_t *h=parameters.soc.spare_headers;
	while (h!=NULL) {
		if (h->type==type) {
			*num_instances=h->num_instances;
			return h->spare;
		} else if (h->type==SST_END) {
			return (void*)0;
		}
		++h;
	}
	return (void*)0;
}

确定flash大小和模块数量。

	pblr_puts("supported flash ID: ");
	printf("[%06x]", para_flash_info.id);
	for (; fi<fi_end; fi++) {
		if((flash_id == MXIC25635EF_ID)&&(flash_id == fi->id)) { /*MXIC 25635E/25635F*/
			if(0xF == probe_mxic_id()) { /* 25635F */
				if ((flash_id == fi->id) && (PM_RWSR_4BCMDSET == fi->pm_method) && (para_flash_info.pm_method != fi->pm_method)) {
					memcpy((void *)&para_flash_info, (void *)fi, sizeof(plr_flash_info_t));
					printf("[%06x]-MXIC25635F", fi->id);
				} else if((fi_generic == NULL) && (GENERIC_FLASH_ID == fi->id)) {
					/* Record the location of common setting */
					fi_generic = fi;
				}
			}else{/* 25635E */			
				if ((flash_id == fi->id) && (PM_RWSR == fi->pm_method) && (para_flash_info.pm_method != fi->pm_method)) {
					memcpy((void *)&para_flash_info, (void *)fi, sizeof(plr_flash_info_t));
					printf("[%06x]-MXIC25635E", fi->id);
				} else if((fi_generic == NULL) && (GENERIC_FLASH_ID == fi->id)) {
					/* Record the location of common setting */
					fi_generic = fi;
				}			
			}				
		}else{	
			printf("[%06x]", fi->id);
			if ((flash_id == fi->id) && (para_flash_info.id != fi->id)) {
				memcpy((void *)&para_flash_info, (void *)fi, sizeof(plr_flash_info_t));
			} else if((fi_generic == NULL) && (GENERIC_FLASH_ID == fi->id)) {
				/* Record the location of common setting */
				fi_generic = fi;
			}
		}
	}
	if (para_flash_info.id == flash_id) {
		printf("... detected flash ID: [%06x]... ", para_flash_info.id);
	} else {
		printf("\nWARNING: flash ID [%06x] is not supported, please check your setting\n");
		if ((fi_generic != NULL) && (GENERIC_FLASH_ID == fi_generic->id)) {
			printf("A common setting found in the database is used but not recommended\n");
			memcpy((void *)&para_flash_info, (void *)fi_generic, sizeof(plr_flash_info_t));
		}
	}

遍历fi数组,比较id是否为0x00c22018,是则复制fi结构到para_flash_info。

/* Set Prefer Mode */
for (i=0; i<para_flash_info.num_chips; i++) {
	SPI_CMD_SIMPLE(i, SFCMD_WRDI);
	tmp = SPI_CMD_SIMPLE(i, SFCMD_RDID) >> 8;
	if ((para_flash_info.id != GENERIC_FLASH_ID) && (tmp != para_flash_info.id)) {
		printf("\nEE: Unsupported chip on CS%d: ID: 0x%x",
		       i, tmp);
		parameters.flash_init_result = INI_RES_UNKNOWN_MODEL;
	};
	switch (para_flash_info.pm_method) {
	case PM_RWSR:
        /* For MXIC, ... */
        if(4 == para_flash_info.addr_mode) {
             /* Addr mode: Using Enter 4Byte Command */
			SPI_CMD_SIMPLE(i, SFCMD_EN4B);                
        }
	case PM_RWSR_4BCMDSET:	/* Addr mode: Using 4Byte Command Set */
        if(NSPI_IO_QIO == para_flash_info.prefer_rd_data_io) {
            /* Quad mode */
		SPI_CMD_SIMPLE(i, SFCMD_WREN);
		tmp = SPI_CMD_SIMPLE(i, SFCMD_PARA_RDSR);
		tmp |= para_flash_info.pm_enable_bits;
		spi_cmd(i, SFCMD_PARA_WRSR, tmp, 0, NULL);
		SPI_CMD_SIMPLE(i, SFCMD_WRDI);
        }            
		break;
	case PM_CMD:
        /* For EON, ... */
        if(4 == para_flash_info.addr_mode) {
            /* Addr mode: Using Enter 4Byte Command */
			SPI_CMD_SIMPLE(i, SFCMD_EN4B);                
        }
	case PM_CMD_4BCMDSET:	/* Addr mode: Using 4Byte Command Set */
        if(NSPI_IO_QIO == para_flash_info.prefer_rd_data_io) {
            /* Quad mode */
		SPI_CMD_SIMPLE(i, SFCMD_PARA_EQCMD);
        }
		break;
	case PM_R2W1SR:
        /* For Winbond, ... */
        if(4 == para_flash_info.addr_mode) {
            /* Addr mode: Using Enter 4Byte Command */
			SPI_CMD_SIMPLE(i, SFCMD_EN4B);                
        }
	case PM_R2W1SR_4BCMDSET:	/* Addr mode: Using 4Byte Command Set */
        if(NSPI_IO_QIO == para_flash_info.prefer_rd_data_io) {
            /* Quad mode */
		SPI_CMD_SIMPLE(i, SFCMD_WREN);
		tmp  = SPI_CMD_SIMPLE(i, SFCMD_PARA_RDSR) << 8;
		tmp |= SPI_CMD_SIMPLE(i, SFCMD_PARA_RDSR2);
		tmp |= para_flash_info.pm_enable_bits;
		spi_cmd(i, SFCMD_PARA_WRSR, tmp, 0, NULL);
		SPI_CMD_SIMPLE(i, SFCMD_WRDI);
        }
		break;
	case PM_EN0:
        /* For Micron, ... */
        /* Disable Hold */
        tmp = SPI_CMD_SIMPLE(i, SFCMD_RDEVCR);
        tmp &= ~(1<<4);
        SPI_CMD_SIMPLE(i, SFCMD_WREN);
		spi_cmd(i, SFCMD_WREVCR, tmp, 0, NULL);
        SPI_CMD_SIMPLE(i, SFCMD_WRDI);
        
        /* Quad mode: nothing to do here */
        if(4 == para_flash_info.addr_mode) {
            /* Addr mode: Using Enter 4Byte Command */
            SPI_CMD_SIMPLE(i, SFCMD_WREN);
			SPI_CMD_SIMPLE(i, SFCMD_EN4B);                
            SPI_CMD_SIMPLE(i, SFCMD_WRDI);
        }
	case PM_EN0_4BCMDSET:	/* Addr mode: Using 4Byte Command Set */
        break;
        
	case PM_NONE:
		break;
	default:
		pblr_puts("\nEE: Unsupported prefer mode");
		parameters.flash_init_result = INI_RES_UNKNOWN_MODE;
	}
}
/* OTTO_FALSH_ADDR_MODE() is defined in arch.h and returns 3 or 4. */
if ((para_flash_info.addr_mode != OTTO_FLASH_ADDR_MODE())
    && (4 == para_flash_info.addr_mode)) {
    OTTO_FLASH_ENABLE_4BYTE_ADDR_MODE();
}
//rw_soc->flash_info.addr_mode = OTTO_FLASH_ADDR_MODE();    
/* for 4B addr mode, address of read/write/erase requires 32-bit and needs no shift.
   for 3B mode, address needs to shift 8 bits left to make a 24-bit address. */
addr_modifier = (4 - para_flash_info.addr_mode) * 8;
/* Set NOR SPI flash CTRL with parameters in flash_info. */
SFCR2 = (SFCR2_SFCMD(para_flash_info.prefer_rd_cmd)     |
         SFCR2_SIZE((para_flash_info.size_per_chip - sf_size_mod[4-para_flash_info.addr_mode])) |
         SFCR2_ADDRIO(para_flash_info.prefer_rd_addr_io)|
         SFCR2_DATAIO(para_flash_info.prefer_rd_data_io)|
         SFCR2_CMDIO(para_flash_info.prefer_rd_cmd_io)  |
         SFCR2_DUMMYCYCLE((para_flash_info.prefer_rd_dummy_c >> 1)));
parameters._nor_spi_erase = flash_unit_erase;
parameters._nor_spi_read  = flash_read;
parameters._nor_spi_write = flash_unit_write;
if(parameters.flash_init_result == INI_RES_UNINIT) {
	parameters.flash_init_result = INI_RES_OK;
	cur_res = res_strings[1];
}

为不同芯片做初始化序列,此时为0x1,mx25l flash芯片,并hook parameters结构相关spi flash的驱动函数。

2.3 environment init

bd->bi_flashstart = CONFIG_SYS_FLASH_BASE;
bd->bi_flashoffset = 0;
/* relocate environment function pointers etc. */
printf("Loading %dB env. variables from offset 0x%x\n",
       CONFIG_ENV_SIZE, CONFIG_ENV_OFFSET);
env_relocate();
init_baudrate();	/* initialize baudrate settings */
serial_init(CONSOLE_CH_INDEX,gd->baudrate);		/* serial communications setup */
console_init_f();
/* IP Address */
bd->bi_ip_addr = getenv_IPaddr("ipaddr");

重新初始化uart,为dram中的环境变量赋值。

2.4 stdio_init

/* Initialize the list */
INIT_LIST_HEAD(&(devs.list));

初始化设备链表,此链表将已经初始化的设备串联起来。

2.4.1 drv_system_init

static void drv_system_init (void)
{
	struct stdio_dev dev;
	memset (&dev, 0, sizeof (dev));
	strcpy (dev.name, "serial");
	dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
	dev.putc = serial_putc;
	dev.puts = serial_puts;
	dev.getc = serial_getc;
	dev.tstc = serial_tstc;
	stdio_register (&dev);
#ifdef CONFIG_SYS_DEVICE_NULLDEV
	memset (&dev, 0, sizeof (dev));
	strcpy (dev.name, "nulldev");
	dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
	dev.putc = nulldev_putc;
	dev.puts = nulldev_puts;
	dev.getc = nulldev_input;
	dev.tstc = nulldev_input;
	stdio_register (&dev);
#endif
}

初始化字符和字符串的get和set功能,然后新建一个设备结构,hook driver到此设备结构dev,最后将此设备结构dev添加到设备链表,stdio_register。

2.5 jumptable_init

void jumptable_init(void)
{
	gd->jt = malloc(XF_MAX * sizeof(void *));
#include <_exports.h>
}

首先根据XF_MAX申请合适空间,然后填充函数地址到gd->jt。

EXPORT_FUNC(get_version)
EXPORT_FUNC(getc)
EXPORT_FUNC(tstc)
EXPORT_FUNC(putc)
EXPORT_FUNC(puts)
EXPORT_FUNC(printf)
EXPORT_FUNC(install_hdlr)
EXPORT_FUNC(free_hdlr)
EXPORT_FUNC(malloc)
EXPORT_FUNC(free)
EXPORT_FUNC(udelay)
EXPORT_FUNC(get_timer)
EXPORT_FUNC(vprintf)
EXPORT_FUNC(do_reset)
EXPORT_FUNC(getenv)
EXPORT_FUNC(setenv)
EXPORT_FUNC(simple_strtoul)
EXPORT_FUNC(strict_strtoul)
EXPORT_FUNC(simple_strtol)
EXPORT_FUNC(strcmp)
EXPORT_FUNC(i2c_write)
EXPORT_FUNC(i2c_read)
EXPORT_FUNC(spi_init)
EXPORT_FUNC(spi_setup_slave)
EXPORT_FUNC(spi_free_slave)
EXPORT_FUNC(spi_claim_bus)
EXPORT_FUNC(spi_release_bus)
EXPORT_FUNC(spi_xfer)
enum {
#define EXPORT_FUNC(x) XF_ ## x ,
#include <_exports.h>
#undef EXPORT_FUNC
	XF_MAX
};

因此,将EXPORT_FUNC(xxx)替换成XF_ ## x后枚举结构如下:

enum {
	XF_get_version,
	XF_getc,
	XF_tstc,
	XF_putc,
	XF_puts,
	XF_printf,
	XF_install_hdlr,
	XF_free_hdlr,
	XF_malloc,
	XF_free,
	XF_udelay,
	XF_get_timer,
	XF_vprintf,
	XF_do_reset,
	XF_getenv,
	XF_setenv,
	XF_simple_strtoul,
	XF_strict_strtoul,
	XF_simple_strtol,
	XF_strcmp,
	XF_i2c_write,
	XF_i2c_read,
	XF_spi_init,
	XF_spi_setup_slave,
	XF_spi_free_slave,
	XF_spi_claim_bus,
	XF_spi_release_bus,
	XF_spi_xfer,
	XF_MAX
};

此时,gd->jt指向刚好合适所有函数地址存放。

/* Reuse _exports.h with a little trickery to avoid bitrot */
#define EXPORT_FUNC(sym) gd->jt[XF_##sym] = (void *)sym;

由于刚才:

#define EXPORT_FUNC(x) XF_ ## x ,
#include <_exports.h>
#undef EXPORT_FUNC

EXPORT_FUNC在这里只是临时定义为XF_ ## x ,所以之后EXPORT_FUNC其实是gd->jt[XF_##sym] = (void *)sym;

因此,jumptable_init里面的#include <_exports.h>实际上如下:

gd->jt[XF_get_version] = (void *)get_version;
gd->jt[XF_getc] = (void *)getc;
gd->jt[XF_tstc] = (void *)tstc;
gd->jt[XF_putc] = (void *)putc;
gd->jt[XF_puts] = (void *)puts;
gd->jt[XF_printf] = (void *)printf;
gd->jt[XF_install_hdlr] = (void *)install_hdlr;
gd->jt[XF_free_hdlr] = (void *)free_hdlr;
gd->jt[XF_malloc] = (void *)malloc;
gd->jt[XF_free] = (void *)free;
gd->jt[XF_udelay] = (void *)udelay;
gd->jt[XF_get_timer] = (void *)get_timer;
gd->jt[XF_vprintf] = (void *)vprintf;
gd->jt[XF_do_reset] = (void *)do_reset;
gd->jt[XF_getenv] = (void *)getenv;
gd->jt[XF_setenv] = (void *)setenv;
gd->jt[XF_simple_strtoul] = (void *)simple_strtoul;
gd->jt[XF_strict_strtoul] = (void *)strict_strtoul;
gd->jt[XF_simple_strtol] = (void *)simple_strtol;
gd->jt[XF_strcmp] = (void *)strcmp;
gd->jt[XF_i2c_write] = (void *)i2c_write;
gd->jt[XF_i2c_read] = (void *)i2c_read;
gd->jt[XF_spi_init] = (void *)spi_init;
gd->jt[XF_spi_setup_slave] = (void *)spi_setup_slave;
gd->jt[XF_spi_free_slave] = (void *)spi_free_slave;
gd->jt[XF_spi_claim_bus] = (void *)claim_bus;
gd->jt[XF_spi_release_bus] = (void *)release_bus;
gd->jt[XF_spi_xfer] = (void *)spi_xferxfer;

hook各自的函数到gd->jt指针。

2.6 console_init_r

/* Called after the relocation - use desired console functions */
int console_init_r(void)
{
	struct stdio_dev *inputdev = NULL, *outputdev = NULL;
	int i;
	struct list_head *list = stdio_get_list();
	struct list_head *pos;
	struct stdio_dev *dev;
#ifdef CONFIG_SPLASH_SCREEN
	/*
	 * suppress all output if splash screen is enabled and we have
	 * a bmp to display. We redirect the output from frame buffer
	 * console to serial console in this case or suppress it if
	 * "silent" mode was requested.
	 */
	if (getenv("splashimage") != NULL) {
		if (!(gd->flags & GD_FLG_SILENT))
			outputdev = search_device (DEV_FLAGS_OUTPUT, "serial");
	}
#endif
	/* Scan devices looking for input and output devices */
	list_for_each(pos, list) {
		dev = list_entry(pos, struct stdio_dev, list);
		if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
			inputdev = dev;
		}
		if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
			outputdev = dev;
		}
		if(inputdev && outputdev)
			break;
	}
	/* Initializes output console first */
	if (outputdev != NULL) {
		console_setfile(stdout, outputdev);
		console_setfile(stderr, outputdev);
#ifdef CONFIG_CONSOLE_MUX
		console_devices[stdout][0] = outputdev;
		console_devices[stderr][0] = outputdev;
#endif
	}
	/* Initializes input console */
	if (inputdev != NULL) {
		console_setfile(stdin, inputdev);
#ifdef CONFIG_CONSOLE_MUX
		console_devices[stdin][0] = inputdev;
#endif
	}
	gd->flags |= GD_FLG_DEVINIT;	/* device initialization completed */
	stdio_print_current_devices();
	/* Setting environment variables */
	for (i = 0; i < 3; i++) {
		setenv(stdio_names[i], stdio_devices[i]->name);
	}
#if 0
	/* If nothing usable installed, use only the initial console */
	if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL))
		return 0;
#endif
	return 0;
}

遍历比标准设备链表,匹配dev->flags = DEV_FLAGS_INPUT和dev->flags = DEV_FLAGS_OUTPUT,寻找标准输入和输出设备。

static int console_setfile(int file, struct stdio_dev * dev)
{
	int error = 0;
	if (dev == NULL)
		return -1;
	switch (file) {
	case stdin:
	case stdout:
	case stderr:
		/* Start new device */
		if (dev->start) {
			error = dev->start();
			/* If it's not started dont use it */
			if (error < 0)
				break;
		}
		/* Assign the new device (leaving the existing one started) */
		stdio_devices[file] = dev;
		/*
		 * Update monitor functions
		 * (to use the console stuff by other applications)
		 */
		switch (file) {
		case stdin:
			gd->jt[XF_getc] = dev->getc;
			gd->jt[XF_tstc] = dev->tstc;
			break;
		case stdout:
			gd->jt[XF_putc] = dev->putc;
			gd->jt[XF_puts] = dev->puts;
			gd->jt[XF_printf] = printf;
			break;
		}
		break;
	default:		/* Invalid file ID */
		error = -1;
	}
	return error;
}

找到之后配置跳转表gd->jt的相关输入和输出函数项的函数指针,指向标准输入和输出设备的驱动函数。

CONFIG_SYS_LOAD_ADDR=0x80000000
ulong load_addr = CONFIG_SYS_LOAD_ADDR;	/* Default Load Address */
	/* Initialize from environment */
	load_addr = getenv_ulong("loadaddr", 16, load_addr);

获取loadaddr的nvironment环境变量,如果有修改的话。

#if defined(CONFIG_CMD_NET)
	{
		char *s = getenv("bootfile");
		if (s != NULL)
			copy_filename(BootFile, s, sizeof(BootFile));
	}
#endif

copy bootfile到BootFile结构。

2.7 misc_init_r

/* Function Name:
 *      misc_init_r
 * Description:
 *      Initialize of misc objects.
 * Input:
 *      None
 * Output:
 *      None
 * Return:
 *      0 - Success
 * Note:
 *      None
 */
int misc_init_r(void)
{
	uint32 chip_index;
	
	/* board probe */
	DBG_PRINT(1, "### Board Probe ###\n");
	
	swcore_probe(&chip_index);
	
	/* int-gpio chip driver init*/
	intGpio_drv_init(chip_index, &gIntGpio);
	
	/* ext-gpio chip driver init*/
	extGpio_drv_init(chip_index, &gExtGpio);
	
	board_probe((rtk_switch_model_t **)&gSwitchModel);
#ifdef __TEST_FT2__
		printf("{B}\n");
		printf("{VL01}\n");
#endif
	
	if (gSwitchModel == NULL)
	{
		printf("Fatal: gSwitchModel is NULL\n");
		return 0;
	}
	/* mac driver init */
	DBG_PRINT(1, "### Mac Driver Init ###\n");
	mac_drv_init(gSwitchModel->chip, (rtk_mac_drv_t **)&gMacDrv);
	if (gMacDrv == NULL)
	{
		printf("Fatal: gMacDrv is NULL\n");
		return 0;
	}
	/* chip config */
	DBG_PRINT(1, "### Chip Config ###\n");
	chip_config(gSwitchModel);
	return 0;
} /* end of misc_init_r */

2.7.1 swcore_probe

void swcore_probe(uint32 *pChip_index)
{
	uint32  i;
	hal_get_chip_id_f f;
	if (pChip_index == NULL)
	{
		OSAL_PRINTF("pChip_index is NULL!\n");
		return;
	}
	
	for (i = 0; i < (sizeof(func)/sizeof(hal_get_chip_id_f)); i++)
	{
		f = (hal_get_chip_id_f) func[i];
		if (0 == ((f)(pChip_index)))
			return;
	}
	
	OSAL_PRINTF("Proper chid ID is not found!\n");
	return;
}
static hal_get_chip_id_f func[] =
{
#if defined(CONFIG_RTL8380)
	_bsp_drv_swcore_cid8380_get,
#endif
#if defined(CONFIG_RTL8390)
	_bsp_drv_swcore_cid8390_get,
#endif
};
#if defined(CONFIG_MISC_INIT_R)
#if defined(CONFIG_RTL8380)
int32 _bsp_drv_swcore_cid8380_get(uint32 *pChip_index)
{
	uint32 temp;
	
	temp = MEM32_READ(SWCORE_BASE_ADDR | RTL8380_MODEL_NAME_INFO_ADDR);
	if ((((temp>>16)&0xFFFF) != 0x8330) && (((temp>>16)&0xFFFF) != 0x8332) &&
		(((temp>>16)&0xFFFF) != 0x8380) && (((temp>>16)&0xFFFF) != 0x8382))
		return 1;
	temp = (temp >> 16) & 0xffff;
	
	switch (temp)
	{
		case 0x8330:
			*pChip_index = RTK_CHIP_RTL8330M;
			return 0;
		case 0x8332:
			*pChip_index = RTK_CHIP_RTL8332M;
			return 0;
		case 0x8380:
			*pChip_index = RTK_CHIP_RTL8380M;
			return 0;
		case 0x8382:
			*pChip_index = RTK_CHIP_RTL8382M;
			return 0;
			
		default:
			return 1;
	}
}
#endif

获取cpu芯片ID,判断cpu型号。

2.7.2 intGpio_drv_init

void intGpio_drv_init(int chip_index, rtk_gpio_drv_t \*\*ppintGpioDrv)
{
	switch (chip_index)
	{
#if defined(CONFIG_RTL8390)
		case RTK_CHIP_RTL8391M:
		case RTK_CHIP_RTL8392M:
		case RTK_CHIP_RTL8393M:
		case RTK_CHIP_RTL8396M:
		case RTK_CHIP_RTL8353M:
		rtl8390_intGpio_drv.intGpio_pindata_get = rtl8390_intGpio_pindata_get;
			rtl8390_intGpio_drv.intGpio_pindata_set = rtl8390_intGpio_pindata_set;
			(*ppintGpioDrv) = &rtl8390_intGpio_drv;
			break;
#endif /* end of #if defined(CONFIG_RTL8390) */
#if defined(CONFIG_RTL8380)
		case RTK_CHIP_RTL8382M:
		case RTK_CHIP_RTL8380M:
		case RTK_CHIP_RTL8332M:
		case RTK_CHIP_RTL8330M:
			rtl8380_intGpio_drv.intGpio_pindata_get = rtl8380_intGpio_pindata_get;
			rtl8380_intGpio_drv.intGpio_pindata_set = rtl8380_intGpio_pindata_set;
			(*ppintGpioDrv) = &rtl8380_intGpio_drv;
			
			break;
#endif /* end of #if defined(CONFIG_RTL8380) */
		default:
			break;
	}
}

hook rtl8380_intGpio_pindata_set和rtl8380_intGpio_pindata_get到rtl8380_intGpio_drv,并返回其到ppintGpioDrv。

2.7.2 extGpio_drv_init

void extGpio_drv_init(int chip_index, rtk_extGpio_drv_t \*\*ppExtGpioDrv)
{
	switch (chip_index)
	{
#if defined(CONFIG_RTL8390)
		case RTK_CHIP_RTL8391M:
		case RTK_CHIP_RTL8392M:
		case RTK_CHIP_RTL8393M:
		case RTK_CHIP_RTL8396M:
		case RTK_CHIP_RTL8353M:
			rtl8390_drv.extGpio_init = rtl8390_rtl8231_init;
			rtl8390_drv.extGpio_reg_read = rtl8390_rtl8231_read;
			rtl8390_drv.extGpio_reg_write = rtl8390_rtl8231_write;
			(*ppExtGpioDrv) = &rtl8390_drv;
			break;
#endif /* end of #if defined(CONFIG_RTL8390) */
#if defined(CONFIG_RTL8380)
		case RTK_CHIP_RTL8382M:
		case RTK_CHIP_RTL8380M:
		case RTK_CHIP_RTL8332M:
		case RTK_CHIP_RTL8330M:
			rtl8380_drv.extGpio_init = rtl8380_rtl8231_init;
			rtl8380_drv.extGpio_reg_read = rtl8380_rtl8231_read;
			rtl8380_drv.extGpio_reg_write = rtl8380_rtl8231_write;
			(*ppExtGpioDrv) = &rtl8380_drv;
			break;
#endif /* end of #if defined(CONFIG_RTL8380) */
		default:
			break;
	}
}

hook rtl8380_rtl8231_init,rtl8380_rtl8231_read和rtl8380_rtl8231_write到rtl8380_intGpio_drv,并返回其到ppintGpioDrv。

2.7.3 board_probe

int board_probe(rtk_switch_model_t \*\*pSwitchModel)
{
#if defined(CONFIG_CUSTOMER_BOARD)
	customer_board_probe(pSwitchModel);
	if (*pSwitchModel != NULL)
		return 0;
#endif
#if defined(CONFIG_RTK_BOARD)
	rtk_board_probe(pSwitchModel);
	if (*pSwitchModel != NULL)
		return 0;
#endif
	
	return 0;
} /* end of board_probe */
int rtk_board_probe(rtk_switch_model_t \*\*pSwitchModel)
{
	rtk_chiptype_t  chip_type;
	char    *board_model;
	board_model = getenv(ENV_BOARD_MODEL);
#if defined(CONFIG_RTL8380)
......
#if defined(CONFIG_RTL8382M_8218B_INTPHY_8218B_8214FC_DEMO)
		if (board_model && ((0 == strcmp(board_model, "RTL8382M_8218B_INTPHY_8218B_8214FC_DEMO"))))
		{
			(*pSwitchModel) = (rtk_switch_model_t *)&rtl8382m_8218b_intphy_8218b_8214fc_demo;
		}
		else
#endif
......
#endif
......
	return 0;
} /* end of rtk_board_probe */
static const rtk_switch_model_t rtl8382m_8218b_intphy_8218b_8214fc_demo = {
	.name = "RTL8382M_8218B_INTPHY_8218B_8214FC_DEMO",
	.chip = RTK_CHIP_RTL8382M,
	.led.offset = 0,
	.led.count = 28,
	.led.num = 2,
	.led.p0_p23_led_num = 2,
	.led.p24_p27_led_num = 2,
	.led.sel_pwr_on_led = 0xd,
	.led.sel_p0_p23_led_mod = 0x1ea,
	.led.sel_p24_p27_led_mod = 0x1ea,
	.port.offset = 0,  /*Should Always be zero*/
	.port.count = 28,
	.port.list = {
		/* Port 0 ~ 7 */
		{ .mac_id = 0,  .phy_idx = 0, .phy = 0 },
		{ .mac_id = 1,  .phy_idx = 0, .phy = 1 },
		{ .mac_id = 2,  .phy_idx = 0, .phy = 2 },
		{ .mac_id = 3,  .phy_idx = 0, .phy = 3 },
		{ .mac_id = 4,  .phy_idx = 0, .phy = 4 },
		{ .mac_id = 5,  .phy_idx = 0, .phy = 5 },
		{ .mac_id = 6,  .phy_idx = 0, .phy = 6 },
		{ .mac_id = 7,  .phy_idx = 0, .phy = 7 },
		/* Port 8 ~ 15 */
		{ .mac_id = 8,  .phy_idx = 1, .phy = 0 },
		{ .mac_id = 9,  .phy_idx = 1, .phy = 1 },
		{ .mac_id = 10, .phy_idx = 1, .phy = 2 },
		{ .mac_id = 11, .phy_idx = 1, .phy = 3 },
		{ .mac_id = 12, .phy_idx = 1, .phy = 4 },
		{ .mac_id = 13, .phy_idx = 1, .phy = 5 },
		{ .mac_id = 14, .phy_idx = 1, .phy = 6 },
		{ .mac_id = 15, .phy_idx = 1, .phy = 7 },
		/* Port 16 ~ 23 */
		{ .mac_id = 16, .phy_idx = 2, .phy = 0 },
		{ .mac_id = 17, .phy_idx = 2, .phy = 1 },
		{ .mac_id = 18, .phy_idx = 2, .phy = 2 },
		{ .mac_id = 19, .phy_idx = 2, .phy = 3 },
		{ .mac_id = 20, .phy_idx = 2, .phy = 4 },
		{ .mac_id = 21, .phy_idx = 2, .phy = 5 },
		{ .mac_id = 22, .phy_idx = 2, .phy = 6 },
		{ .mac_id = 23, .phy_idx = 2, .phy = 7 },
		/* Port 24~ 27 */
		{ .mac_id = 24, .phy_idx = 3, .phy = 0 },
		{ .mac_id = 25, .phy_idx = 3, .phy = 1 },
		{ .mac_id = 26, .phy_idx = 3, .phy = 2 },
		{ .mac_id = 27, .phy_idx = 3, .phy = 3 },
	},  /* port.list */
	.serdes.offset = 0,
	.serdes.count = 6,
	.serdes.list = {
		{ .phy_idx = 0, .mii = RTK_MII_QSGMII, .rx_polarity = SERDES_POLARITY_NORMAL, .tx_polarity = SERDES_POLARITY_NORMAL },
		{ .phy_idx = 0, .mii = RTK_MII_QSGMII, .rx_polarity = SERDES_POLARITY_NORMAL, .tx_polarity = SERDES_POLARITY_NORMAL },
		{ .phy_idx = 2, .mii = RTK_MII_QSGMII, .rx_polarity = SERDES_POLARITY_NORMAL, .tx_polarity = SERDES_POLARITY_NORMAL },
		{ .phy_idx = 2, .mii = RTK_MII_QSGMII, .rx_polarity = SERDES_POLARITY_NORMAL, .tx_polarity = SERDES_POLARITY_NORMAL },
		{ .phy_idx = 3, .mii = RTK_MII_QSGMII, .rx_polarity = SERDES_POLARITY_NORMAL, .tx_polarity = SERDES_POLARITY_NORMAL },
		{ .phy_idx = 3, .mii = RTK_MII_NONE, .rx_polarity = SERDES_POLARITY_NORMAL, .tx_polarity = SERDES_POLARITY_NORMAL },
	},  /* serdes.list */
	.phy.baseid = 0,
	.phy.count = 4,
	.phy.list = {
		[0] =   { .chip = RTK_CHIP_RTL8218B, .mac_id = 0,  .phy_max = 8 },
		[1] =   { .chip = RTK_CHIP_RTL8218B, .mac_id = 8,  .phy_max = 8 },
		[2] =   { .chip = RTK_CHIP_RTL8218B, .mac_id = 16,  .phy_max = 8 },
		[3] =   { .chip = RTK_CHIP_RTL8214FC, .mac_id = 24,  .phy_max = 4 },
	}   /* .phy.list */
};

省略不执行的部分,此函数唯一的目的就是将rtl8382m_8218b_intphy_8218b_8214fc_demo结构赋值给gSwitchModel,判断并初始化switch体系结构。

2.7.4 mac_drv_init

int mac_drv_init(int chip_index, rtk_mac_drv_t \*\*ppMacDrv)
{
	switch (chip_index)
	{
#if defined(CONFIG_RTL8328)
		case RTK_CHIP_RTL8328M:
		case RTK_CHIP_RTL8328S:
		case RTK_CHIP_RTL8328L:
			rtl8328_drv.miim_max_page = 127;
			rtl8328_drv.drv_probe = NULL;
			rtl8328_drv.drv_miim_read = rtl8328_getPhyReg;
			rtl8328_drv.drv_miim_write = rtl8328_setPhyReg;
			rtl8328_drv.drv_miim_portmask_write = rtl8328_setPhyRegByMask;
			(*ppMacDrv) = &rtl8328_drv;
			break;
#endif /* end of #if defined(CONFIG_RTL8328) */
#if defined(CONFIG_RTL8390)
		case RTK_CHIP_RTL8391M:
		case RTK_CHIP_RTL8392M:
		case RTK_CHIP_RTL8393M:
		case RTK_CHIP_RTL8396M:
		case RTK_CHIP_RTL8353M:
			rtl8390_drv.miim_max_page = 0x1FFF;
			rtl8390_drv.drv_probe = NULL;
			rtl8390_drv.drv_miim_read = rtl8390_getPhyReg;
			rtl8390_drv.drv_miim_write = rtl8390_setPhyReg;
			rtl8390_drv.drv_miim_portmask_write = rtl8390_setPhyRegByMask;
			(*ppMacDrv) = &rtl8390_drv;
			break;
#endif /* end of #if defined(CONFIG_RTL8390) */
#if defined(CONFIG_RTL8380)
		case RTK_CHIP_RTL8382M:
		case RTK_CHIP_RTL8380M:
		case RTK_CHIP_RTL8332M:
		case RTK_CHIP_RTL8330M:
			rtl8380_drv.miim_max_page = 4095;
			rtl8380_drv.drv_probe = NULL;
			rtl8380_drv.drv_miim_read = rtl8380_getPhyReg;
			rtl8380_drv.drv_miim_write = rtl8380_setPhyReg;
			rtl8380_drv.drv_miim_portmask_write = rtl8380_setPhyRegByMask;
			(*ppMacDrv) = &rtl8380_drv;
			break;
#endif /* end of #if defined(CONFIG_RTL8380) */
		default:
			break;
	}
	return 0;
} /* end of mac_drv_init */

hook rtl8380_getPhyReg,rtl8380_setPhyReg和rtl8380_setPhyRegByMask到rtl8380_drv,并返回其到gMacDrv,初始化phy register的读写函数。

2.7.5 chip_config

void chip_config(const rtk_switch_model_t \*pModel)
{
	Tuint32 i;
	if (pModel == NULL)
		return;
	OSAL_PRINTF("Switch Model: %s (Port Count: %d)\n", pModel->name, pModel->port.count);
	OSAL_PRINTF("Switch Chip: %s\n", \
		(pModel->chip == RTK_CHIP_RTL8328M)?  "RTL8328M" : \
		(pModel->chip == RTK_CHIP_RTL8328S)?  "RTL8328S" : \
		(pModel->chip == RTK_CHIP_RTL8328L)?  "RTL8328L" : \
		(pModel->chip == RTK_CHIP_RTL8352M)?  "RTL8352M" : \
		(pModel->chip == RTK_CHIP_RTL8353M)?  "RTL8353M" : \
		(pModel->chip == RTK_CHIP_RTL8391M)?  "RTL8391M" : \
		(pModel->chip == RTK_CHIP_RTL8392M)?  "RTL8392M" : \
		(pModel->chip == RTK_CHIP_RTL8393M)?  "RTL8393M" : \
		(pModel->chip == RTK_CHIP_RTL8396M)?  "RTL8396M" : \
		(pModel->chip == RTK_CHIP_RTL8382M)?  "RTL8382M" : \
		(pModel->chip == RTK_CHIP_RTL8332M)?  "RTL8332M" : \
		(pModel->chip == RTK_CHIP_RTL8380M)?  "RTL8380M" : \
		(pModel->chip == RTK_CHIP_RTL8330M)?  "RTL8330M" : \
		"Unknown");
	for (i=0; i<pModel->phy.count; i++)
	{
		DBG_PRINT(1, "  PHY[%d] %-8s : macid = %2d, phy_max = %d\n", i, \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8208)?  "RTL8208" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8208D)?  "RTL8208D" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8208L)?  "RTL8208L" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8214)?  "RTL8214" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8214F)? "RTL8214F" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8214FB)? "RTL8214FB" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8214FC)? "RTL8214FC" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8214B)? "RTL8214B" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8218)?  "RTL8218" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8212F)?  "RTL8212F" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8212B)?  "RTL8212B" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8218B)?  "RTL8218B" : \
			(pModel->phy.list[i].chip == RTK_CHIP_RTL8218FB)?  "RTL8218FB" : \
			"Unknown",
			pModel->phy.list[i].mac_id, pModel->phy.list[i].phy_max);
	}
	switch (pModel->chip)
	{
#if defined(CONFIG_RTL8328)
	case RTK_CHIP_RTL8328M:
	case RTK_CHIP_RTL8328S:
	case RTK_CHIP_RTL8328L:
		rtl8328_config(pModel);
		break;
#endif /* end of #if defined(CONFIG_RTL8328) */
#if defined(CONFIG_RTL8390)
	case RTK_CHIP_RTL8352M:
	case RTK_CHIP_RTL8353M:
	case RTK_CHIP_RTL8391M:
	case RTK_CHIP_RTL8392M:
	case RTK_CHIP_RTL8393M:
	case RTK_CHIP_RTL8396M:
		rtl8390_config(pModel);
		break;
#endif /* end of #if defined(CONFIG_RTL8328) */
#if defined(CONFIG_RTL8380)
	case RTK_CHIP_RTL8382M:
	case RTK_CHIP_RTL8332M:
	case RTK_CHIP_RTL8380M:
	case RTK_CHIP_RTL8330M:
		rtl8380_config(pModel);
		break;
#endif /* end of #if defined(CONFIG_RTL8380) */
	default:
		break;
	}
	return;
} /* end of chip_config */

打印芯片型号,配置8383m。

void rtl8380_config(const rtk_switch_model_t \*pModel)
{
	rtl8380_version_check();
	if(rtl8380_version >= RTL8380_VERSION_A)
	{
#if CONFIG_RTL8380_EQC_TEST
		/*For  EQC Use*/
		rtl8380_eqc_test_config();
#else
#if defined(CONFIG_MDC_MDIO_EXT_SUPPORT)
	rtl8231_init();	
#endif
		/*Disable MAC Polling PHY*/
		rtl8380_disable_polling_phy();
		DBG_PRINT(1, "### Platform Config ###\n");
		rtl8380_platform_config_init(gSwitchModel);
		/*MAC Configration*/
		DBG_PRINT(1, "### MAC Config ###\n");
		rtl8380_mac_config_init(gSwitchModel);
#if defined(CONFIG_CUSTOMER_BOARD)
		/*Customer MAC Config*/
		DBG_PRINT(1, "### Customer MAC Config ###\n");
		customer_mac_config_init(gSwitchModel);
#endif
		/*Serdes Patch*/
		DBG_PRINT(1, "### Intra-Serdes Config ###\n");
		rtl8380_intraSerdes_config_init(gSwitchModel);
		/*Phy Patch*/
		DBG_PRINT(1, "### PHY Config (RTL82XX) ###\n");
		rtl8380_phy_config_init(gSwitchModel);
#if defined(CONFIG_CUSTOMER_BOARD)
		/*Customer PHY Config*/
		DBG_PRINT(1, "### Customer PHY Config (RTL82XX) ###\n");
		customer_phy_config_init(gSwitchModel);
#endif
		DBG_PRINT(1, "### Misc Config ###\n");
		rtl8380_misc_config_init(gSwitchModel);
#if defined(CONFIG_CUSTOMER_BOARD)
		DBG_PRINT(1, "### Customer Misc Config ###\n");
		customer_mac_misc_config_init(gSwitchModel);
#endif
		/*Enable MAC Polling PHY*/
		rtl8380_enable_polling_phy(gSwitchModel);
		/*Phy power down*/
		rtl8380_phyPowerOff();
#if defined(CONFIG_RTL8380_OLT_TEST)
		  rtl8380_olt_loop();
#endif
		
#endif
	}
	return;
} /* end of rtl8380_config */
  1. 检查芯片版本
  2. 禁用MAC(media access control,媒体访问控制器)轮询PHY(physical layer物理层)
  3. Platform Config 3.1 Store the MAC address info specify registers 3.2 Write to another two duplicate registers, ALE/MAC block
  4. MAC Config 4.1 Set Port28-CPU Port Egress Drop always enable 4.2 Serdes: change tx & rx polarity 4.3 LED Settings 4.4 Giga Ability & PortID 4.5 MAC Patch 4.6 patch mantis#0013049
  5. Intra-Serdes Config 5.1 Back up serdes power down value 5.2 serdes software reset take own ship 5.3 Serdes Common Patch 5.4 Enable Internal Read/Write 5.5 MAC Serdes Interface Settings 5.6 Serdes Module Settings 5.7 MAC Serdes force linkdown Settings
  6. PHY Config (RTL82XX) 6.1 Now External 8218B 6.2 Now Internal PHY 6.3 Now External 8218B 6.4 Now External 8214FC
  7. Misc Config:IEEE off
  8. Enable MAC Polling PHY

2.8 soc_post_init

static inline void soc_post_init(void) {
	const char sf_probe_cmd[] = {"sf probe 0"};
	/* The `baudrate' env was originally set by
	   CONFIG_BAUDRATE. Since we removed CONFIG_BAUDRATE, we
	   set baudrate env manually. */
	if (getenv_ulong("baudrate", 10, 0xFFFFFFFF) == 0xFFFFFFFF) {
		setenv_ulong("baudrate", gd->baudrate);
	}
	/* Issue `sf probe 0' before entering CLI. Since our dirver
	   automatically switches to next CS when access across CS
	   boundary, it is lousy to do `sf probe 0' before other
	   `sf' commands can be used. */
	run_command(sf_probe_cmd, 0);
	return;
}

执行sf probe 0命令。

2.9 run_command

int run_command (const char *cmd, int flag)
{
	cmd_tbl_t *cmdtp;
	char cmdbuf[CONFIG_SYS_CBSIZE];	/* working copy of cmd		*/
	char *token;			/* start of token in cmdbuf	*/
	char *sep;			/* end of token (separator) in cmdbuf */
	char finaltoken[CONFIG_SYS_CBSIZE];
	char *str = cmdbuf;
	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated	*/
	int argc, inquotes;
	int repeatable = 1;
	int rc = 0;
	strcpy (cmdbuf, cmd);
	while (*str) 
	{
		/*
		 * Find separator, or string end
		 * Allow simple escape of ';' by writing "\;"
		 */
		for (inquotes = 0, sep = str; *sep; sep++) 
		{
			if ((*sep=='\'') &&
				(*(sep-1) != '\\'))
				inquotes=!inquotes;
			if (!inquotes &&
				(*sep == ';') &&	/* separator		*/
				( sep != str) &&	/* past string start	*/
				(*(sep-1) != '\\'))	/* and NOT escaped	*/
				break;
		}
		/*
		 * Limit the token to data between separators
		 */
		token = str;
		if (*sep) 
		{
			str = sep + 1;	/* start of command for next pass */
			*sep = '\0';
		}
		else
			str = sep;	/* no more commands for next pass */
		/* find macros in this token and replace them */
		process_macros (token, finaltoken);
		/* Extract arguments */
		if ((argc = parse_line (finaltoken, argv)) == 0) 
		{
			rc = -1;	/* no command at all */
			continue;
		}
		/* Look up command in command table */
		if ((cmdtp = find_cmd(argv[0])) == NULL) 
		{
			printf ("Unknown command '%s' - try 'help'\n", argv[0]);
			rc = -1;	/* give up after bad command */
			continue;
		}
		/* found - check max args */
		if (argc > cmdtp->maxargs) 
		{
			cmd_usage(cmdtp);
			rc = -1;
			continue;
		}
#if defined(CONFIG_CMD_BOOTD)
		/* avoid "bootd" recursion */
		if (cmdtp->cmd == do_bootd) 
		{
			if (flag & CMD_FLAG_BOOTD) 
			{
				puts ("'bootd' recursion detected\n");
				rc = -1;
				continue;
			} 
			else 
			{
				flag |= CMD_FLAG_BOOTD;
			}
		}
#endif
		/* OK - call function to do the command */
		if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) 
		{
			rc = -1;
		}
		repeatable &= cmdtp->repeatable;
		/* Did the user stop this? */
		if (had_ctrlc ())
			return -1;	/* if stopped then not repeatable */
	}
	return rc ? rc : repeatable;
}

1. run_command接受的命令格式如下:

rtk network on;bootm 0xb42a0000

2. line1250,while循环将输入的多行命令行字符串整理成由“;”分割成的单行命令处理。

3. 每一次循环的token代表了当前正在处理的命令的指针,例如:

第一次循环:token = “rtk network on” 第二次循环:token = “bootm 0xb42a0000”

4. process_macros将token指向的当前命令转换成可以被识别的形式finaltoken

5. parse_line将finaltoken指向的命令字符串转换成多个参数形式保存,参数数组为argv,例如:

argv[0] = "rtk"
argv[1] = "network"
argv[1] = "on"

6. find_cmd在__u_boot_cmd_start~__u_boot_cmd_end表中寻找和argv[0]匹配的命令,如找到,则返回命令的具体信息及执行函数,例如:

cmdtp
struct cmd_tbl_s {
	char		*name;		/* Command Name			*/
	int		maxargs;	/* maximum number of arguments	*/
	int		repeatable;	/* autorepeat allowed?		*/
					/* Implementation function	*/
	int		(*cmd)(struct cmd_tbl_s *, int, int, char * const []);
	char		*usage;		/* Usage message	(short)	*/
#ifdef	CONFIG_SYS_LONGHELP
	char		*help;		/* Help  message	(long)	*/
#endif
#ifdef CONFIG_AUTO_COMPLETE
	/* do auto completion on the arguments */
	int		(*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

7. argc > cmdtp->maxargs检查命令参数最大值是否小于当前输入的参数个数

8. (cmdtp->cmd) (cmdtp, flag, argc, argv),执行命令结构指向的命令函数

2.10 eth_initialize

rtl8380_initialize:

int rtl8380_initialize(bd_t *bis)
{
	struct eth_device *dev;
	/* Alloc memory to ethernet device info */
	dev = (struct eth_device *)malloc(sizeof *dev);
	if (NULL == dev)
	{
		return 0;   /* No device (Out of memory) */
	}
	/* Set the ethernet device info */
	sprintf(dev->name, "rtl8380#0");
	dev->priv = NULL;
	dev->iobase = 0;
	dev->init = swnic_init;
	dev->halt = swnic_halt;
	dev->send = swnic_send;
	dev->recv = swnic_recv;
	dev->write_hwaddr = NULL;
	/* Register the ethernet device */
	eth_register(dev);
	return 1;    /* Only one device */
} /* end of rtl8380_initialize */

将switch注册到标准设备链表。

六、主循环main_loop

1. source code

void main_loop (void)
{
#ifndef CONFIG_SYS_HUSH_PARSER
	static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
	int len;
	int rc = 1;
	int flag;
#endif
#if defined(CONFIG_BOOTDELAY)
	char *s;
	int bootdelay;
#endif
#if defined(CONFIG_BOOTDELAY)
	s = getenv ("bootdelay");
	bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
	debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);
		s = getenv ("consume input");
	debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
	if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
		run_command2(s, 0);
	}
#endif /* CONFIG_BOOTDELAY */
	sprintf((char *)prompt_str,"%s# ",CONFIG_SYS_PROMPT);
	/*
	 * Main Loop for Monitor Command Processing
	 */
	for (;;) 
	{
		len = readline ((const char *)prompt_str);
		flag = 0;	/* assume no special flags for now */
		if (len > 0)
			strcpy (lastcommand, console_buffer);
		else if (len == 0)
			flag |= CMD_FLAG_REPEAT;
		if (len == -1)
			puts ("<INTERRUPT>\n");
		else
			rc = run_command (lastcommand, flag);
		if (rc <= 0) 
		{
			/* invalid command or not repeatable, forget it */
			lastcommand[0] = 0;
		}
	}
}

经过删减,去除不执行代码。

2. bootdelay

s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

获取倒计时时间bootdelay.

3. consume input”

s = getenv ("consume input");

获取在自动加载模式将要执行的命令,猜测是rtk network on; bootm 0xb42a0000。

4. abortboot和run_command2

if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
	run_command2(s, 0);
}

abortboot由bootdelay开始倒计时,如果在倒计时过程中遇到输入,则执行run_command2,命令是s,也就是环境变量consume input的值,因为是rtk network on; bootm 0xb42a0000,所以,先启用网络,然后加载内核。

......
Hit any key to stop autoboot:  0 
Enable network
Force port28 link up 1G
Please wait for PHY init-time ...
## Booting kernel from Legacy Image at b42a0000 ...
......

5. RTL838x#

#define CONFIG_SYS_PROMPT  "RTL838x# " /* Monitor Command Prompt */
sprintf((char *)prompt_str,"%s# ",CONFIG_SYS_PROMPT);

输出字符串“RTL838x#”。

6. run_command

for (;;) 
{
	len = readline ((const char *)prompt_str);
	flag = 0;	/* assume no special flags for now */
	if (len > 0)
		strcpy (lastcommand, console_buffer);
	else if (len == 0)
		flag |= CMD_FLAG_REPEAT;
	if (len == -1)
		puts ("<INTERRUPT>\n");
	else
		rc = run_command (lastcommand, flag);
	if (rc <= 0) 
	{
		/* invalid command or not repeatable, forget it */
		lastcommand[0] = 0;
	}
}

在for (;;)中接受输入,并执行命令。

七、命令行及其参数

1. 环境变量初始化

const uchar default_environment[] = {
#if defined(CONFIG_BOARDVERSION)
	"boardversion="	CONFIG_BOARDVERSION		"\0"
#endif
#if defined(CONFIG_BOARDMODEL)
	"boardmodel="	CONFIG_BOARDMODEL		"\0"
#endif
#ifdef	CONFIG_BOOTARGS
	"bootargs="	CONFIG_BOOTARGS			"\0"
#endif
#ifdef	CONFIG_BOOTCOMMAND
	"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
#endif
#ifdef	CONFIG_RAMBOOTCOMMAND
	"ramboot="	CONFIG_RAMBOOTCOMMAND		"\0"
#endif
#ifdef	CONFIG_NFSBOOTCOMMAND
	"nfsboot="	CONFIG_NFSBOOTCOMMAND		"\0"
#endif
#if defined(CONFIG_BOOTDELAY)
	"bootdelay="	MK_STR(CONFIG_BOOTDELAY)	"\0"
#endif
/* Assign the baudrate to the default value at init_baudrate()
   to fix br@default and br@tcl/soc_t mismatch issue
 */ 
//#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
//	"baudrate="	MK_STR(CONFIG_BAUDRATE)		"\0"
//#endif
#ifdef	CONFIG_LOADS_ECHO
	"loads_echo="	MK_STR(CONFIG_LOADS_ECHO)	"\0"
#endif
#ifdef	CONFIG_ETHADDR
	"ethaddr="	MK_STR(CONFIG_ETHADDR)		"\0"
#endif
#ifdef	CONFIG_ETH1ADDR
	"eth1addr="	MK_STR(CONFIG_ETH1ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH2ADDR
	"eth2addr="	MK_STR(CONFIG_ETH2ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH3ADDR
	"eth3addr="	MK_STR(CONFIG_ETH3ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH4ADDR
	"eth4addr="	MK_STR(CONFIG_ETH4ADDR)		"\0"
#endif
#ifdef	CONFIG_ETH5ADDR
	"eth5addr="	MK_STR(CONFIG_ETH5ADDR)		"\0"
#endif
#ifdef	CONFIG_IPADDR
	"ipaddr="	MK_STR(CONFIG_IPADDR)		"\0"
#endif
#ifdef	CONFIG_SERVERIP
	"serverip="	MK_STR(CONFIG_SERVERIP)		"\0"
#endif
#ifdef	CONFIG_SYS_AUTOLOAD
	"autoload="	CONFIG_SYS_AUTOLOAD		"\0"
#endif
#ifdef	CONFIG_PREBOOT
	"preboot="	CONFIG_PREBOOT			"\0"
#endif
#ifdef	CONFIG_ROOTPATH
	"rootpath="	CONFIG_ROOTPATH			"\0"
#endif
#ifdef	CONFIG_GATEWAYIP
	"gatewayip="	MK_STR(CONFIG_GATEWAYIP)	"\0"
#endif
#ifdef	CONFIG_NETMASK
	"netmask="	MK_STR(CONFIG_NETMASK)		"\0"
#endif
#ifdef	CONFIG_BAUDRATE
	"baudrate="	MK_STR(CONFIG_BAUDRATE)		"\0"
#endif
#ifdef	CONFIG_HOSTNAME
	"hostname="	MK_STR(CONFIG_HOSTNAME)		"\0"
#endif
#ifdef	CONFIG_BOOTFILE
	"bootfile="	CONFIG_BOOTFILE			"\0"
#endif
#ifdef	CONFIG_LOADADDR
	"loadaddr="	MK_STR(CONFIG_LOADADDR)		"\0"
#endif
#ifdef	CONFIG_CLOCKS_IN_MHZ
	"clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
	"pcidelay="	MK_STR(CONFIG_PCI_BOOTDELAY)	"\0"
#endif
#ifdef	CONFIG_POSTWORD
	"postword="	CONFIG_POSTWORD			"\0"
#endif
#ifdef	CONFIG_EXTRA_ENV_SETTINGS
	CONFIG_EXTRA_ENV_SETTINGS
#endif
	"\0"
};

命令结构体。

/*
 * Initialize Environment use
 *
 * We are still running from ROM, so data use is limited
 */
int env_init(void)
{
	gd->env_addr	= (ulong)&default_environment[0];
	gd->env_valid	= 0;
	return 0;
}
init_fnc_t *init_sequence[] = {
	board_early_init_f,
	timer_init,
	env_init,		/* initialize environment */
	//init_baudrate,		/* initialize baudrate settings */
	//serial_init,		/* serial communications setup */
	//console_init_f,
	display_banner,		/* say that we are here */
	checkboard,
	init_func_ram,
	NULL,
};

初始化序列中的env_init。

由于default_environment结构保存在flash中,因此,gd->env_addr指向flash,

2. U_BOOT_CMD

#define U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \
	{#name, maxargs, rep, cmd, usage, _CMD_HELP(help) _CMD_COMPLETE(comp)}
#define U_BOOT_CMD_MKENT(name,maxargs,rep,cmd,usage,help) \
	U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,NULL)
#define U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,comp) \
	cmd_tbl_t __u_boot_cmd_##name Struct_Section = \
		U_BOOT_CMD_MKENT_COMPLETE(name,maxargs,rep,cmd,usage,help,comp)
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
	U_BOOT_CMD_COMPLETE(name,maxargs,rep,cmd,usage,help,NULL)
#define Struct_Section  __attribute__((unused, section(".u_boot_cmd"), \
		aligned(4)))

可以大致看出,U_BOOT_CMD指令的功能是在flash中的.u_boot_cmd段申请一段空间存放命令行及其参数和执行函数地址,可以简单理解为申请变量的过程,只不过是申请在flash的环境变量区域中,也就是:

extern cmd_tbl_t  `__u_boot_cmd_start`;
extern cmd_tbl_t  `__u_boot_cmd_end`;

这两个地址的中间区域。

3. 其他命令的申请

U_BOOT_CMD(
	go, CONFIG_SYS_MAXARGS, 1,	do_go,
	"start application at address 'addr'",
	"addr [arg ...]\n    - start application at address 'addr'\n"
	"      passing 'arg' as arguments"
);
U_BOOT_CMD(
	reset, 1, 0,	do_reset,
	"Perform RESET of the CPU",
	""
);
U_BOOT_CMD(
	bootm,	CONFIG_SYS_MAXARGS,	1,	do_bootm,
	"boot application image from memory",
	"[addr [arg ...]]\n    - boot application image stored in memory\n"
	"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
	"\t'arg' can be the address of an initrd image\n"
#if defined(CONFIG_OF_LIBFDT)
	"\tWhen booting a Linux kernel which requires a flat device-tree\n"
	"\ta third argument is required which is the address of the\n"
	"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
	"\tuse a '-' for the second argument. If you do not pass a third\n"
	"\ta bd_info struct will be passed instead\n"
#endif
#if defined(CONFIG_FIT)
	"\t\nFor the new multi component uImage format (FIT) addresses\n"
	"\tmust be extened to include component or configuration unit name:\n"
	"\taddr:<subimg_uname> - direct component image specification\n"
	"\taddr#<conf_uname>   - configuration specification\n"
	"\tUse iminfo command to get the list of existing component\n"
	"\timages and configurations.\n"
#endif
	"\nSub-commands to do part of the bootm sequence.  The sub-commands "
	"must be\n"
	"issued in the order below (it's ok to not issue all sub-commands):\n"
	"\tstart [addr [arg ...]]\n"
	"\tloados  - load OS image\n"
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_SPARC)
	"\tramdisk - relocate initrd, set env initrd_start/initrd_end\n"
#endif
#if defined(CONFIG_OF_LIBFDT)
	"\tfdt     - relocate flat device tree\n"
#endif
	"\tcmdline - OS specific command line processing/setup\n"
	"\tbdt     - OS specific bd_t processing\n"
	"\tprep    - OS specific prep before relocation or go\n"
	"\tgo      - start OS"
);
U_BOOT_CMD(
	iminfo,	CONFIG_SYS_MAXARGS,	1,	do_iminfo,
	"print header information for application image",
	"addr [addr ...]\n"
	"    - print header information for application image starting at\n"
	"      address 'addr' in memory; this includes verification of the\n"
	"      image contents (magic number, header and payload checksums)"
);
U_BOOT_CMD(
	boota, 2, 0, do_boota,
	"boota  - boot application image from one of dual images partition automatically\n",
	"index [index ...]\n"
	"    - boot application image from one of dual images partition\n"
	"      automatically; 'index' allow you to force on selected partition\n"
);
U_BOOT_CMD(
	boardid, 2, 0, do_boardid,
	"boardid  - Get/Set board model id\n",
	"id       - Board model id\n"
);
U_BOOT_CMD(
	sf,	2,	1,	do_serial_flash,
	"Serial flash sub-system",
	"probe [bus:]cs		- init flash device on given SPI bus and CS")
U_BOOT_CMD(
	flinfo,    2,    1,    do_flinfo,
	"print FLASH memory information",
	"\n    - print information for all FLASH memory banks\n"
	"flinfo N\n    - print information for FLASH memory bank # N"
);
U_BOOT_CMD(
	erase,   3,   0,  do_flerase,
	"erase FLASH memory",
	"start end\n"
	"    - erase FLASH from addr 'start' to addr 'end'\n"
	"erase start +len\n"
	"    - erase FLASH from addr 'start' to the end of sect "
	"w/addr 'start'+'len'-1\n"
	"erase N:SF[-SL]\n    - erase sectors SF-SL in FLASH bank # N\n"
	"erase bank N\n    - erase FLASH bank # N\n"
	TMP_ERASE
	"erase all\n    - erase all FLASH banks"
);
U_BOOT_CMD(
	protect,  4,  0,   do_protect,
	"enable or disable FLASH write protection",
	"on  start end\n"
	"    - protect FLASH from addr 'start' to addr 'end'\n"
	"protect on start +len\n"
	"    - protect FLASH from addr 'start' to end of sect "
	"w/addr 'start'+'len'-1\n"
	"protect on  N:SF[-SL]\n"
	"    - protect sectors SF-SL in FLASH bank # N\n"
	"protect on  bank N\n    - protect FLASH bank # N\n"
	TMP_PROT_ON
	"protect on  all\n    - protect all FLASH banks\n"
	"protect off start end\n"
	"    - make FLASH from addr 'start' to addr 'end' writable\n"
	"protect off start +len\n"
	"    - make FLASH from addr 'start' to end of sect "
	"w/addr 'start'+'len'-1 wrtable\n"
	"protect off N:SF[-SL]\n"
	"    - make sectors SF-SL writable in FLASH bank # N\n"
	"protect off bank N\n    - make FLASH bank # N writable\n"
	TMP_PROT_OFF
	"protect off all\n    - make all FLASH banks writable"
);
U_BOOT_CMD(
	help,	CONFIG_SYS_MAXARGS,	1,	do_help,
	"print command description/usage",
	"\n"
	"	- print brief description of all commands\n"
	"help command ...\n"
	"	- print detailed usage of 'command'"
);

还有很多命令,请参考source code。

–end–

by dycc