u-boot移植笔记

时间:2021-07-19 16:36:13
u-boot版本:    u-boot-2012.04.01

pc机系统:ubuntu 12.04.5

开发板:TQ2440


1.新建一个单板目录
进入board/samsung/目录,执行 cp -rf smdk2410 smdk2440
进入include/configs目录,执行 cp smdk2410.h smdk2440.h
修改boards.cfg文件,添加2440的单板信息
执行make smdk2440_config 进行配置
执行make 进行编译

2.分析源码
从start.S文件里开始分析:

第一阶段:
start_code -> cpu_init_crit -> call_board_init_f -> board_init_f  (board_init_f 在 arch/arm/lib目录下)
1.修改系统时钟,把MPLL的设置放到start.S里,取消board_init_f -> init_sequence[] -> board_early_init_f 里对MPLL的初始化

2.查看串口波特率,在board_init_f -> init_sequence[] -> serial_init -> serial_init_dev(UART_NR) -> _serial_setbrg -> 
               get_PCLK -> get_HCLK -> 里,没有定义 “CONFIG_S3C2440”
在include/configs/smdk2440.h里,定义“CONFIG_S3C2440”

3.编译时发现错误,找出在哪个文件出错,然后到该文件相对应的Makefile找出相对应的宏,然后到smdk2440.h把相应的宏注释掉
在include/configs/smdk2440.h里,先暂时去掉CONFIG_CMD_NAND,可以避免编译时关于nand的错误

4.支持nand_flash启动
在start.S里面,添加对nand_flash的初始化,nand_init_ll
在 include/configs/smdk2440.h里,把CONFIG_SYS_TEXT_BASE 修改为0x33f80000,强制指定程序的链接地址
执行copy_code_to_sdram,把代码重定位到sdram
执行clear_bss,清BSS段,(BSS段就是初始值为0的全局变量的存放地址)
最后,执行 ldr pc, =call_board_init_f ,直接跳转到sdram里面执行代码 

5.把 board_init_f 里面的重定位函数 relocate_code(addr_sp, id, addr) 注释掉,因为已经在start.S里面进行了重定位

6.把 board_init_f 里面的 addr -= gd->mon_len; addr &= ~(4096 - 1);屏蔽
改为:addr = CONFIG_SYS_TEXT_BASE;

7.把start.S里面原来的 重定位 和 清bss段 这两部分代码去掉

第二阶段:
第二阶段的起始代码 arch/arm/lib/board.c 里的 void board_init_r(gd_t *id, ulong dest_addr) 函数

仍然在start.S文件里,
1.直接在 board_init_f (板级初始化函数)下面,跳转到第二阶段

2.board_init_r 函数,需要两个参数,
 id 从 board_init_f 里面获取,直接让 board_init_f 函数返回 id ,同时修改 include/common.h 里的 board_init_f,
 把 原来的 board_init_f 修改成 “ unsigned int board_init_f  (ulong); ”
 dest_addr 是u-boot的链接地址,直接指定为 _TEXT_BASE

3.修改/board/samsung/smdk2440/里的 Makefile ,把 init.c 添加到工程里面编译
把 COBJS := smdk2410.o 
改为:
COBJS := smdk2410.o init.o

4.修改链接脚本,把start.S,init.c,lowlevel_init.S 等文件,放在代码的最前面
链接脚本在 arch/arm/cpu/u-boot.lds

.text :
{
    __image_copy_start = .;
    CPUDIR/start.o (.text)
    *(.text)
}
修改为:
.text :
{
    __image_copy_start = .;
    CPUDIR/start.o (.text)
    /board/samsung/smdk2440/libsmdk2440.o (.text)
    *(.text)
}

libsmdk2440.o 是 /board/samsung/smdk2440/ 目录下的文件编译出来的库


5.修改 arch/arm/config.mk 文件,去掉 “-pie” 选项,
把 LDFLAGS_u-boot += -pie 屏蔽掉
pie选项是指:程序在编译链接时,会把一些地址信息(如“*(rel*),*(dynsym*)”)编译进u-boot.bin,
这样会使得u-boot编译出来的文件远远大于4k

