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

    之前的博客已经写到SPL阶段中关于MMC读取uboot的image的部分,我们将其简单的复制如下:

     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中,返回

一 mmc_initialize(gd->bd)

    首先是第一个功能, mmc_initialize(gd->bd);这个函数在drivers\mmc\mmc.c中定义,如下

int mmc_initialize(bd_t *bis)
{
	INIT_LIST_HEAD (&mmc_devices);//mmc_devices is a struct: list_head, which keep a next and a prev
	cur_dev_num = 0;

	if (board_mmc_init(bis) < 0)// arch/arm/cpu/armv7/Omap-common/boot-common.c,
		cpu_mmc_init(bis);		//after this board_mmc_init, we updata the mmc_devices link!

#ifndef CONFIG_SPL_BUILD
	print_mmc_devices(',');//int the stage of SPL, print is not function.
#endif

	do_preinit();//call mmc.init to make initialize mmc,
	return 0;
}
    需要详细的介绍下mmc_devices这个变量:

static struct list_head mmc_devices;
    实际上mmc_devices只是一个链表结构体而已,这个链表结构体名为list_head ,继续看list_head :

struct list_head {
	struct list_head *next, *prev;
};
    而链表结构体里面保存着两个元素:

    1. 指向前一个链表的指针

    2. 指向后一个链表的指针

    回过头继续看mmc_initialize()函数,其完成的工作大致如下:

    1.  链表初始化

    初始化一个名为mmc_devices的全局(链表)变量,即INIT_LIST_HEAD (&mmc_devices),这个函数实际上只是将mmc_devices这个结构体里的两个指针赋值为mmc_devices,即默认前一个链表和后一个链表都是自己,即为初始化状态!

    2.  全局变量初始化

    将全局变量cur_dev_num 赋值为0,这个变量记录着当前mmc设备的数量,目前mmc设备还没有被发现,所以默认数量就是0

    3. board_mmc_init()

    接着执行board_mmc_init(bis)函数,这个函数定义在arch/arm/cpu/armv7/Omap-common/boot-common.c,如下:

int board_mmc_init(bd_t *bis)//this function is initial the mmc which chose by SYS_BOOT...
{
	switch (spl_boot_device()) {
	case BOOT_DEVICE_MMC1:
		omap_mmc_init(0, 0, 0, -1, -1);
		break;
	case BOOT_DEVICE_MMC2:
	case BOOT_DEVICE_MMC2_2:
		omap_mmc_init(1, 0, 0, -1, -1);
		break;
	}
	return 0;
}
    这个函数就是一个switch分支结构,选择的依据就是spl_boot_device()的返回,其定义也在boot-common.c内,如下:

u32 spl_boot_device(void)
{
	return (u32) (gd->arch.omap_boot_params.omap_bootdevice);
}
    也就是返回了全局变量中的某一个元素!这个元素在之前的博客中有介绍,它表示MLO(就是SPL)被保存的地方。由于之前是采用默认的配置方式,这里要做进一步的mmc初始化!
    接下来执行omap_mmc_init()函数,其定义在drivers\mmc\Omap-hsmmc.c中

int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio,
		int wp_gpio)
{
	struct mmc *mmc;
	struct omap_hsmmc_data *priv_data;
	struct mmc_config *cfg;
	uint host_caps_val;

	priv_data = malloc(sizeof(*priv_data));
	if (priv_data == NULL)
		return -1;

	host_caps_val = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS |
			     MMC_MODE_HC;

	switch (dev_index) {
	case 0:
		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE;
		break;
#ifdef OMAP_HSMMC2_BASE
	case 1:
		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE;
#if (defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \
     defined(CONFIG_DRA7XX)) && defined(CONFIG_HSMMC2_8BIT)
		/* Enable 8-bit interface for eMMC on OMAP4/5 or DRA7XX */
		host_caps_val |= MMC_MODE_8BIT;
#endif
		break;
#endif
#ifdef OMAP_HSMMC3_BASE
	case 2:
		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE;
#if defined(CONFIG_DRA7XX) && defined(CONFIG_HSMMC3_8BIT)
		/* Enable 8-bit interface for eMMC on DRA7XX */
		host_caps_val |= MMC_MODE_8BIT;
#endif
		break;
#endif
	default:
		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE;
		return 1;
	}
