linux开发环境搭建(4)-从SD卡启动uboot

1、superboot烧写程序
前面已经将网卡配置好,也已经可以通过tftp下载程序到nand flash,也可以通过nfs挂载根文件系统,可以说基本的开发环境已经搭建好。

使用过友善开发板的人应该知道,友善对于在第一次空烧程序进nand flash里面的时候提供了一个叫superboot-6410.bin的sd卡启动的uboot,要烧写uboot到nand flash上的时候需要先将友善提供的superboot-6410.bin,可以在这里下载:
http://download.csdn.net/detail/atmega_chen/9782685

使用SD-Flasher.exe这个工具烧进sd卡里面,可以在这里下载:
http://download.csdn.net/detail/atmega_chen/9782681

如下图,表示superboot-6410.bin已经烧写到sd卡
linux开发环境搭建(4)-从SD卡启动uboot_第1张图片

然后把sd卡插入到开发板上,并将拨码开关拨到sd卡启动,上电后会打印出下面的菜单
linux开发环境搭建(4)-从SD卡启动uboot_第2张图片

从这些选项中可以看到,主要是可以下载uboot,kernel和filesystem,并且这个菜单没有q选项,退出到uboot原始shell命令行,也就是说使用superboot.bin下载文件只能通过dnw使用usb下载,不能使用tftp下载。由于本人的电脑一直装不上dnw的驱动,所以一直无法使用dnw下载程序,一般都是使用tftp下载uboot和kernel。但是友善提供的superboot.bin并没有tftp下载的功能,而且也不提供源码。所以就想移植一个已经有tftp下载功能的uboot,并且内从sd卡启动这个uboot,这样就可以通过从sd卡启动,然后使用tftp功能下载文件。

2、如何从sd卡启动
这里以移植好的tiny6410_uboot作为测试,这个是友善移植好的uboot,支持从sd卡启动,可以在这里下载:
http://download.csdn.net/detail/atmega_chen/9772444

不过官方提供的代码有些问题,主要是nand flash初始化部分,在从nand flash启动的时候没有问题,但是从sd卡启动的时候要稍作修改,否则启动不了。

了解uboot启动流程的都知道,在uboot汇编阶段的时候如果是从nand flash启动,则BL1的代码会从nand flash中把剩余的uboot代码拷贝到内存中。同样,如果要是从sd卡启动,则会把剩余的uboot代码从sd卡拷贝到内存,而三星的sd固件里面就提供了一个在启动阶段将数据从sd卡拷到内存的函数,这个函数是

#define CopyMovitoMem(a,b,c,d,e)    (((int(*)(int, uint, ushort, uint *, int))(*((uint *)(TCM_BASE + 0x8))))(a,b,c,d,e))

五个参数分别为a是使用的sd卡通道0或者1,b为BL2的长度,c为BL2的大小,d为要将BL2拷贝到内存的位置,d一般为0。
使用这个函数就可以把BL2从sd卡拷贝到内存,因为是从sd卡启动,所以只要BL1放对了位置,开机的时候芯片内的固化代码是会自动从sd卡上把BL1的代码拷贝到setupping_stone上,然后运行的,我们需要做的是在BL1里面调用CopyMovitoMem函数把BL2拷贝到内存去。

3、编译uboot从sd卡启动
因为tiny6410_uboot是友善移植好的,支持uboot从sd卡启动,所以我们尝试编译使用这个uboot,之后我们会自己移植,现在是搭建开发环境,先用移植好的。

进入到uboot目录,打开顶层Makefile,搜索mini6410,可以看到有下面几个配置选项,其中
mini6410_nand_config-ram128表示从nand flash启动,RAM大小为128M;
mini6410_sd_config-ram128表示从sd卡启动,RAM大小为128M;
tiny6410_config表示从nand flash启动,RAM大小为256M;
mini6410_sd_config-ram256表示从sd卡启动,RAM大小为256M;
我使用的开发板RAM大小为256M,因为要从sd卡启动,所以选择mini6410_sd_config-ram256。先配置,然后编译
linux开发环境搭建(4)-从SD卡启动uboot_第3张图片

