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 移植完成 *****/