嵌入式Linux学习:u-boot源码分析(4)--AM335X系列的2014.10版

    继续之前的第三篇,之前已经完成了I2C的引脚配置以及分频,接下来就要实际开始去检测和读取EEPROM里的数据。看下面的代码

/*
 * Read header information from EEPROM into global structure.
 */
static int read_eeprom(struct am335x_baseboard_id *header)
{
	/* Check if baseboard eeprom is available *///wlg: all board based on ti's am335x has a eeprom to save the board's information!
	if (i2c_probe(CONFIG_SYS_I2C_EEPROM_ADDR)) {
		puts("Could not probe the EEPROM; something fundamentally "
			"wrong on the I2C bus.\n");
		return -ENODEV;//wlg: at first we probe it is exist or not;
	}

	/* read the eeprom using i2c */
	if (i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, 0, 2, (uchar *)header,
		     sizeof(struct am335x_baseboard_id))) {
		puts("Could not read the EEPROM; something fundamentally"
			" wrong on the I2C bus.\n");
		return -EIO;
	}//wlg: then we read the data from eeprom into point ----header,

	if (header->magic != 0xEE3355AA) {
		/*
		 * read the eeprom using i2c again,
		 * but use only a 1 byte address
		 */
		if (i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, 0, 1, (uchar *)header,
			     sizeof(struct am335x_baseboard_id))) {
			puts("Could not read the EEPROM; something "
				"fundamentally wrong on the I2C bus.\n");
			return -EIO;
		}

		if (header->magic != 0xEE3355AA) {
			printf("Incorrect magic number (0x%x) in EEPROM\n",
					header->magic);
			return -EINVAL;
		}
	}

	return 0;
}

    注释已经说得比较清楚了,本函数分以下阶段:

    1. 试探EEPROM是否存在

    2. 如果存在,就读取相应长度的数据

    3. 对读回的数据进行检测,如果读回的数据与预设一致,则返回0

    4. 如果不一致,则再次进行读取,若还是失败就返回相应的标志

    首先是第一步,通过判断i2c_probe(CONFIG_SYS_I2C_EEPROM_ADDR)的返回来判断EEPROM是否有效,实际上就是通过去检测I2C的slave设备是否会回复ACK信号来判断设备是否存在,这方面的知识请去了解下I2C的控制方式即可明白;具体的做法是执行i2c_read()函数做一次无效数据读取。在读取过程中会检测是否接收到ACK信号;

    加入EEPROM设备是存在的,那么接下来就可以执行i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, 0, 2, (uchar *)header,  sizeof(struct am335x_baseboard_id))函数,很简单的,其函数原型如下,看注释应该非常清楚了,不需要解释了。

/*
 * Read/Write interface:
 *   chip:    I2C chip address, range 0..127
 *   addr:    Memory (register) address within the chip
 *   alen:    Number of bytes to use for addr (typically 1, 2 for larger
 *              memories, 0 for register type devices with only one
 *              register)
 *   buffer:  Where to read/write the data
 *   len:     How many bytes to read/write
 *
 *   Returns: 0 on success, not 0 on failure
 */
int i2c_read(uint8_t chip, unsigned int addr, int alen, uint8_t *buffer, int len)

    其结果就是读取外置的EEPROM设备上的偏移地址为0,长度为sizeof(struct am335x_baseboard_id) 的数据,保存到起始地址为(uchar *)header的内存空间。执行完毕后,header这个结构体里应该塞满了关于board信息的数据。然后就检查里面某个元素(header->magic)是否等于预设0xEE3355AA。正常情况都是会满足的!所以read_eeprom()函数返回0!

    再次回到上一篇的get_dpll_ddr_params()函数。重复如下:

if (board_is_evm_sk(&header))  
        return &dpll_ddr_evm_sk;  
    else if (board_is_bone_lt(&header))  
        return &dpll_ddr_bone_black;  
    else if (board_is_evm_15_or_later(&header))  
        return &dpll_ddr_evm_sk;  
    else  
        return &dpll_ddr;  
    上面的代码就很容易理解了,就是会去检测header这个结构体中的name元素是否与其一致,比如说

static inline int board_is_evm_sk(struct am335x_baseboard_id *header)
{
	return !strncmp("A335X_SK", header->name, HDR_NAME_LEN);
}
    上面的代码就是对比header->name是否等于"A335X_SK",若相等就会返回1.这就意味着EEPROM中记录的数据,就有板子的名字等信息!

    假如我们用的是Beaglebone Black板子的话,只有执行board_is_bone_lt(&header)才会返回1,其他检测函数只能返回0!

    所以get_dpll_ddr_params()最终返回了dpll_ddr_bone_black这个指针!

    其他的代码就不一一解释了,其大概的作用就是,根据header(从EEPROM中读回)结构体的数据,有选择的进行引脚配置,包括SDRAM,MII设备的初始化。

    请注意,不同的board读回的header是不一样的,所以其相关的底层硬件配置也略有不同,所以如果要自定义一块板子,那么将这个EEPROM里的内容覆盖成满足你板子要求的相应参数即可。或者可以跳过EEPROM的读写,直接用我们的配置参数代替,因为用户的板子一般都不会有那么灵活的要求。

     最终我们跳出了board_early_init_f()函数,开始执行下一个函数sdram_init()(定义在board\ti\am335x\board.c)

