嵌入式Linux学习:u-boot源码分析(5)--AM335X系列的2014.10版
来源:互联网 发布:java用户权限管理 编辑:程序博客网 时间:2024/06/07 15:07
之前的博客已经写到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 prevcur_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_BUILDprint_mmc_devices(',');//int the stage of SPL, print is not function.#endifdo_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_BASEcase 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;#endifbreak;#endif#ifdef OMAP_HSMMC3_BASEcase 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;#endifbreak;#endifdefault: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");#endifcfg = &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;elsecfg->f_max = 26000000;} elsecfg->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;#endifmmc = 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_GPIOint 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_devicesreturn mmc;}这个函数的主要功能是:
a. 在SRAM上申请内存用来存放mmc这个结构体,mmc这个结构体中包含了上面所说的priv结构体,cfg结构体等等,也包含了一个链表
b.将之前定义的priv和cfg赋值给mmc里的同名元素
整个效果就如下图:
c.给mmc结构体中的其他元素赋值
d. 初始化其链表
e. 将mmc的链表和之前已经存在的链表进行连接
连接的过程如下:
上图是个广泛的例子,就是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);#endifreturn 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学习:u-boot源码分析(5)--AM335X系列的2014.10版
- 嵌入式Linux学习:u-boot源码分析(1)--AM335X系列的2014.10版
- 嵌入式Linux学习:u-boot源码分析(2)--AM335X系列的2014.10版
- 嵌入式Linux学习:u-boot源码分析(3)--AM335X系列的2014.10版
- 嵌入式Linux学习:u-boot源码分析(4)--AM335X系列的2014.10版
- 嵌入式Linux学习:u-boot源码分析(6)--AM335X系列的2014.10版
- 嵌入式Linux学习:u-boot源码分析(7)--AM335X系列的2014.10版
- 【嵌入式Linux学习七步曲之第三篇 Linux系统bootlaoder移植】U-BOOT全线移植分析系列之三--U-BOOT在AT91RM9200上的移植
- 【嵌入式Linux学习七步曲之第三篇 Linux系统bootlaoder移植】U-BOOT全线移植分析系列之三--U-BOOT在AT91RM9200上的移植
- 【嵌入式Linux学习七步曲之第三篇 Linux系统bootlaoder移植】U-BOOT全线移植分析系列之四--U-boot如何引导Linux内核启动?
- 【嵌入式】探究bootloader,分析u-boot源码
- 【嵌入式】探究bootloader,分析u-boot源码
- 【嵌入式Linux学习七步曲之第三篇 Linux系统bootlaoder移植】U-BOOT全线移植分析系列之二--U-boot基础
- 【嵌入式Linux学习七步曲之第三篇 Linux系统bootlaoder移植】U-BOOT全线移植分析系列之二--U-boot基础
- AM335x(TQ335x)学习笔记——u-boot-2014.10移植
- AM335x(TQ335x)学习笔记——u-boot-2014.10移植
- AM335x(TQ335x)学习笔记——u-boot-2014.10移植
- 学习嵌入式Linux-JZ2440-U-Boot简介
- python所有错误
- sessionStorage 和 localStorage 使用体会
- ajax上传文件
- 模拟数据,无网络下--wireMock的使用
- 织梦首页、列表页调用文章body内容的两种方法
- 嵌入式Linux学习:u-boot源码分析(5)--AM335X系列的2014.10版
- Android图片上传
- USACO 1.2 回文平方数
- hibernate execute与executefind的区别
- Android学习笔记(三)Activiy学习——启动模式
- 每天一个 Linux 命令(23):Linux 目录结构
- unity FBX模型导出系统源码WRP FBX Exporter下载
- LuceneInAction-构建索引
- NLP︱高级词向量表达(一)——GloVe(理论、相关测评结果、R&python实现、相关应用)