#make mini6410_sd_config-ram256
#make ARCH=arm CROSS_COMPILE=arm-linux-

编译出来后可以通过dnw需要将uboot.bin放到sd里面去,下面介绍uboot.bin在sd卡中的位置。

4、uboot在sd卡中位置
由于之前把sd卡做成了superboot.bin的卡,现在要把卡恢复成正常卡,依然是使用SD-Flasher.exe,选择ReFormat,然后再按scan,如果现实Available为no说明sd卡已经恢复正常,如下图
linux开发环境搭建(4)-从SD卡启动uboot_第4张图片

要让uboot从sd卡启动,则要正确烧写uboot.bin在sd卡中的位置,这个具体位置可以参看三星的介绍SD卡启动的文档《CHxx_IROM_ApplicationNote_Rev1.00_080801.pdf》,可以在这里下载:
http://download.csdn.net/detail/atmega_chen/9782687

sd卡分为普通SD卡和大容量SDHC卡,大于2G的卡为SDHC卡,本人的SD卡为8G,所以属于SDHC卡。对应不同容量的卡,uboot在sd卡中的位置不一样,这个在文档里面有说明,下图是uboot.bin在SDHC卡中的位置。
linux开发环境搭建(4)-从SD卡启动uboot_第5张图片
从图中可以知道,uboot启动的第一阶段代码BL1,也就是uboot.bin的前8k代码放在sd的最后的1025+1+16(block),BL2也就是完整的uboot.bin代码放在1025+1+16+BL2_size(block)的地方,其中BL2_size为代码定义的BL2的大小,比如友善的代码就定义这个大小为0x60000。

所以BL1的位置为,以块为单位:
BL1_pos = sdcard_size-1025-1-16;
BL2_pos = sdcard_size-1025-1-16-BL2_size;

下面是我写的一个shell脚本,主要是自动读取sd卡的容量,根据容量判断是普通SD卡还是SDHC卡,然后确定BL1和BL2的位置,再把uboot.bin的前8k分离出来写入到BL1的位置,整个uboot.bin写入到BL2的位置。
注意需要使用root或者sudo权限运行,使用方法为插入sd卡,然后运行后会自动将uboot.bin文件烧写入sd卡中。接受两个参数,你的uboot.bin的路径名,和sd卡的设备文件名:
./sd_boot.sh ./u-boot.bin /dev/sdx

#!/bin/sh