void sdram_init(void)
{
	__maybe_unused struct am335x_baseboard_id header;

	if (read_eeprom(&header) < 0)
		puts("Could not get board ID.\n");
	//wlg: get the header from eeprom, it has information which board we now use
	if (board_is_evm_sk(&header)) {
		/*
		 * EVM SK 1.2A and later use gpio0_7 to enable DDR3.
		 * This is safe enough to do on older revs.
		 */
		gpio_request(GPIO_DDR_VTT_EN, "ddr_vtt_en");
		gpio_direction_output(GPIO_DDR_VTT_EN, 1);
	}

	if (board_is_evm_sk(&header))
		config_ddr(303, &ioregs_evmsk, &ddr3_data,
			   &ddr3_cmd_ctrl_data, &ddr3_emif_reg_data, 0);
	else if (board_is_bone_lt(&header))//wlg: this branch will return true,
		config_ddr(400, &ioregs_bonelt,//wlg: then will initial sdram as BBB!
			   &ddr3_beagleblack_data,
			   &ddr3_beagleblack_cmd_ctrl_data,
			   &ddr3_beagleblack_emif_reg_data, 0);
	else if (board_is_evm_15_or_later(&header))
		config_ddr(303, &ioregs_evm15, &ddr3_evm_data,
			   &ddr3_evm_cmd_ctrl_data, &ddr3_evm_emif_reg_data, 0);
	else
		config_ddr(266, &ioregs, &ddr2_data,
			   &ddr2_cmd_ctrl_data, &ddr2_emif_reg_data, 0);
}
#endif
    很明显,上面的代码和之前的代码非常相似,实际上就是读取EEPROM里的内容放置到header中,这里又读了一次,估计是想要实现每一步都是独立的。其顺序也大致相同,EEPROM里的数据读回来以后,就通过半段header->name来判断板子的类型,根据类型有选择进行初始化,也就是DDR的初始化,具体的实现方式应该就是底层寄存器的操作,这里不展开。

-------S_init()执行完毕,返回lowlevel_init,在返回_reset,也就是返回start.S---------

总结下都实现了什么:

    1. 实现了一个可用与console的uart,可以输出简单的信息,帮助调试!

    2. 读取EEPROM,按照类型配置SDRAM,为下一步将u-boot镜像复制到SDRAM空间里做准备

--------------------------------返回start.S-------------------------------------------

    又回到了汇编部分,

	bl	_main	/*wlg: jump to arch/arm/lib/crt0.s*/
    就是一个转跳,再次展开_main函数

/*
 * entry point of crt0 sequence
 */

ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
	ldr	sp, =(CONFIG_SPL_STACK)/* wlg: in spl, it seems be 0x40310000-sizeof(global_data)*/
#else
	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)/* wlg: in uboot, it will be ?*/
#endif
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance		|		      	SPL	|			uboot	|*/
	mov	r2, sp	/* wlg: we record the end of address of the initial 	|sp is useful, r9 (gdata) will 	|sp is useful,  a new temp 	|*/
	sub	sp, sp, #GD_SIZE	/* allocate one GD above SP 		|be redefined in board_init_f	|GD(pointed by r9) will be  	|*/			
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance		|so next text is only to clear	| set above on sp,   gdata 	|*/
	mov	r9, sp		/* GD is above SP 				|a invalid memory		|be discarded 			|*/
	mov	r1, sp	/* wlg: we record the start address of the initial*/
	mov	r0, #0 	/*wlg : the num of initialition*/
clr_gd:
	cmp	r1, r2			/* while not at end of GD */
	strlo	r0, [r1]		/* clear 32-bit GD word */ /*wlg: ro >> [r1]*/
	addlo	r1, r1, #4		/* move to next */
	blo	clr_gd