/******  支持nandflash    *********/
6.修改include/configs/smdk2440.h,把 CONFIG_CMD_NAND 重新定义,使其支持nandflash
把 drivers/mtd/nand/s3c2410_nand.c 复制为 drivers/mtd/nand/s3c2440_nand.c

修改 drivers/mtd/nand/Makefile,使其编译 s3c2440_nand.c,在Makefile添加 COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
在smdk2440.h 里关于nandflash配置,添加:
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC

分析 board.c文件里面的 nand_init() 函数,分析 nand_init_chip(i) 函数
分析 board_nand_init(nand) 函数,分析 nand_scan(mtd, maxchips) 函数


board_nand_init(nand)函数进行了底层操作函数的初始化
nand_scan(mtd, maxchips)函数会使用到 board_nand_init(nand)函数 注册的底层操作函数
nand_scan_ident
nand_set_defaults
nand_command()   //分析这个函数,这个函数最终调用 chip->cmd_ctrl(mtd, readcmd, ctrl)
chip->cmd_ctrl(mtd, readcmd, ctrl) 这个函数,在board_nand_init里面被注册为 s3c2440_hwcontrol


在 s3c2440_hwcontrol() 函数完成数据/命令 的读写操作

/************** 支持 DM9000 网卡 *******************/
在smdk2440.h文件中,屏蔽cs8900的相关部分,改为:

#if 0
#define CONFIG_CS8900		/* we have a CS8900 on-board */
#define CONFIG_CS8900_BASE	0x19000300
#define CONFIG_CS8900_BUS16	/* the Linux driver does accesses as shorts */
#else
#define CONFIG_DRIVER_DM9000
#define CONFIG_DM9000_BASE              0x20000000
#define DM9000_IO                       CONFIG_DM9000_BASE
#define DM9000_DATA                     (CONFIG_DM9000_BASE + 4)
#endif

DM9000_DATA 是指 DM9000网卡的CMD引脚,原理图接了 LADDR2,用来区别发给DM9000的是命令还是数据

DM9000_IO 是指要发给DM9000的数据值

在board.c文件里,分析函数
board_init_r
eth_initialize
board_eth_init  // 在这个函数里面,对DM9000进行初始化

在smdk2440.h文件中,添加关于ip地址和网卡的配置,如下:
#define CONFIG_NETMASK   255.255.255.0
#define CONFIG_IPADDR    192.168.1.59
#define CONFIG_SERVERIP  192.168.1.61
#define CONFIG_ETHADDR      00:0c:29:4d:e4:f4

/********* 裁剪u-boot ******************/
在common/env_common.c里,default_environment数组是环境变量
修改smdk2440.h 文件,去掉不必要的配置项,如usb、fs、rtc、date等相关的配置

设置u-boot,使其支持save命令保存环境变量
在u-boot里,执行save命令保存环境变量到nandflash时,会执行common/env_nand.c里面的saveenv(void)函数,
查看 common 目录,发现COBJS-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o
表示要编译env_nand.c,则需要在smdk2440.h文件里定义 CONFIG_ENV_IS_IN_NAND
在smdk2440.h文件里添加以下内容:
#define CONFIG_ENV_OFFSET       0x40000   /* 环境变量在nandflash中的保存地址 */
#define CONFIG_ENV_SIZE         0x20000   /* 环境变量的大小 */
#define CONFIG_ENV_RANGE        CONFIG_ENV_SIZE   /* 环境变量要擦除的范围 */

