嵌入式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阶段!





2 0