#ifdef OMAP_HSMMC_USE_GPIO
	/* on error gpio values are set to -1, which is what we want */
	priv_data->cd_gpio = omap_mmc_setup_gpio_in(cd_gpio, "mmc_cd");
	priv_data->wp_gpio = omap_mmc_setup_gpio_in(wp_gpio, "mmc_wp");
#endif

	cfg = &priv_data->cfg;

	cfg->name = "OMAP SD/MMC";
	cfg->ops = &omap_hsmmc_ops;

	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
	cfg->host_caps = host_caps_val & ~host_caps_mask;

	cfg->f_min = 400000;

	if (f_max != 0)
		cfg->f_max = f_max;
	else {
		if (cfg->host_caps & MMC_MODE_HS) {
			if (cfg->host_caps & MMC_MODE_HS_52MHz)
				cfg->f_max = 52000000;
			else
				cfg->f_max = 26000000;
		} else
			cfg->f_max = 20000000;
	}

	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;

#if defined(CONFIG_OMAP34XX)
	/*
	 * Silicon revs 2.1 and older do not support multiblock transfers.
	 */
	if ((get_cpu_family() == CPU_OMAP34XX) && (get_cpu_rev() <= CPU_3XX_ES21))
		cfg->b_max = 1;
#endif
	mmc = mmc_create(cfg, priv_data);//after this, we linked the new mmc to mmc_devices.
	if (mmc == NULL)
		return -1;

	return 0;
}

    这个函数比较长,其主要完成以下工作:

    a. 定义函数内所需要的指针变量

    b. 在SRAM内申请一片内存空间,用来保存omap_hsmmc_data 这个结构体,申请成功后将其首地址赋值给priv_data这个指针,来了解一下这个结构体:

struct omap_hsmmc_data {
	struct hsmmc *base_addr;
	struct mmc_config cfg;
#ifdef OMAP_HSMMC_USE_GPIO
	int cd_gpio;
	int wp_gpio;
#endif
};
    其主要包含了两个重要的元素,第一个是指向片上控制mmc的寄存器的指针,第二个是用来保存mmc配置信息的mmc_config 结构体cfg,再来看一下该结构体:

struct mmc_config {
	const char *name;
	const struct mmc_ops *ops;
	uint host_caps;
	uint voltages;
	uint f_min;
	uint f_max;
	uint b_max;
	unsigned char part_type;
};
    该结构体包含了关于mmc的配置信息,包括名字等等;那么回到omap_mmc_init()函数

    c. 根据传入的变量值,来选择片上的哪一个mmc控制寄存器作为其所实际控制寄存器。因为片上可能有2个或以上的mmc控制器,每一个控制器都对应着某一个mmc卡或者SD卡,所以要根据实际的需要来选择哪一个控制器,比如说这里我们选择:

	case 0:
		priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC1_BASE;
    就是将上面的priv_data->base_addr赋值为OMAP_HSMMC1_BASE,这个值实际上就是对应着片上的外设寄存器的首地址!
    d. 然后利用cfg这个指针变量,让其指向priv_data中的cfg元素,这样方便下面的赋值,包括名字、最大,最小频率等配置信息

    e. 执行mmc = mmc_create(cfg, priv_data);//after this, we linked the new mmc to mmc_devices.

struct mmc *mmc_create(const struct mmc_config *cfg, void *priv)
{
	struct mmc *mmc;

	/* quick validation */
	if (cfg == NULL || cfg->ops == NULL || cfg->ops->send_cmd == NULL ||
			cfg->f_min == 0 || cfg->f_max == 0 || cfg->b_max == 0)
		return NULL;

	mmc = calloc(1, sizeof(*mmc));
	if (mmc == NULL)
		return NULL;

	mmc->cfg = cfg;
	mmc->priv = priv;

	/* the following chunk was mmc_register() */

	/* Setup dsr related values */
	mmc->dsr_imp = 0;
	mmc->dsr = 0xffffffff;
	/* Setup the universal parts of the block interface just once */
	mmc->block_dev.if_type = IF_TYPE_MMC;
	mmc->block_dev.dev = cur_dev_num++;
	mmc->block_dev.removable = 1;
	mmc->block_dev.block_read = mmc_bread;
	mmc->block_dev.block_write = mmc_bwrite;
	mmc->block_dev.block_erase = mmc_berase;

