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

2019-07-12 23:39发布

    之前的博客已经写到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);这个函数在driversmmcmmc.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()函数,其定义在driversmmcOmap-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里的同名元素         整个效果就如下图:
    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 ", 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 ", __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阶段!