#if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SPL_BUILD)
	sub	sp, sp, #CONFIG_SYS_MALLOC_F_LEN
	str	sp, [r9, #GD_MALLOC_BASE]
#endif
	/* mov r0, #0 not needed due to above code */
	bl	board_init_f 	/*wlg: SPL: board_init_f - Function in spl.c (arch\arm\lib) , and it will not return, it will jump to uboot's start's*/
						/*wlg: Uboot: board_init_f - Function in Board.c (arch\arm\lib) at line 263 (199 lines), it will return*/

    上面的代码我添加了比较详细的备注,在这里SPL和uboot的执行目的有很大的不同,我们这里只谈SPL这个阶段,所以上述代码主要是为了完成以下工作:

    1. 重新确定了sp,这是为了下一步的C语言函数调用做准备,貌似之前也有做过,所以不解释了

    2. 在sp上划出了一部分空间作为保存全局数据的空间(划分完以后sp=sp-GD_SIZE),并将其请0!即使这里将sp(减过后)赋给了r9,但是这部分空间目前在SPL阶段没有被利用起来,在后面又会重新定义r9,再次将其指向我们之前一直提及的gdata,而不会沿用这里的sp!

    3. 跳到 board_init_f ()函数执行接下来的代码,在SPL阶段,这个函数被定义在spl.c (arch\arm\lib),在uboot阶段被定义在另一个地方,看注释,以后再解释。接下来我们跳到board_init_f()函数继续:

/*
 * In the context of SPL, board_init_f must ensure that any clocks/etc for
 * DDR are enabled, ensure that the stack pointer is valid, clear the BSS
 * and call board_init_f.  We provide this version by default but mark it
 * as __weak to allow for platforms to do this in their own way if needed.
 */
void __weak board_init_f(ulong dummy)
{
	/* Clear the BSS. */
	memset(__bss_start, 0, __bss_end - __bss_start);

	/* Set global data pointer. */
	gd = &gdata;

	board_init_r(NULL, 0);//wlg: SPL:  board_init_r - Function in spl.c (arch\arm\lib) 
}

    看注释很好理解,完成以下工作:
    1. 清除BSS段

    2. 又将gd指针指向了gdata,也就是将r9指向了gdata

    3.跳到board_init_r()函数继续执行,这个函数被定义在spl.c (arch\arm\lib) 

void board_init_r(gd_t *dummy1, ulong dummy2)
{
	u32 boot_device;
	debug(">>spl:board_init_r()\n");

#ifdef CONFIG_SYS_SPL_MALLOC_START
	mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
			CONFIG_SYS_SPL_MALLOC_SIZE);
#endif

#ifndef CONFIG_PPC
	/*
	 * timer_init() does not exist on PPC systems. The timer is initialized
	 * and enabled (decrementer) in interrupt_init() here.
	 */
	timer_init();
#endif

#ifdef CONFIG_SPL_BOARD_INIT
	spl_board_init();//wlg: skip it now, we will look into it later 
#endif
	//return gd->arch.omap_boot_params.omap_bootdevice
	boot_device = spl_boot_device();//this parament saved early: save_omap_boot_params()
	debug("boot device - %d\n", boot_device);
	switch (boot_device) {
#ifdef CONFIG_SPL_RAM_DEVICE
	case BOOT_DEVICE_RAM:
		spl_ram_load_image();
		break;
#endif
#ifdef CONFIG_SPL_MMC_SUPPORT
	case BOOT_DEVICE_MMC1:
	case BOOT_DEVICE_MMC2:
	case BOOT_DEVICE_MMC2_2:
		spl_mmc_load_image();	//wlg: for BBB, we will boot from MMC, whether MMC  or SD 
		break;					//after this, we copy the u-boot to sdram with offset at .....
#endif
#ifdef CONFIG_SPL_NAND_SUPPORT
	case BOOT_DEVICE_NAND:
		spl_nand_load_image();
		break;
#endif
#ifdef CONFIG_SPL_ONENAND_SUPPORT
	case BOOT_DEVICE_ONENAND:
		spl_onenand_load_image();
		break;
#endif
#ifdef CONFIG_SPL_NOR_SUPPORT
	case BOOT_DEVICE_NOR:
		spl_nor_load_image();
		break;
#endif
#ifdef CONFIG_SPL_YMODEM_SUPPORT
	case BOOT_DEVICE_UART:
		spl_ymodem_load_image();
		break;
#endif
#ifdef CONFIG_SPL_SPI_SUPPORT
	case BOOT_DEVICE_SPI:
		spl_spi_load_image();
		break;
#endif
#ifdef CONFIG_SPL_ETH_SUPPORT
	case BOOT_DEVICE_CPGMAC:
#ifdef CONFIG_SPL_ETH_DEVICE
		spl_net_load_image(CONFIG_SPL_ETH_DEVICE);
#else
		spl_net_load_image(NULL);
#endif
		break;
#endif
#ifdef CONFIG_SPL_USBETH_SUPPORT
	case BOOT_DEVICE_USBETH:
		spl_net_load_image("usb_ether");
		break;
#endif
#ifdef CONFIG_SPL_USB_SUPPORT
	case BOOT_DEVICE_USB:
		spl_usb_load_image();
		break;
#endif
#ifdef CONFIG_SPL_SATA_SUPPORT
	case BOOT_DEVICE_SATA:
		spl_sata_load_image();
		break;
#endif
	default:
		debug("SPL: Un-supported Boot Device\n");
		hang();
	}

	switch (spl_image.os) {
	case IH_OS_U_BOOT:
		debug("Jumping to U-Boot\n");
		break;
#ifdef CONFIG_SPL_OS_BOOT
	case IH_OS_LINUX:
		debug("Jumping to Linux\n");
		spl_board_prepare_for_linux();
		jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
	default:
		debug("Unsupported OS image.. Jumping nevertheless..\n");
	}
	jump_to_image_no_args(&spl_image);//now we jump from spl to uboot
}
    这段代码主要是完成以下工作:

    1. 根据之前保存的boot parament参数来从哪一个设备读取uboot的镜像,这里有很多种加载镜像的途径,可以从MMC,也可以从网络,串口等

    2. 将uboot的镜像复制到SDRAM的相应位置后,然后跳转到SDRAM中执行。依旧是说,我们之前SPL程序都是在SRAM中执行的,从现在开始,终于跳转到SDRAM中运行了!

    接下来详细的介绍下上述步骤:

    本函数最早被执行的,实际上是这个语句:

        boot_device = spl_boot_device();//this parament saved early: save_omap_boot_params()

    检查下spl_boot_device()函数,

    

