接着前几篇博客继续分析,在lowlevel_init结束后也就是s_init返回后,跳转到_main,那么就从_main开始吧!
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)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
sub sp, sp, #GD_SIZE /* allocate one GD above SP */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
mov r9, sp /* GD is above SP */
mov r0, #0
bl board_init_f
#if ! defined(CONFIG_SPL_BUILD)
/*
* Set up intermediate environment (new sp and gd) and call
* relocate_code(addr_moni). Trick here is that we'll return
* 'here' but relocated.
*/
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
sub r9, r9, #GD_SIZE /* new GD is below bd */
adr lr, here
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
add lr, lr, r0
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate_code
here:
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
ldr r0, =__bss_start /* this is auto-relocated! */
ldr r1, =__bss_end /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */
clbss_l:cmp r0, r1 /* while not at end of BSS */
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
bl coloured_LED_init
bl red_led_on
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
ldr pc, =board_init_r /* this is auto-relocated! */
/* we should not return here. */
#endif
ENDPROC(_main)
前面已经分析过了,SPL阶段只会编译到bl board_init_f,而且这个函数是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);
}
这里的board_init_r是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();
#endif
boot_device = spl_boot_device();
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();
break;
#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);
}
其实这些前几篇大概已经分析过了,这里详细分析一下spl_mmc_load_image();系统从SD卡启动
的就是运行这段代码的,想学习一下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);
if (!mmc) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
puts("spl: mmc device not found!!\n");
#endif
hang();
}
err = mmc_init(mmc);
if (err) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
printf("spl: mmc init failed: err - %d\n", err);
#endif
hang();
}
boot_mode = spl_boot_mode();
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_initialize(gd->bd);
int mmc_initialize(bd_t *bis)
{
INIT_LIST_HEAD (&mmc_devices);
cur_dev_num = 0;
if (board_mmc_init(bis) < 0)
cpu_mmc_init(bis);
#ifndef CONFIG_SPL_BUILD
print_mmc_devices(',');
#endif
do_preinit();
return 0;
}
其中的board_mmc_init(bis)
#ifdef CONFIG_GENERIC_MMC
int board_mmc_init(bd_t *bis)
{
sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT);
#if !defined (CONFIG_SPL_BUILD) && defined (CONFIG_MMC_SUNXI_SLOT_EXTRA)
sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT_EXTRA);
#endif
return 0;
}
#endif
很明显实际的工作交给了sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT);
int sunxi_mmc_init(int sdc_no)
{
struct mmc *mmc;
memset(&mmc_dev[sdc_no], 0, sizeof(struct mmc));
memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
mmc = &mmc_dev[sdc_no];
sprintf(mmc->name, "SUNXI SD/MMC");
mmc->priv = &mmc_host[sdc_no];
mmc->send_cmd = mmc_send_cmd;
mmc->set_ios = mmc_set_ios;
mmc->init = mmc_core_init;
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->host_caps = MMC_MODE_4BIT;
mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
mmc->f_min = 400000;
mmc->f_max = 52000000;
mmc_resource_init(sdc_no);
mmc_clk_io_on(sdc_no);
mmc_register(mmc);
return 0;
}
上面就是mmc_initialize(gd->bd);的实际工作了,满眼都是结构体,还是从结构体说起吧!
首先抛开linux下SD卡的驱动模型,比如什么kobject、kset、块设备的缓冲、总线等概念,
但是有些概念还是需要的,比如HOST控制器,所以这里主要介绍4个struct
struct mmc {
struct list_head link;
char name[32];
void *priv;
uint voltages;
uint version;
uint has_init;
uint f_min;
uint f_max;
int high_capacity;
uint bus_width;
uint clock;
uint card_caps;
uint host_caps;
uint ocr;
uint dsr;
uint dsr_imp;
uint scr[2];
uint csd[4];
uint cid[4];
ushort rca;
char part_config;
char part_num;
uint tran_speed;
uint read_bl_len;
uint write_bl_len;
uint erase_grp_size;
u64 capacity;
u64 capacity_user;
u64 capacity_boot;
u64 capacity_rpmb;
u64 capacity_gp[4];
block_dev_desc_t block_dev;
int (*send_cmd)(struct mmc *mmc,
struct mmc_cmd *cmd, struct mmc_data *data);
void (*set_ios)(struct mmc *mmc);
int (*init)(struct mmc *mmc);
int (*getcd)(struct mmc *mmc);
int (*getwp)(struct mmc *mmc);
uint b_max;
char op_cond_pending; /* 1 if we are waiting on an op_cond command */
char init_in_progress; /* 1 if we have done mmc_start_init() */
char preinit; /* start init as early as possible */
uint op_cond_response; /* the response byte from the last op_cond */
};
不用说了,MMC的主要结构体。其中要注意的是block_dev_desc_t block_dev;void *priv;
说一下个人的理解,其中block_dev_desc_t block_dev;相当于MMC留个上层的接口,相对于
linux驱动中的嵌入到实际驱动的字符设备结构体、块设备结构体 ,毕竟MMC对于上层而言是
块设备,那么这个block_dev_desc_t block_dev;中重要的接口就是block_read、block_write、
block_erase,由于对linux块设备驱动研究的不是很深,好像linux的块设备驱动没有这种直接的
接口,这里的接口更像是无OS的裸机驱动,还有和linux块设备驱动的最大区别莫过于没有缓冲,
或者说没有linux的缓冲结构复杂,这个跟SD卡是否存在文件系统有关系吗??
那么void *priv;是什么呢?在sunxi_mmc_init中对priv 的操作就是mmc->priv = &mmc_host[sdc_no];
所以猜测用于指向mmc控制器,和linux的MMC驱动中的host有点像。
那么小结一下:
mmc驱动的模型:
block_dev_desc_t block_dev;是留给上层的接口,使用方法是
mmc->block_dev.block_read(0, sector, 1, header);读取块设备的一个例子。
void *priv;是具体的MMC控制器,为什么要有这个呢?还拿读取块设备作为例子吧。
mmc->block_dev.block_read(0, sector, 1, header);的实际操作按理说是直接操作MMC控制器的,
但是系统往往不只有一个MMC控制器,所以加入这个void *priv可以指向某个MMC控制器,而不至于
把驱动写死了,也就是说mmc->block_dev.block_read(0, sector, 1, header);的实际执行动作是可以
从几个MMC控制器中选择的。
小结完毕
看看block_dev_desc(也就是block_dev_desc_t)和sunxi_mmc_host的定义吧
typedef struct block_dev_desc {
int if_type; /* type of the interface */
int dev; /* device number */
unsigned char part_type; /* partition type */
unsigned char target; /* target SCSI ID */
unsigned char lun; /* target LUN */
unsigned char type; /* device type */
unsigned char removable; /* removable device */
#ifdef CONFIG_LBA48
unsigned char lba48; /* device can use 48bit addr (ATA/ATAPI v7) */
#endif
lbaint_t lba; /* number of blocks */
unsigned long blksz; /* block size */
int log2blksz; /* for convenience: log2(blksz) */
char vendor [40+1]; /* IDE model, SCSI Vendor */
char product[20+1]; /* IDE Serial no, SCSI product */
char revision[8+1]; /* firmware revision */
unsigned long (*block_read)(int dev,
lbaint_t start,
lbaint_t blkcnt,
void *buffer);
unsigned long (*block_write)(int dev,
lbaint_t start,
lbaint_t blkcnt,
const void *buffer);
unsigned long (*block_erase)(int dev,
lbaint_t start,
lbaint_t blkcnt);
void *priv; /* driver private struct pointer */
}block_dev_desc_t;
struct sunxi_mmc_host {
unsigned mmc_no;
uint32_t *mclkreg;
unsigned database;
unsigned fatal_err;
unsigned mod_clk;
struct sunxi_mmc *reg;
};
虽然不能理解每个成员的意义,但是主要的成员还是看的懂的。
那么回过头来看看int sunxi_mmc_init(int sdc_no)
memset(&mmc_dev[sdc_no], 0, sizeof(struct mmc));
memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
意思很明白,但是问题是为什么会有struct mmc、struct sunxi_mmc_host数组呢?
其实也不难理解,a20有4个MMC控制器吧,搜索参考手册中的SDC,结果存在SDC0~3。
OK继续,mmc->priv = &mmc_host[sdc_no]不用解释了,参考前面对MMC控制器的解释。
mmc->send_cmd = mmc_send_cmd;
mmc->set_ios = mmc_set_ios;
mmc->init = mmc_core_init;
mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->host_caps = MMC_MODE_4BIT;
mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
mmc->f_min = 400000;
mmc->f_max = 52000000;
这些都不用说了吧,每个意义不能很深的理解,但是大概意思还是懂的,
mmc_resource_init(sdc_no);
static int mmc_resource_init(int sdc_no)
{
struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
debug("init mmc %d resource\n", sdc_no);
switch (sdc_no) {
case 0:
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
mmchost->mclkreg = &ccm->sd0_clk_cfg;
break;
case 1:
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
mmchost->mclkreg = &ccm->sd1_clk_cfg;
break;
case 2:
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
mmchost->mclkreg = &ccm->sd2_clk_cfg;
break;
case 3:
mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
mmchost->mclkreg = &ccm->sd3_clk_cfg;
break;
default:
printf("Wrong mmc number %d\n", sdc_no);
return -1;
}
mmchost->database = (unsigned int)mmchost->reg + 0x100;
mmchost->mmc_no = sdc_no;
return 0;
}
设置mmhost为具体的控制器。由于在这里是SUNXI_MMC0_BASE
mmc_clk_io_on(sdc_no);代码就不粘了,主要是对MMC要用到的管脚初始化、还有用刚才
设置好的mmhost设置MMC时钟等等。
mmc_register(mmc);不用说了,注册MMC,在这里分析一下
int mmc_register(struct mmc *mmc)
{
/* 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;
if (!mmc->b_max)
mmc->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
INIT_LIST_HEAD (&mmc->link);
list_add_tail (&mmc->link, &mmc_devices);
return 0;
}
很明显,设置对上层留出的接口。
mmc->block_dev.block_read = mmc_bread;
mmc->block_dev.block_write = mmc_bwrite;
mmc->block_dev.block_erase = mmc_berase;
等赋值语句,其实就是通过给block_dev.block_read、block_dev.block_write、block_dev.block_erase赋值。
从而给出了读、写、擦除块的统一访问方式,要不然必须直接(其实也可以)调用mmc_bread、mmc_bwrite、
mmc_berase。注意这里的 mmc_bread、mmc_bwrite、mmc_berase并不属于struct mmc。
同样
mmc->send_cmd = mmc_send_cmd;
mmc->set_ios = mmc_set_ios;
mmc->init = mmc_core_init;
中的mmc_send_cmd、mmc_set_ios、mmc_core_init;并不属于struct mmc。可以看到struct mmc的作用
只是起到具体的连接几个函数的作用。比如把block_read连接到mmc_bread;而mmc_bread又调用
mmc_read_blocks,而mmc_read_blocks又调用mmc_send_cmd,需要注意的是mmc_send_cmd是操作
具体的MMC控制器的,刚才说了block_read是可以选择具体的MMC控制器的,那么怎么选择呢?这当中就是
通过mmc这个结构体,可以看到整个流程的每个函数都需要参数struct mmc,而这个mmc可以通过设备号
查找得到,从而就选择了某个mmc,由于mmc的mmc_initialize(gd->bd)过程已经把mmc设备号绑定到具体的
MMC控制器上,但是可以重新绑定的,用户需要注意的就是在选在实际SD卡0,的时候有可能程序中是find设备
好为3的mmc(是由mmc_initialize(gd->bd)造成的)。
继续分析INIT_LIST_HEAD (&mmc->link);list_add_tail (&mmc->link, &mmc_devices);
不用说了,系统是通过链表把各个设备的驱动联系起来的,所以要有链表的操作。
这里需要注意的是mmc->block_dev.dev = cur_dev_num++;给了mmc->block_dev.dev 一个设备号。
所以在寻找设备时,可以通过设备号。比如spl_mmc_load_image函数中
/* We register only one device. So, the dev id is always 0 */
mmc = find_mmc_device(0);
意思就很明白了。
告一段落,返回spl_mmc_load_image中,接下来是mmc = find_mmc_device(0);不用说了吧。
err = mmc_init(mmc);刚开始看到的时候好郁闷,怎么又是init?那么分析一下吧
int mmc_init(struct mmc *mmc)
{
int err = IN_PROGRESS;
unsigned start = get_timer(0);
if (mmc->has_init)
return 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_start_init(mmc)、mmc_complete_init(mmc)。
int mmc_start_init(struct mmc *mmc)
{
int err;
if (mmc_getcd(mmc) == 0) {
mmc->has_init = 0;
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC: no card present\n");
#endif
return NO_CARD_ERR;
}
if (mmc->has_init)
return 0;
err = mmc->init(mmc);
if (err)
return err;
mmc_set_bus_width(mmc, 1);
mmc_set_clock(mmc, 1);
/* Reset the Card */
err = mmc_go_idle(mmc);
if (err)
return err;
/* The internal partition reset to user partition(0) at every CMD0*/
mmc->part_num = 0;
/* Test for SD version 2 */
err = mmc_send_if_cond(mmc);
/* Now try to get the SD card's operating condition */
err = sd_send_op_cond(mmc);
/* If the command timed out, we check for an MMC card */
if (err == TIMEOUT) {
err = mmc_send_op_cond(mmc);
if (err && err != IN_PROGRESS) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("Card did not respond to voltage select!\n");
#endif
return UNUSABLE_ERR;
}
}
if (err == IN_PROGRESS)
mmc->init_in_progress = 1;
return err;
}
看到这里大概就明白了,这其实是涉及到SD卡的协议的,回想一下,其实刚才的mmc_initialize(gd->bd)
做的工作其实都只是为访问SD卡而做的数据结构的初始化和a20的mmc控制器、管脚的初始化,这里的
mmc_init(mmc);其实是按照SD协议去初始化SD卡,恍然大迷瞪。
mmc_complete_init()就不说了。
返回到void spl_mmc_load_image(void)中,接下来boot_mode = spl_boot_mode();
前面已经分析过了,会返回boot_mode == MMCSD_MODE_RAW。
所以mmc_load_image_raw(mmc,CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR);
我去,这才是真正的开始加载uboot代码。
下一篇再分析吧,有点累