#125 heads, 62 sectors/track, 1017 cylinders, total 7882752 sectors
#useage: ./sd_boot.sh u-boot.bin /dev/sdx
size=$(fdisk -l $2 | grep total)
size=${size#*total }
size=${size%% sectors*}

echo "SD card size is $size"

if [ $size -gt 3941376 ]; then
        echo "SD card is high room"     
        #形如$[ a + b ]这种形式的加减在ubuntu14.04里面已经不支持了,在ubuntu12.04里面可以
        #可以使用$((a+b))这种形式的加减
        BL1_POS=$((size-1025-1-16))
        #友善的uboot代码中,定义BL2的大小为0x60000,也就是768个block
        BL2_POS=$((size-1025-1-16-768))
        echo "BL1: $BL1_POS, BL2: $BL2_POS"
else
        echo "SD card is normal room" 
        BL1_POS=$((size-1-1-16))
        BL2_POS=$((size-1-1-16-768))
        echo "BL1: $BL1_POS, BL2: $BL2_POS"
fi

split -b 8k $1
dd if=./xaa of=$2 seek=$BL1_POS bs=512
dd if=$1 of=$2 seek=$BL2_POS bs=512

rm x* -rf

5、从sd卡启动uboot
将编译出来的uboot.bin通过sd_boot.sh脚本烧写入sd后,将sd卡插入开发板,拨码开关拨到从sd卡启动,串口打印出了下面的log:
linux开发环境搭建(4)-从SD卡启动uboot_第6张图片

从打印log看,sd卡中的代码已经被正确拷贝到内存中,并且成功运行,说明前面的写入的BL1和BL2的位置是正确,不过好像nand flash初始化的时候失败了,提示没有找到设备,而且emmc卡初始化也失败。当nand flash启动的时候,nand flash的初始化是正常的,从sd卡启动时出现了错误。

通过分析代码,在start_armboot的下面代码是初始化nand flash的地方。

#if defined(CONFIG_NAND)
        puts ("NAND:    ");
        nand_init();            /* go init the NAND */
        NAND_Init();
#endif

继续查看nand_init的代码

void nand_init(void)
{
    int i;
    unsigned int size = 0;
    for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {
        nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
        size += nand_info[i].size;
        if (nand_curr_device == -1)
            nand_curr_device = i;
    }
    printf("%lu MB ", size / (1024 * 1024));

#if defined(CFG_NAND_FLASH_BBT)
    printf("(Flash Based BBT Enabled)");
#endif

    printf("\n");

#ifdef CFG_NAND_SELECT_DEVICE
    /*
     * Select the chip in the board/cpu specific driver
     */
    board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
#endif

其中nand_init_chip是初始化nand_flash的代码

static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
               ulong base_addr)
{
    mtd->priv = nand;

    nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;
    board_nand_init(nand);

    if (nand_scan(mtd, 1) == 0) {
        if (!mtd->name)
            mtd->name = (char *)default_nand_name;
    } else
        mtd->name = NULL;

}

跟进到board_nand_init函数,这个函数比较长,贴取部分关键的代码,在这里函数里面主要就是去通过NAND_CMD_READID命令读取nand flash的ID,将读取出来的ID和nand_flash_ids这个表里面已经有的ID进行比较,如果和这个表里面的某一项nand flash型号对的上说明读取的ID是正确值,否则有可能读取的ID是错误的。

从打印的错误log来看,这里读取到的ID为0,说明读取操作有问题。首先从硬件上排查,因为从nand flash启动的时候,nand flash的初始化是正常的,所以可以肯定的是硬件上是没问题的,而且整个代码的逻辑也是没问题的,这从nand flash启动中可以知道,所以应该是读取ID的操作出了问题。

s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
    s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
    s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);
    s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
    s3c_nand_device_ready(0);

    tmp = readb(nand->IO_ADDR_R); /* Maf. ID */
    //printf("Maf. ID :0x%x\n", tmp);

    tmp = readb(nand->IO_ADDR_R); /* Device ID */
    //printf("Device ID :0x%x\n", tmp);
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
        if (tmp == nand_flash_ids[i].id) {
            //printf("ID match, tmp=0x%x, nand_flash_ids=0x%x\n", tmp, nand_flash_ids[i].id);
            type = &nand_flash_ids[i];
            break;
        }
    }

仔细想了一下,如果是从nand flash启动,执行在这里初始化nand flash操作的时候其实nand flash已经在前面拷贝代码的时候初始化过一遍,所以这里可以不需要重新初始化和复位nand flash。但是从sd卡启动的话,前面没有nand flash的相关操作,所以必须要重新初始化和复位才能正常读写。所以在读取ID操作之前尝试加了初始化操作和nand_reset复位函数,如下

void select_chip(void)
{
    unsigned int cur =readl(NFCONT);
    cur &= ~(1<<1);
    writel(cur, NFCONT);
}

void delselect_chip(void)
{
    unsigned int cur =readl(NFCONT);
    cur |= (1<<1);
    writel(cur, NFCONT);
}

void clear_RnB(void)
{
    unsigned int cur =readl(NFSTAT);
    cur |= (1<<4);
    writel(cur, NFSTAT);
}