u32 spl_boot_device(void)
{
	return (u32) (gd->arch.omap_boot_params.omap_bootdevice);
}
    也就是最后就是boot_device  = gd->arch.omap_boot_params.omap_bootdevice,这个gd里的参数是之前保存的,它记录的就是加载SPL 的设备,有可能是UART,MMC,网络等等,都会如实的被保存在这个参数里。因为我们的板子是BBBlack,假设是从MMC的SD中完成SPL的加载,那么接下来被执行的就是

    spl_mmc_load_image(); //wlg: for BBB, we will boot from MMC, whether MMC  or SD 

void spl_mmc_load_image(void)
{
	struct mmc *mmc;
	int err;
	u32 boot_mode;

	mmc_initialize(gd->bd);
	/* We register only one device. So, the dev id is always 0 */
	mmc = find_mmc_device(0);//it is SPL stage, we only have one mmc.
	if (!mmc) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
		puts("spl: mmc device not found!!\n");
#endif
		hang();
	}

	err = mmc_init(mmc);//if mmc initial is complete, it will return 0
	if (err) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
		printf("spl: mmc init failed: err - %d\n", err);
#endif
		hang();
	}

	boot_mode = spl_boot_mode();//recognize boot mode from global_data
	if (boot_mode == MMCSD_MODE_RAW) {
		debug("boot mode - RAW\n");
#ifdef CONFIG_SPL_OS_BOOT
		if (spl_start_uboot() || mmc_load_image_raw_os(mmc))
#endif
		err = mmc_load_image_raw(mmc,
			CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR);
#ifdef CONFIG_SPL_FAT_SUPPORT
	} else if (boot_mode == MMCSD_MODE_FAT) {
		debug("boot mode - FAT\n");
#ifdef CONFIG_SPL_OS_BOOT
		if (spl_start_uboot() || spl_load_image_fat_os(&mmc->block_dev,
								CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION))
#endif
		err = spl_load_image_fat(&mmc->block_dev,
					CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION,
					CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME);
#endif
#ifdef CONFIG_SUPPORT_EMMC_BOOT
	} else if (boot_mode == MMCSD_MODE_EMMCBOOT) {
		/*
		 * We need to check what the partition is configured to.
		 * 1 and 2 match up to boot0 / boot1 and 7 is user data
		 * which is the first physical partition (0).
		 */
		int part = (mmc->part_config >> 3) & PART_ACCESS_MASK;

		if (part == 7)
			part = 0;

		if (mmc_switch_part(0, part)) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
			puts("MMC partition switch failed\n");
#endif
			hang();
		}
#ifdef CONFIG_SPL_OS_BOOT
		if (spl_start_uboot() || mmc_load_image_raw_os(mmc))
#endif
		err = mmc_load_image_raw(mmc,
			CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR);
#endif
	} else {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
		puts("spl: wrong MMC boot mode\n");
#endif
		hang();
	}

	if (err)
		hang();
}
    也就是说所我们选择从MMC中加载uboot,要做的工作就是:

    1. mmc_initialize(gd->bd);

    2. mmc = find_mmc_device(0);//it is SPL stage, we only have one mmc.

    3. err = mmc_init(mmc);//if mmc initial is complete, it will return 0

    4.  boot_mode = spl_boot_mode();//recognize boot mode from global_data

    5. 根据boot_mode,将uboot的镜像复制到SDRAM中,返回

    详细的功能介绍,会在下一篇中继续介绍。







    





    







  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值