	/* setup initial part type */
	mmc->block_dev.part_type = mmc->cfg->part_type;

	INIT_LIST_HEAD(&mmc->link);

	list_add_tail(&mmc->link, &mmc_devices);//to make a linked between new device: mmc
											//and default: mmc_devices
	return mmc;
}
    这个函数的主要功能是:

    a. 在SRAM上申请内存用来存放mmc这个结构体,mmc这个结构体中包含了上面所说的priv结构体,cfg结构体等等,也包含了一个链表

    b.将之前定义的priv和cfg赋值给mmc里的同名元素

        整个效果就如下图:

嵌入式Linux学习:u-boot源码分析(5)--AM335X系列的2014.10版_第1张图片

    c.给mmc结构体中的其他元素赋值

    d. 初始化其链表

    e. 将mmc的链表和之前已经存在的链表进行连接
        连接的过程如下:

嵌入式Linux学习:u-boot源码分析(5)--AM335X系列的2014.10版_第2张图片
    上图是个广泛的例子,就是mmc_device作为链表的结尾。其next是指向自己,其prev是指向新加入的mmc结构体,在这里就是上面定义的mmc。如果此时要再加入一个mmc2,那么效果也如上面右边所示,也就是说mmc_device总是在链表的最后,而新加入的链表总是在mmc_devices的旁边!

    这样我们就完成了mmc的初始化,这个初始化包括其配置信息以及链表信息等等

    4.  do_preinit()

    这个函数定义在mmc.c文件中

static void do_preinit(void)
{
	struct mmc *m;
	struct list_head *entry;

	list_for_each(entry, &mmc_devices) {
		m = list_entry(entry, struct mmc, link);

		if (m->preinit)
			mmc_start_init(m);
	}
}
    这个函数实际上就是历遍mmc_devices这个链表头,提取出有效的mmc地址并赋值给m这个指针,然后利用mmc_start_init(m)进行初始化。

---------------------完成mmc的初始化--------------------

二、 mmc = find_mmc_device(0)

    其函数定义如下:

struct mmc *find_mmc_device(int dev_num)
{
	struct mmc *m;
	struct list_head *entry;

	list_for_each(entry, &mmc_devices) {
		m = list_entry(entry, struct mmc, link);

		if (m->block_dev.dev == dev_num)
			return m;
	}

#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
	printf("MMC Device %d not found\n", dev_num);
#endif

	return NULL;
}
    实际上也是历遍mmc_devices链表,将其中符合要求的(就是索引为0,代表默认mmc设备,在这里只有一个,所以其默认就是0)mmc设备结构体指针返回

三、err = mmc_init(mmc)

    其函数定义如下:
int mmc_init(struct mmc *mmc)
{
	int err = IN_PROGRESS;
	unsigned start;

	if (mmc->has_init)
		return 0;

	start = get_timer(0);

	if (!mmc->init_in_progress)
		err = mmc_start_init(mmc);

	if (!err || err == IN_PROGRESS)
		err = mmc_complete_init(mmc);
	debug("%s: %d, time %lu\n", __func__, err, get_timer(start));
	return err;
}
    实际上就是通过全局函数去判断mmc是否初始化完成,并记录初始化完成的时间。这个函数和之前的mmc_start_init(m);项对应!

四、load

err = spl_load_image_fat(&mmc->block_dev,
					CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION,
					CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME);

    根据选择文件类型,或raw或者是fat类型,将mmc设备中的uboot镜像加载到SDRAM中,至此完成了uboot镜像从mmc中加载到SDRAM中。但是目前程序任然还是SPL阶段,所以CPU的指针还是指向SRAM


-----------------uboot镜像加载完成------------------------


     回到开始的board_init_r(),接下来继续执行jump_to_image_no_args(&spl_image);//now we jump from spl to uboot

			spl_image.entry_point = image_get_ep(header);

    然后程序就从SPL阶段跳转到Uboot阶段,且CPU从SRAM跳到了SDRAM中执行!

    后期的博客继续续写uboot阶段!





你可能感兴趣的:(嵌入式Linux--uboot)