void send_cmd(unsigned char cmd)
{
    writel(cmd, NFCMMD);
}

void send_addr(unsigned char addr)
{
    writel(addr, NFADDR);
}

void wait_RnB(void)
{
    while(!(readl(NFSTAT)&0x1));
}

void nand_reset(void)
{
    //选择nandflash
    select_chip();

    //清除RB
    clear_RnB();

    //发送0xff
    send_cmd(0xff);

    //等待RB信号
    wait_RnB();

    //取消选择nandflash
    delselect_chip();
}

/********************************/
//跳过部分代码
    //add by CHB
    //初始化NFCONF
    cur = readl(NFCONF);
    cur &= ~((1<<12)|(2<<8)|(1<<4));
    cur |= (1<<12)|(2<<8)|(1<<4);
    writel(cur, NFCONF);

    //初始化NFCONT
    cur = readl(NFCONT);
    cur |= (0x1<<0) | (0x1<<1);
    writel(cur, NFCONT);

    //这里必须要复位一下,否则无法读出ID
    nand_reset();

    s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
    s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
    s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);
    s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
    s3c_nand_device_ready(0);

    tmp = readb(nand->IO_ADDR_R); /* Maf. ID */
    //printf("Maf. ID :0x%x\n", tmp);

    tmp = readb(nand->IO_ADDR_R); /* Device ID */
    //printf("Device ID :0x%x\n", tmp);

    for (i = 0; nand_flash_ids[i].name != NULL; i++) {
        if (tmp == nand_flash_ids[i].id) {
            //printf("ID match, tmp=0x%x, nand_flash_ids=0x%x\n", tmp, nand_flash_ids[i].id);
            type = &nand_flash_ids[i];
            break;
        }
    }

重新编译烧写uboot.bin到sd卡,上电后出现下面的打印log,可以看到nand flash已经被正确识别,说明我们的修改有作用,确实是nand flash没有初始化和复位导致的。
linux开发环境搭建(4)-从SD卡启动uboot_第7张图片

虽然nand flash的问题解决了,但是没有看到uboot弹出shell命令行。可以看到emmc识别出来的大小为0,说明emmc的初始化也有问题,这里的emmc也就是我们的SD卡。
跟了下代码,主要是发现是初始化SD卡的时候主机无法获取到SD卡的OCR寄存器的值导致SD卡初始化失败,然后进入了死循环,所以shell命令行出不来,这个问题目前没有进一步深究下去。因为我们的SD卡主要是用来启动uboot的,而启动从sd卡读取uboot主要使用的是sd卡的固件函数,所以这里只要能从sd卡将uboot读取到内存去,那么sd的功能也就实现了,不需要用在uboot阶段用sd卡来存储文件。所以,我是直接将sd卡初始化这部分代码直接注释掉了,在start_armboot函数中:

/*
#if defined(CONFIG_BOOT_MOVINAND)
    puts ("MMC:     ");

    if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) {
        printf("Boot up for burning\n");
    } else {
        movi_set_capacity();
        //printf("MOVI_TOTAL_BLKCNT is %d\n", MOVI_TOTAL_BLKCNT);
        movi_set_ofs(MOVI_TOTAL_BLKCNT);
        movi_init();
    }

#endif
*/

注释掉了以后,重新编译烧写uboot.bin到sd卡启动,出现下面的打印log,可以看到uboot已经正常启动了,并且菜单也打印出来了,说明uboot已经正确从sd卡启动了。
linux开发环境搭建(4)-从SD卡启动uboot_第8张图片

选q退出菜单,回到原始命令行,输入tftp 0xc0008000 uImage
可以看到我们也可以从sd卡启动通过tftp下载程序到内存了。
linux开发环境搭建(4)-从SD卡启动uboot_第9张图片

下一节会介绍如何使用tftp和nand命令结合下载uboot到nand flash,需要修改一些nand flash的命令。

你可能感兴趣的:(linux)