设置u-boot,添加分区,使其支持 mtdparts 命令
在u-boot代码里面搜索 mtdparts,发现需要编译cmd_mtdparts.c,查看common目录下的Makefile
需要在smdk2440.h里面定义 CONFIG_CMD_MTDPARTS 
添加以下内容:
#define CONFIG_CMD_MTDPARTS     /* 支持mtdparts命令 */
#define CONFIG_MTD_DEVICE
#define MTDIDS_DEFAULT   "nand0=tq2440-0"  /* 默认名称 */
#define MTDPARTS_DEFAULT   "mtdparts=tq2440-0:256k(u-boot),"\
      "128k(params),"\
      "2m(kernel),"\
      "-(rootfs)"\
             
在cmd_mtdparts.c文件里,有 mtdparts_init(void)函数,用来初始化分区
在board.c文件里,初始化完之后,在进入main_loop之前,需要执行 mtdparts default 命令来使分区生效
重新编译u-boot,烧写,然后在u-boot 命令行执行 mtdparts ,可以看到分区信息
执行 nand erase.part kernel ,可以擦除kernel 分区
执行 nand write 30000000 kernel ,把sdram 30000000 位置的内容写到 kernel 分区

/********* 支持 yaffs2烧写及制作补丁 ************/
修改smdk2440.h,添加
#define CONFIG_CMD_NAND_YAFFS   /* wenjs modify 支持yaffs文件系统烧写 */
执行 nand write.yaffs 烧写文件系统时,发现烧写很快,查找代码发现:
common目录下,cmd_nand.c文件 do_nand 函数,关于 .yaffs2 的烧写,
nand_write_skip_bad(nand, off, &rwsize,(u_char *)addr, WITH_YAFFS_OOB);
在这个函数里,关于yaffs文件系统烧写,有一段代码:
if (flags & WITH_YAFFS_OOB) {
    int page, pages;
    size_t pagesize = nand->writesize;
    size_t pagesize_oob = pagesize + nand->oobsize;
    struct mtd_oob_ops ops;

    ops.len = pagesize;
    ops.ooblen = nand->oobsize;
    ops.mode = MTD_OOB_AUTO;
    ops.ooboffs = 0;

    pages = write_size / pagesize_oob;
    for (page = 0; page < pages; page++) {
        WATCHDOG_RESET();

        ops.datbuf = p_buffer;
        ops.oobbuf = ops.datbuf + pagesize;

        rval = nand->write_oob(nand, offset, &ops);
        if (!rval)
          break;

        offset += pagesize;
        p_buffer += pagesize_oob;
    }
}

改为:
if (flags & WITH_YAFFS_OOB) {
    int page, pages;
    size_t pagesize = nand->writesize;
    size_t pagesize_oob = pagesize + nand->oobsize;
    struct mtd_oob_ops ops;

    ops.len = pagesize;
    ops.ooblen = nand->oobsize;
    ops.mode = MTD_OOB_RAW;    //这里表示把OOB信息原原本本地写回去
    ops.ooboffs = 0;

    pages = write_size / pagesize_oob;
    for (page = 0; page < pages; page++) {
        WATCHDOG_RESET();
        ops.datbuf = p_buffer;
        ops.oobbuf = ops.datbuf + pagesize;
        rval = nand->write_oob(nand, offset, &ops);
        if (rval)  //这里,应该把 ! 去掉,返回非0表示烧写错误
           break;

        offset += pagesize;
        p_buffer += pagesize_oob;
    }
}

生成补丁文件:
先执行 make distclean ,清掉编译出来的文件
执行 mv u-boot-2012.04.01 u-boot-2012.04.01_ok 把u-boot重命名
然后执行 tar xvfj u-boot-2012.04.01.tar.bz2 -C ./ 把原来的 u-boot 解压
执行 diff -urN u-boot-2012.04.01 u-boot-2012.04.01_ok > u-boot-2012.04.01_ok.patch
参数 u -- 指定某种输出格式
r -- 递归地生成文件
N -- 如果某个文件为空,就保持为空

给未经修改的u-boot打补丁:
进入原来的u-boot,执行 cd u-boot-2012.04.01
打补丁 patch -p1 < ../u-boot-2012.04.01_ok.patch
参数 -p1 表示忽略第一级目录


/***** u-boot 移植完成 *****/