u-boot-2010.03在tq6410上的移植详解(连载)
原创文章版权所有!如需转载,请注明出处: http://hi.baidu.com/liushuiyue1/myhome谢谢合作!!!!!由于Uboot2010.03对S3C6410有了很好的支持,所以采用Uboot2010.03版本。
一、 移植环境
主 机:VMWare--Fedora 8
开发板:天嵌TQ6410—256M nandflash,Kernel:2.6.30.4
编译器:EABI-4.3.3_V0.1
u-boot:u-boot-2010.08
二、 源码获得
Uboot源码到:ftp://ftp.denx.de/pub/u-boot/下载
三、 本次移植的功能实现
· 支持Nand Flash读写
· 支持从Nand Flash/SD启动(SD卡启动只在uboot1.1.6中完成)
· 支持CS8900或者DM9000网卡
· 支持Yaffs文件系统
· 支持USB下载
四、 移植步骤
1. 建立自己的开发板项目并测试编译
目前u-boot对很多CPU直接支持,可以查看board目录的一些子目录,如:board/samsung/目录下就是对三星一些ARM处理器的支持,有smdk2400、smdk2410和smdk6400,但没有6410,所以我们就在这里建立自己的开发板项目。
1)因6410和6400的资源差不多,主频和外设有点差别,所以我们就在board/samsung/下建立自己开发板的项目,取名叫my2440
#tar -jxvf u-boot-2010.03.tar.bz2 //解压源码
#cd u-boot-2010.03/board/samsung/ //进入目录
#mkdir smdk6410 //创建smkd6410文件夹
2)因6410和6400的资源差不多,所以就以6400项目的代码作为模板,以后再修改
#cp -rf smdk6400/* smdk6410/ //将6400下所有的代码复制到6410下
#cd smdk6410 //进入smdk6410目录
#mv smdk6400.c my6410.c //将smdk6410下的smdk6400.c改名为smdk6410.c
#cd ../../../ //回到u-boot根目录
#cp include/configs/smdk6400.h include/configs/smdk6410.h //建立6410配置头文件 (二)
3)修改u-boot跟目录下的Makefile文件。查找到smdk6400_config的地方,在他下面按照smdk6400_config的格式建立smdk6410_config的编译选项,另外还要指定交叉编译器
#gedit Makefile
CROSS_COMPILE ?= arm-linux- //指定交叉编译器为arm-linux-gcc
smdk6410_noUSB_config \
smdk6410_config : unconfig
@mkdir -p $(obj)include $(obj)board/samsung/smdk6410
@mkdir -p $(obj)nand_spl/board/samsung/smdk6410
@echo "#define CONFIG_NAND_U_BOOT" > $(obj)include/config.h
@if [ -z "$(findstring smdk6410_noUSB_config,$@)" ]; then \
echo "RAM_TEXT = 0x57e00000" >> $(obj)board/samsung/smdk6410/config.tmp;\
$(MKCONFIG) $(@:_config=) arm arm1176 smdk6410 samsung s3c64xx; \
else \
echo "RAM_TEXT = 0xc7e00000" >> $(obj)board/samsung/smdk6410/config.tmp;\
$(MKCONFIG) $(@:_noUSB_config=) arm arm1176 smdk6410 samsung s3c64xx; \
fi
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
*说明:arm :CPU的架构(ARCH)
arm1176:CPU的类型
smdk6410 :对应在board目录下建立新的开发板项目的目录
samsung:新开发板项目目录的上级目录,如直接在board下建立新的开发板项目的目录,则这里就为NULL
s3c64xx:CPU型号
*注意:编译选项格式的第二行要用Tab键开始,否则编译会出错
4)进入顶层目录nand_spl/board/samsung目录新建目录smdk6410,并将smdk6400下的文件复制到刚刚新建的smdk6410下
#cd nand_spl/board/samsung //
#cp –ar smdk6400/* smdk6410/
5)测试编译新建的smdk6410开发板项目
#make smdk6410_config //如果出现Configuring for smdk6410 board...则表示设置正确
#make //编译后在根目录下会出现u-boot.bin和u-boot-nand.bin文件,则u-boot移植的第一步就算完成了
由于此版本的u-boot对于串口和时钟在开始配置文件中就已经初始化,对于ARM1176有很好的支持,利用从SD卡启动的uboot将u-boot-nand.bin下载到nand flash中,同时将开发板上SW2设置到从Nand flash启动位置。利用SecureCRT.exe软件观察串口打印信息如下:
可以看到移植在tq6410上移植uboot已经成功。可是Nor flash是2M可是信息上显示为0,是因为没有添加Nor flash的驱动引起的,而且开发板上自带的是DM9000AE的100M网卡,而uboot默认支持CS8900所以开始添加uboot的相关功能。
移植是采用SD烧写nand flash的uboot,为了方便烧写和调试首先移植网卡实现tftp下载。 (三)
五、 DM9000在Uboot2010.03上的移植
1)首先进入include/configs/修改smdk6410.h文件,屏蔽CS8900的相关宏定义同时添加DM9000AE的相关宏
#cd include/configs///
#gedit smdk6410.h //
smdk6410.h修改内容如下:
#define CONFIG_NET_MULTI 1
//#define CONFIG_CS8900 /* we have a CS8900 on-board */
//#define CONFIG_CS8900_BASE 0x18800300
//#define CONFIG_CS8900_BUS16 /* follow the Linux driver */
#define CONFIG_DRIVER_DM9000 1/* we have a DM9000AE on-board */
#define CONFIG_DM9000_BASE 0x18000300
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT
#define CONFIG_ETHADDR 10:23:45:67:89:ab
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.174.2 //开发板上的ip地址
#define CONFIG_SERVERIP 192.168.174.1//虚拟机上的ip地址
#define CONFIG_GATEWAYIP 192.168.174.6
#define CONFIG_DM9000_DEBUG//一定要加上否则会出现没有设置ipaddr的现象
2)修改net/eth.c,添加DM9000AE的初始化函数(红色部分为修改的地方):
int eth_initialize(bd_t *bis)
{
unsigned char env_enetaddr;
int eth_number = 0;
eth_devices = NULL;
eth_current = NULL;
show_boot_progress (64);
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
miiphy_init();
#endif
/* Try board-specific initialization first. If it fails or isn't
* present, try the cpu-specific initialization */
if (board_eth_init(bis) < 0)
cpu_eth_init(bis);
#if defined(CONFIG_DB64360) || defined(CONFIG_CPCI750)
mv6436x_eth_initialize(bis);
#endif
#if defined(CONFIG_DB64460) || defined(CONFIG_P3Mx)
mv6446x_eth_initialize(bis);
#endif
#if defined(CONFIG_DRIVER_DM9000)
dm9000_initialize(bis);
#endif
if (!eth_devices) {
puts ("No ethernet found.\n");
show_boot_progress (-64);
} else {
struct eth_device *dev = eth_devices;
char *ethprime = getenv ("ethprime");
show_boot_progress (65);
do {
if (eth_number)
puts (", ");
printf("%s", dev->name);
if (ethprime && strcmp (dev->name, ethprime) == 0) {
eth_current = dev;
puts (" ");
}
eth_getenv_enetaddr_by_index(eth_number, env_enetaddr);
if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) {
if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) &&
memcmp(dev->enetaddr, env_enetaddr, 6))
{
printf ("\nWarning: %s MAC addresses don't match:\n",
dev->name);
printf ("Address in SROM is %pM\n",
dev->enetaddr);
printf ("Address in environment is %pM\n",
env_enetaddr);
}
memcpy(dev->enetaddr, env_enetaddr, 6);
}
eth_number++;
dev = dev->next;
} while(dev != eth_devices);
重新编译uboot,并下载uboot到nand flash中,重新启动开发板,观察打印信息如下,可以发现uboot已经能识别DM9000芯片:
使用ping指令进行测试如下:
至此,uboot已经能支持dm9000的网卡,可以使用tftp协议下载程序并在ram中运行了。 (五)
原创文章版权所有!如需转载,请注明出处: http://hi.baidu.com/liushuiyue1/myhome谢谢合作!!!!!
七.Uboot支持yaffs2文件的读写
由于Uboot本身支持yaffs2文件的读写直接修改./include/configs/smdk6410.h的配置文件,使得uboot支持yaffs2的读写,重新编译Uboot并下载到Nand Flash中,串口打印信息如下,uboot已经支持yaffs2烧写到nand flash(由于本人对于此uboot中指令不熟悉,所以至今还未写yaffs2文件成功,如有知道的可告知,谢谢)。
于是又重新手动yaffs2文件的烧写指令。首先修改./include/configs/smdk6410.h
#cd include/configs/smdk6410.h//
#define CONFIG_YAFFS2
于是又重新手动yaffs2文件的烧写指令。
1) 修改./include/configs/smdk6410.h
注意一定要屏蔽掉CONFIG_YAFFS2,否则yaffs文件读写有冲突。
#cd include/configs/smdk6410.h//
//#define CONFIG_YAFFS2
#define CONFIG_SYS_NAND_YAFFS_WRITE 1 /* support yaffs write EN */
#define CONFIG_MTD_NAND_ECC_YAFFS 1
2)修改common/cmd_nand.c
在373行,红色字体为添加部分
if (!s || !strcmp(s, ".jffs2") ||
!strcmp(s, ".e") || !strcmp(s, ".i")) {
if (read)
ret = nand_read_skip_bad(nand, off, &size,
(u_char *)addr);
else
ret = nand_write_skip_bad(nand, off, &size,
(u_char *)addr);
}
#if defined(ENABLE_CMD_NAND_YAFFS)
}else if ( s != NULL &&
(!strcmp(s, ".yaffs") || !strcmp(s, ".yaffs1"))){
if(read) {
printf("nand read.yaffs is not provide temporarily!");
} else {
nand->rw_oob = 1;
#if defined(ENABLE_CMD_NAND_YAFFS_SKIPFB)
nand->skipfirstblk = 1;
#else
nand->skipfirstblk = 0;
#endif
ret = nand_write_skip_bad(nand,off,&size,(u_char *)addr);
#if defined(ENABLE_CMD_NAND_YAFFS_SKIPFB)
nand->skipfirstblk = 0;
#endif
nand->rw_oob = 0;
}
#endif
}
else if (!strcmp(s, ".oob")) {
/* out-of-band data */
mtd_oob_ops_t ops = {
.oobbuf = (u8 *)addr,
.ooblen = size,
.mode = MTD_OOB_RAW
};
在505行添加,红色字体为添加部分
"nand erase - erase 'size' bytes from\n"
" offset 'off' (entire device if not specified)\n"
#if defined(CONFIG_SYS_NAND_YAFFS_WRITE)
"nand read[.yaffs] is not provide temporarily\n"
"nand write[.yaffs] addr off size -write the 'size' byte yaffs image starting\n"
"at offset 'off' from memory address 'addr' (.yaffs for 512+16 Nand)"
#endif
"nand bad - show bad blocks\n" (六)
原创文章版权所有!如需转载,请注明出处: http://hi.baidu.com/liushuiyue1/myhome谢谢合作!!!!!
3)drivers/mtd/nand/nand_base.c
在2095行处红色字体为修改部分:
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
int ret;
#if defined(CONFIG_SYS_NAND_YAFFS_WRITE)
int oldopsmode = 0;
if(mtd->rw_oob==1)
{
size_t oobsize =mtd->oobsize;
size_t datasize = mtd->writesize;
int i=0;
uint8_t oobtemp;
int datapages = 0;
datapages = len/(datasize);
for(i=0;i<(datapages);i++)
{
memcpy((void *)oobtemp,(void *)(buf+datasize*(i+1)),oobsize);
memmove((void *)(buf+datasize*(i+1)),(void *)(buf+datasize*(i+1)+oobsize),(datapages-(i+1))*(datasize)+(datapages-1)*oobsize);
memcpy((void *)(buf+(datapages)*(datasize+oobsize)-oobsize),(void *)(oobtemp),oobsize);
}
}
#endif
/* Do not allow reads past end of device */
if ((to + len) > mtd->size)
return -EINVAL;
if (!len)
return 0;
nand_get_device(chip, mtd, FL_WRITING);
chip->ops.len = len;
chip->ops.datbuf = (uint8_t *)buf;
#if !defined(CONFIG_SYS_NAND_YAFFS_WRITE)
chip->ops.oobbuf = NULL;
#endif
#if defined(CONFIG_SYS_NAND_YAFFS_WRITE)
if(mtd->rw_oob!=1)
{
chip->ops.oobbuf = NULL;
}
else
{
chip->ops.oobbuf = (uint8_t*)(buf+len);
chip->ops.ooblen = mtd->oobsize;
oldopsmode = chip->ops.mode;
chip->ops.mode = MTD_OOB_RAW;
}
#endif
ret = nand_do_write_ops(mtd, to, &chip->ops);
*retlen = chip->ops.retlen;
nand_release_device(mtd);
#if defined(CONFIG_SYS_NAND_YAFFS_WRITE)
chip->ops.mode = oldopsmode;
#endif
return ret;
} (7)
原创文章版权所有!如需转载,请注明出处: http://hi.baidu.com/liushuiyue1/myhome谢谢合作!!!!!
4)修改drivers/mtd/nand/nand_util.c
int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer)
{
int rval;
size_t left_to_write = *length;
size_t len_incl_bad;
u_char *p_buffer = buffer;
#if defined(CONFIG_SYS_NAND_YAFFS_WRITE)
if(nand->rw_oob==1)
{
size_t oobsize=nand->oobsize;
size_t datasize = nand->writesize;
int datapages = 0;
if(((*length)%(nand->oobsize+nand->writesize))!=0)
{
printf("Attempt to write error lenght data!\n");
return -EINVAL;
}
datapages = *length/(datasize+oobsize);
*length = datapages*datasize;
left_to_write = *length;
}
#endif
........
len_incl_bad = get_len_incl_bad (nand, offset, *length);
if ((offset + len_incl_bad) > nand->size) {
printf ("Attempt to write outside the flash area\n");
return -EINVAL;
}
#if !defined(CONFIG_SYS_NAND_YAFFS_WRITE)
if (len_incl_bad == *length) {
rval = nand_write (nand, offset, length, buffer);
if (rval != 0)
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
return rval;
}
#endif
while (left_to_write > 0) {
size_t block_offset = offset & (nand->erasesize - 1);
size_t write_size;
WATCHDOG_RESET ();
if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
printf ("Skip bad block 0x%08llx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}
#if defined(CONFIG_SYS_NAND_YAFFS_WRITE)
if (nand->skipfirstblk==1)
{
nand->skipfirstblk = 0;
printf("Skip the first good block%llx\n",offset&~(nand->erasesize-1));
offset+=nand->erasesize-block_offset;
continue;
}
#endif
....
left_to_write -= write_size;
printf("%d%% is complete",100-(left_to_write/(*length/100)));
offset += write_size;
// p_buffer += write_size;
#if defined(CONFIG_SYS_NAND_YAFFS_WRITE)
if(nand->rw_oob==1)
{
p_buffer+=write_size+(write_size/nand->writesize*nand->oobsize);
}
else
{
p_buffer+=write_size;
}
#else
p_buffer += write_size;
#endif
}
return 0;
}
5)修改include/linux/mtd/mtd.h
在131行添加红色字体部分
#if defined(CONFIG_SYS_NAND_YAFFS_WRITE)
u_char rw_oob;
u_char skipfirstblk;
#endif (八)
原创文章版权所有!如需转载,请注明出处: http://hi.baidu.com/liushuiyue1/myhome谢谢合作!!!!!
重新编译uboot并下载到Nand Flash中,使用nand指令烧写yaffs2文件到nand中如下图所示,至此uboot已支持yaffs的烧写。
八.支持串口xmodem协议
U-Boot默认支持的loadb命令需要配合Linux下的kermit工具来使用,loady命令通过串口ymodem协议来传输文件。Windows下的超级终端虽然支持ymodem,但是它的使用界面实在不友好。而SecureCRT只支持xmodem和zmodem。而上位机和开发板之间的文件传输又实在是一件非常重要的事,所以现在修改代码以增加对xmodem的支持,即增加一个命令loadx。
1)依照loady的实现来编写代码,首先使用U_BOOT_CMD宏来增加loadx命令(在文件common/cmd_load.c文件中):
U_BOOT_CMD(
loadx, 3, 0, do_load_serial_bin,
"load binary file over serial line (xmodem mode)",
"[ off ] [ baud ]\n"
" - load binary file over serial line"
" with offset 'off' and baudrate 'baud'"
);
2)依照loady在do_load_serial_bin函数中增加对loadx命令的处理分支。
else if(strcmp(argv,"loadx")==0) {
printf ("## Ready for binary (xmodem) download "
"to 0x%08lX at %d bps...\n",
offset,
load_baudrate);
addr = load_serial_xmodem (offset);
}
3)由于addr行调用了load_serial_xmodem函数,依照load_serial_ymodem实现的一个函数。首先在文件开头增加loadx_serial_xmodem函数的声明,然后复制load_serial_ymodem函数为load_serial_xmodem,稍作修改:(1)、将局部数组ymodemBuf改名为xmodemBuf,并在后面使用到的地方统一修改,这只是为了与函数名称一致,可改可不改。(2)、info.mode的值从xyzModem_ymodem改为xyzModem_xmodem。
#if defined(CONFIG_CMD_LOADB)
static ulong load_serial_ymodem (ulong offset);
static ulong load_serial_xmodem (ulong offset);
#endif
重新编译,执行loadx,然后在secureCRT的Transfer菜单下点Send Xmodem(xxx已经放在了Upload目录里),完成对串口xmodem协议的支持。 不错呀,顶一个!! 顶一下,希望有空了弄个PDF格式的,方便大家,个人意见。 严重支持。。。 好,不过,pdf 要等连载完之后。 TQ2440板子的Uboot移植什么时候能出来啊? ↑↑↑↑↑↑↑↑↑↑↑↑↑ 支持,支持,支持 都是精品!!!!