您的位置:首页 > 其它

Uboot移植之<二>------支持DM900、zImage内核启动

2014-01-17 17:43 489 查看
接着前一节uboot移植,继续做移植工作。

3.5 支持网络DM9000

uboot中已集成了网络驱动,支持了多种网卡设备的驱动。具体移植步骤如下:
第一步:查找uboot源码中drivers/net/目录是否支持DM9000芯片。经查:U-BOOT-2010.06版本已经对CS8900和DM9000X网卡有比较完善的代码支持。

第二步:修改配置文件inlude/configs/tx2440.h。 why?因为s3c24xx默认支持cs8900,所以需要将代码修改成支持DM9000。需要修改哪些地方?通过查看drivers/net中dm9000x.c和dm9000x.h可知:

1、
dm9000x.h需要CONFIG_DRIVER_DM9000宏定义依赖

2、
dm9000x.c需要CONFIG_DM9000_BASE、DM9000_DATA、DM9000_IO、CONFIG_DM9000_NO_SROM等

修改后代码:

#if 0 注释到cs8900

#define CONFIG_NET_MULTI

#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 */

#endif

#define CONFIG_DRIVER_DM9000 1

#define CONFIG_NET_MULTI 1

#define CONFIG_DM9000_NO_SROM 1

#define CONFIG_DM9000_BASE 0x20000300

#define DM9000_IO CONFIG_DM9000_BASE

#define DM9000_DATA (CONFIG_DM9000_BASE + 4)

3、给u-boot加上ping命令,用来测试网络通不通

#define CONFIG_CMD_PING

4、恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址

#define CONFIG_ETHADDR 12:34:56:78:90:ab

#define CONFIG_NETMASK 255.255.255.0

#define CONFIG_IPADDR 192.168.1.200 开发板的IP地址

#define CONFIG_SERVERIP 192.168.1.100 windows XP服务器的IP地址

说明:CONFIG_IPADDR指的是开发板的IP地址,CONFIG_SERVERIP指的是windows XP服务器的IP地址,这两个IP地址必须在同一网段上,在使用网络时,必须保证电脑服务器端的IP地址和CONFIG_SERVERIP的值保持一致。

第三步:增加板级网络设置。修改board/samsung/tx2440/tx2440.c

int board_eth_init(bd_t *bis)

{ int rc = 0;

#ifdef CONFIG_CS8900

rc = cs8900_initialize(0, CONFIG_CS8900_BASE);

#endif

#ifdef CONFIG_DRIVER_DM9000

rc = dm9000_initialize(bis);

#endif

return rc;

}

第四步:编译uboot.bin make –j4

编译运行时,ping 网关即 ping192.168.1.1

tx2440 # ping 192.168.1.106

dm9000 i/o: 0x20000300, id: 0x90000a46

DM9000: running in 16 bit mode

MAC: 08:00:3e:26:0a:5b

could not establish link

Using dm9000 device

ping failed; host 192.168.1.106 isnot alive 上述现象是无法ping通。

第五步:根据第四步提示无法ping通信息可知:无法建立link连接。解决办法:修改dm9000x.c相关代码。

1、找到“could not establish link”相关代码并屏蔽

#if 0

i = 0;

while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */

udelay(1000);

i++;

if (i == 10000) {

printf("could not establish link\n");

return 0;

}

}

#endif

屏蔽掉dm9000_halt函数中的内容:

static void dm9000_halt(struct eth_device *netdev)

{

#if 0

DM9000_DBG("%s\n", __func__);

/* RESET devie */

phy_write(0, 0x8000); /* PHY RESET */

DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */

DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */

DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */

#endif

再次编译后运行。

tx2440 # ping 192.168.1.106

dm9000 i/o: 0x20000300, id: 0x90000a46

DM9000: running in 16 bit mode

MAC: 08:00:3e:26:0a:5b

operating at unknown: 0 mode

Using dm9000 device

host 192.168.1.106 is
alive 表示可以ping通

下面截图是:使用TFTP工具网络下载程序



3.6加入MTD(NAND)分区

MTD分区是uboot中一个非常重要的概念,因为uboot的主要作用就是引导操作系统和烧写程序,那么我们的程序(指bootloader、内核、文件系统等)是如何存储在MTD设备中。
我们开发板上使用的MTD设备就是nand flash,我们在uboot中需要对 nand flash进行分区,这和我们电脑上硬盘的分区很相似。其实就是把nand flash划分几个地址区域,每个分区都有 分区名、分区的size、偏移地址。
有了分区,我们就可以规定:哪些分区存放什么程序,烧写nand flash的时候,就可以直接指定分区名,而不用在计算烧写的地址了。
在uboot中已经集成MTD分区的相关操作,我们只要加入MTD分区的配置和分区表的定义。
1、在tx2440.h配置文件中加入 MTDPARTS命令
#define CONFIG_CMD_MTDPARTS

#define CONFIG_MTD_DEVICE /*使用MTD设备*/

#define CONFIG_MTD_PARTITIONS
2、加入分区信息
#define MTDIDS_DEFAULT "nand0=nandflash0" /*设置mtdids变量*/

#define MTDPARTS_DEFAULT /*默认的MTD分区表*/

"mtdparts=nandflash0:1m@0(bios)," \

"128k(params)," \

"4m(kernel)," \

"-(root)"
分区说明:

序号分区名分区大小偏移地址 作用
0bios0x0010 0000(1M)0x0000 0000存放uboot
1params0x0002 0000(128K)0x0010 0000存储环境变量
2kernel0x0040 0000(4M)0x0012 0000存放内核
3root0xfae0 0000(其他)0x0052 0000存放文件系统
如果定义了CONFIG_CMD_MTDPARTS,就会编译common目录下的cmd_mtdparts.c文件,这需要用到drivers/mtd/mtdcore.c中函数。就要定义CONFIG_MTD_DEVICE和CONFIG_MTD_PARTITIONS这两项,才能编译mtdcore.c和mtdpart.c。
编译uboot,烧录nand flash中。uboot运行后,输入命令:help,即可看到mtdparts命令。
现象:使用命令mtdparts试了,未打印出我们设置分区信息。而使用mtdparts default,然后再用
mtdparts,便可打印出来分区信息。如下图:



解决方法:在程序中执行一次mtdparts default命令,才能使分区生效。修改 common/mian文件,加入mtdparts_init函数调用:
#ifdef CONFIG_AUTO_COMPLETE /*自动补齐命令*/

install_auto_complete();

#endif
#ifdef CONFIG_CMD_MTDPARTS /*nand flash分区初始化*/

extern int mtdparts_init(void); //该函数在common/cmd_mtdparts.c中定义

if (!(s=getenv("mtdparts")))//获取mtdparts环境变量值

{

run_command("mtdparts default", 0); //运行命令

}

else

{

mtdparts_init();

}

#endif

3.7支持内核启动

uboot默认只支持启动uImage,还不支持启动zImage。
具体移植步骤如下:
1、在arch/arm/cpu/lib目录下创建 boot_zImage.c文件,同时在同目录下Makefile加入编译选项:
COBJS-y += boot_zImage.o
给文件主要实现boot_zImage命令
#include <common.h>
#include <command.h>
#include <image.h>
#include <u-boot/zlib.h>
#include <asm/byteorder.h>
#include <asm/arch-s3c24x0/s3c2410.h> /*modify by yedapeng */
#include "asm/mach-types.h"/*modify by yedapeng */

#define LINUX_KERNEL_OFFSET   0x8000
#define LINUX_PARAM_OFFSET   0x100
#define LINUX_PAGE_SIZE    0x00001000
#define LINUX_PAGE_SHIFT   12
#define LINUX_ZIMAGE_MAGIC   0x016f2818
#define DRAM_SIZE    0x04000000

extern int nand_copy(unsigned char*, unsigned long, int);

/*
* Disable IRQs
*/
#define local_irq_disable()     \
({       \
unsigned long temp;    \
__asm__ __volatile__(     \
"mrs %0, cpsr  @ local_irq_disable\n" \
" orr %0, %0, #128\n"     \
" msr cpsr_c, %0"     \
: "=r" (temp)      \
:       \
: "memory", "cc");     \
})

static inline void cpu_arm920_cache_clean_invalidate_all(void)
{
__asm__(
" mov r1, #0\n"
" mov r1, #7 << 5\n"    /* 8 segments */
"1: orr r3, r1, #63 << 26\n"   /* 64 entries */
"2: mcr p15, 0, r3, c7, c14, 2\n" /* clean & invalidate D index */
" subs r3, r3, #1 << 26\n"
" bcs 2b\n"     /* entries 64 to 0 */
" subs r1, r1, #1 << 5\n"
" bcs 1b\n"     /* segments 7 to 0 */
" mcr p15, 0, r1, c7, c5, 0\n"  /* invalidate I cache */
" mcr p15, 0, r1, c7, c10, 4\n" /* drain WB */
);
}

void cache_clean_invalidate(void)
{
cpu_arm920_cache_clean_invalidate_all();
}

static inline void cpu_arm920_tlb_invalidate_all(void)
{
__asm__(
"mov r0, #0\n"
"mcr p15, 0, r0, c7, c10, 4\n" /* drain WB */
"mcr p15, 0, r0, c8, c7, 0\n" /* invalidate I & D TLBs */
);
}

void tlb_invalidate(void)
{
cpu_arm920_tlb_invalidate_all();
}

void  call_linux(long a0, long a1, long a2)
{
local_irq_disable(); /*禁止终端*/
cache_clean_invalidate(); /*使cache失效*/
tlb_invalidate(); /*刷新cache*/

__asm__(
"mov r0, %0\n"
"mov r1, %1\n"
"mov r2, %2\n"
"mov ip, #0\n"
"mcr p15, 0, ip, c13, c0, 0\n" /* zero PID */
"mcr p15, 0, ip, c7, c7, 0\n" /* invalidate I,D caches */
"mcr p15, 0, ip, c7, c10, 4\n" /* drain write buffer */
"mcr p15, 0, ip, c8, c7, 0\n" /* invalidate I,D TLBs */
"mrc p15, 0, ip, c1, c0, 0\n" /* get control register */
"bic ip, ip, #0x0001\n"  /* disable MMU */
"mcr p15, 0, ip, c1, c0, 0\n" /* write control register */
"mov pc, r2\n"
"nop\n"
"nop\n"
: /* no outpus */
: "r" (a0), "r" (a1), "r" (a2)  /*参数传递*/
: "r0","r1","r2","ip"
);
}

/*
* pram_base: base address of linux paramter 存放linux启动参数的地址即0x3000 0100
*/
static void setup_linux_param(ulong param_base)
{
struct param_struct *params = (struct param_struct *)param_base;
char *linux_cmd;

// printf("Setup linux parameters at 0x%08lx\n", param_base);
memset(params, 0, sizeof(struct param_struct));

params->u1.s.page_size = LINUX_PAGE_SIZE;  /* LINUX_PAGE_SIZE=0x00001000=4Kbyte 为什么是该值? */
params->u1.s.nr_pages = (DRAM_SIZE >> LINUX_PAGE_SHIFT); /* why? */

/* set linux command line */
/*bootargs--uboot传递给内核启动参数,具体定义在tx2440文件下:
CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 mem=64M"
*/
linux_cmd = getenv ("bootargs");
if (linux_cmd == NULL) {
printf("Wrong magic: could not found linux command line\n");
} else {
memcpy(params->commandline, linux_cmd, strlen(linux_cmd) + 1);
//  printf("linux command line is: \"%s\"\n", linux_cmd);
}
}

/*
* dst: destination address
* src: source
* size: size to copy
* mt: type of storage device
*/
static inline int copy_kernel_img(ulong dst, const char *src, size_t size)
{
int ret = 0;

ret = nand_copy((unsigned char *)dst,
(unsigned long)src, (int)size);

return ret;
}

int boot_zImage(ulong from, size_t size)
{
int ret;
ulong boot_mem_base; /* base address of bootable memory */
ulong to;
ulong mach_type;

boot_mem_base = 0x30000000;

/* copy kerne image */
to = boot_mem_base + LINUX_KERNEL_OFFSET; /*=0x3000 0000 + 0x8000*/
printf("Copy linux kernel from 0x%08lx to 0x%08lx, size = 0x%08lx ... ",
from, to, size);
/*函数原型:copy_kernel_img()
功能:将nand flash地址0x120000,长度为4Mbytekernel源码,复制到内存SDRAM地址0x3000 8000开始处
参数: 	1、to---目的地址(即内存地址)
2、form-源地址(即nand  flash地址)
3、size-复制的长度
返回值:0--表示成功复制,-1表示失败
*/
ret = copy_kernel_img(to, (char *)from, size);
if (ret) {
printf("failed\n");
return -1;
} else {
printf("Copy Kernel to SDRAM done,");
}

if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) {
printf("Warning: this binary is not compressed linux kernel image\n");
printf("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
} else {
//  printf("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
;
}

/* Setup linux parameters and linux command line */
/*1、设置启动参数地址=0x3000 0000 + 0x100=0x3000 0100*/
setup_linux_param(boot_mem_base + LINUX_PARAM_OFFSET);

/* 2、设置CPU机器型号 Get machine type */
mach_type = MACH_TYPE_S3C2440;
// printf("MACH_TYPE = %d\n", mach_type);

/* Go Go Go */
printf("NOW, Booting Linux......\n");
call_linux(0, mach_type, to);

return 0;
}

int do_boot_zImage (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
boot_zImage(0x120000,0x400000);
return 0;
}

U_BOOT_CMD(
boot_zImage, 3, 0, do_boot_zImage,
"boot_zImage - boot Linux 's zImage\n",
" - boot Linux 's zImage"
);

boot_zImage函数启动内核步骤:
1、调用copy_kernel_img函数,再调用nand_copy函数,读取nand flash中0x12 0000地址处开始的内核镜像(大小为0x400000即4Mbyte)拷贝到内存中0x3000
8000地址处。
2、设置启动参数setup_linux_param( )、获取match_type
3、调用call_linux函数,启动内核

注:用nand_copy函数位于board/samsung/tx2440/nand_read.c中。

3.8 支持yaffs2启动

uboot默认不支持烧写yaff2文件系统镜像,我们只需要加入烧写yaffs文件的命令,并修改nand的驱动中写操作。
1、在include/configs/tx2440.h中加入支持yafss的配置:
#define ENABLE_CMD_NAND_YAFFS 1 /*支持Yaffs文件系统*/

#define ENABLE_CMD_NAND_YAFFS_SKIPFB 1 /*支持烧写Yaffs时,跳过第一个好bolck*/
2、加入烧写yaffs的命令支持,修改common/cmd_nand.c文件:
else
ret = nand_write_skip_bad(nand, off, &size,
(u_char *)addr);
/* hailin add start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
}
else if ( s != NULL &&	!strcmp(s, ".yaffs") || !strcmp(s, ".yaffs2"))
{
if(read)
{	/*不支持 nand read.yaffs2 命令*/
printf("nand read.yaffs[2] is not provide temporarily!");
}
else
{
nand->rw_oob = 1;//支持yaff文件的OBB区的读写
#if defined(ENABLE_CMD_NAND_YAFFS_SKIPFB)
nand->skipfirstblk = 1; //跳过第一个好block
#else
nand->skipfirstblk = 0;
#endif
/**********************************************
**函数原型:int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
u_char *buffer)
**功能:向nand flash中写入镜像文件。写入过程中,遇到bad块跳过,写入下一个新的块。
**参数:	1、nand表示  nand_info_t型结构体
2、offset表示目的地址 即写入nand flash起始地址
3、*length表示写入镜像文件长度
4、*buffer表示源地址 即需要写入镜像文件在内存存储地址
**返回值: 0表示成功
***********************************************/
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
/*hailin add end*/

在U_BOOT_CMD中加入yaffs的选项
U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand, 。。。。。。。。。。。。。

#if defined(ENABLE_CMD_NAND_YAFFS)  /*hailin add start */
"nand read[.yaffs[2]] is not provide temporarily!\n"
"nand write[.yaffs[2]] addr off size - write the `size' byte yaffs image starting\n"
" at offset `off' from memory address `addr' (.yaffs2 for 2048+64 NAND)\n"
#endif							/*hailin add end */
加入了烧写yaffs文件系统命令后,我们即可执行nand write.yaffs命令来烧写 yaffs文件系统镜像。
3、修改 include/linux/mtd/mtd.h文件:
在mtd_info结构体中加入nand驱动中用到的两个变量:
struct mtd_info {
/* hailin add start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
u_char rw_oob;//支持OOB区的读取
u_char skipfirstblk;//跳过第一个好block
#endif
/*hailin add end*/

4、在drivers/mtd/nand/nand_utilc文件中修改 nand_write_skip_bad( )函数,添加对OOB区操作支持
size_t len_incl_bad;
u_char *p_buffer = buffer;
/* hailin add  start  添加OOB操作的支持*/
#if defined(ENABLE_CMD_NAND_YAFFS)
if(nand->rw_oob==1) {
size_t oobsize = nand->oobsize;
size_t datasize = nand->writesize;
int datapages = 0;
/*测试烧写yaffs文件印象是不是(64+2048byte)整数倍,如果是表示烧写数据正确*/
if (((*length)%(nand->oobsize+nand->writesize)) != 0) {
printf ("Attempt to write error length data!\n");
return -EINVAL;
}
datapages = *length/(datasize+oobsize); //烧写所需页数
*length = datapages*datasize; //计算烧写到nand flash数据区中长度
left_to_write = *length;
}
#endif
/*hailin add end*/


/*检测烧写文件在nand占用长度(包括坏块长度)*/
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(ENABLE_CMD_NAND_YAFFS) //add by hailin
/*烧写非yaffs的镜像文件,例如uboot、kernel文件*/
if (len_incl_bad == *length) {//当在烧写文件在nand占用长度 == 烧写文件长度,表示在nand这段储存空间无bad block
rval = nand_write (nand, offset, length, buffer);
if (rval != 0)
printf ("NAND write to offset %llx failed %d\n",
offset, rval);

return rval;
}
#endif //add by hailin

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;
}
/*hailin add start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
if(nand->skipfirstblk==1) {//跳过第一个好block
nand->skipfirstblk=0;
printf ("Skip the first good block %08lx\n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}
#endif
/*hailin add end*/

if (left_to_write < (nand->erasesize - block_offset))
write_size = left_to_write;
else
write_size = nand->erasesize - block_offset;

printf("\rWriting at 0x%08lx --",offset); //hailin add

rval = nand_write (nand, offset, &write_size, p_buffer);
if (rval != 0) {
printf ("NAND write to offset %llx failed %d\n",
offset, rval);
*length -= left_to_write;
return rval;
}

left_to_write -= write_size;

printf("%d%% is complete.",100-(left_to_write/(*length/100))); //add by yedapeng
offset        += write_size;
//p_buffer      += write_size;

/* add for hailin start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
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

}

/* add for hailin end*/
return 0;
}


5、修改drivers/mtd/nand/nand_base.c文件:
在nand_do_write_ops函数加入

在nand_write函数中加入
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;
/*add by hailin begin*/
/*
以K9F2G08U0B为例,该nand flash一页为(2K+64)字节(2K 表示的是 main 区容量,
64 表示的是 spare 区容量),它的一块为 64 页,而整个设备包括了2048个块。
则mtd->oobsize=64,mtd->writesize=2048
*/
#if defined(ENABLE_CMD_NAND_YAFFS)
int oldopsmode = 0;
u_char data_buffer[len] ,oob_buffer[len/(mtd->writesize)*mtd->oobsize];
if(mtd->rw_oob==1) {
size_t oobsize = mtd->oobsize;
size_t datasize = mtd->writesize;
int i = 0;
uint8_t oobtemp[oobsize];
int datapages = 0;
datapages = len/(datasize);
/*
下面for循环函数作用:将buf内存中yaffs文件中data区和oob区,
分别存储在data_buffer和obb_buffer两个内存块。
具体步骤:
1、将buf内存地址中data数据复制到data_buffer中
2、将buf内存地址中oob数据复制到oob_buffer中
3、复制次数=页数datapage
*/
for(i=0;i<(datapages);i++) {
memcpy((void *)(data_buffer+i*datasize),
(void *)(buf+datasize*i+i*oobsize),
datasize);
memcpy((void *)(oob_buffer+i*oobsize),
(void *)(buf+datasize*(i+1)+i*oobsize),
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;
/* hailin add start*/
if(mtd->rw_oob!=1) {//是否支持OOB区读写?
chip->ops.datbuf = (uint8_t *)buf; //
}
else{
chip->ops.datbuf = (uint8_t *)data_buffer;//支持OOB区读写,设置data区
}

#if defined(ENABLE_CMD_NAND_YAFFS)
if(mtd->rw_oob!=1) {//是否支持OOB区读写?
chip->ops.oobbuf = NULL;
} else {
//chip->ops.oobbuf = (uint8_t *)(buf+len);
chip->ops.oobbuf = (uint8_t *)oob_buffer;//设置OOB区
chip->ops.ooblen = mtd->oobsize;//设置OOB区长度
oldopsmode = chip->ops.mode;
chip->ops.mode = MTD_OOB_RAW;
}
#else
chip->ops.oobbuf = NULL; //未定义NAND_YAFFS命令,则不使用OOB区
#endif
/*hailin add end*/

//chip->ops.datbuf = (uint8_t *)buf; /*此处是否需要屏蔽,需要屏蔽*/
//chip->ops.oobbuf = NULL;/*此处是否需要屏蔽,需要屏蔽*/

ret = nand_do_write_ops(mtd, to, &chip->ops);//执行写操作

*retlen = chip->ops.retlen;

nand_release_device(mtd);//释放nand flash
/*hailin add start*/
#if defined(ENABLE_CMD_NAND_YAFFS)
chip->ops.mode = oldopsmode;
#endif
/*hailin add end*/
return ret;
}

将存放yaffs文件的buf中 data和OOB数据的分离出来,方便向nand flash写入 。(该代码参考天祥电子)

for(i=0;i<(datapages);i++)

{

memcpy( (void *)(data_buffer+i*datasize), (void *)(buf+datasize*i+i*oobsize), datasize);

memcpy( (void *)(oob_buffer+i*oobsize), (void *)(buf+datasize*(i+1)+i*oobsize),oobsize);

}

执行过程如下:



疑问:1、nand falsh中何时设置nand->writesize,nand->oobsize,nand->erasesize??

3.9 支持menu菜单选项

下面以添加menu命令(启动菜单)为例讲解U-Boot添加命令的方法。
(1) 建立common/cmd_menu.c
习惯上通用命令源代码放在common目录下,与开发板专有命令源代码则放在board/<board_dir>目录下,并且习惯以“cmd_<命令名>.c”为文件名。
源码:
#include <common.h>
#include <command.h>
static char awaitkey(unsigned long delay, int* error_p)
{
int i;
char c;
if (delay == -1) {
while (1) {
if (tstc()) /* we got a key press */
return getc();
}
}
else {
for (i = 0; i < delay; i++) {
if (tstc()) /* we got a key press */
return getc();
udelay (10*1000);
}
}
if (error_p)
*error_p = -1;
return 0;
}

void main_menu_usage(void)
{
printf("\r\n######## Hotips TFTP DownLoad for TX2440A ########\r\n");
printf("\r\n");
printf("[1] 下载 u-boot.bin       写入 Nand Flash\r\n");
printf("[2] 下载 Linux(zImage)    内核镜像写入 Nand Flash\r\n");
printf("[3] 下载 yaffs2(fs.yaffs2) 文件系统镜像写入 Nand Flash\r\n");
printf("[4] 下载 Linux(uImage)    内核镜像到内存并运行\r\n");
printf("[5] 重启设备\r\n");
printf("[6] 擦除nand flash\r\n");
printf("[q] 退出菜单\r\n");
printf("\r\n");
printf("输入选择: ");
}

void menu_shell(void)
{
char cmd_buf[200];
char c;
//unsigned char j=1;
while (1)
{
main_menu_usage(); //打印菜单
c = awaitkey(-1, NULL);
printf("%c\n", c);
switch (c)
{
case '1': //输入‘1’  下载 u-boot.bin
{  /* 1、将镜像通过tftp下载到开始地址0x3000 0000的内存中
2、擦除需要写入空间
3、将存储在内存中镜像 烧写到起始地址为0x0的nand flash中
*/
strcpy(cmd_buf,"tftp 0x30000000 u-boot.bin;nand erase 0 0x100000;nand write 0x30000000 0x0 0x100000");
run_command(cmd_buf,0);
break;
}

case '2': //输入‘2’ 下载 Linux(zImage_2.6.31.bin)
{
strcpy(cmd_buf,"tftp 0x30000000 zImage_2.6.31.bin;nand erase 0x120000 0x400000;nand write 0x30000000 0x120000 0x400000");
run_command(cmd_buf,0);
break;
}

case '3': //输入‘3’  下载 yaffs2(root_qtfs.bin)
{
strcpy(cmd_buf,"tftp 0x30000000 root_qtfs.bin;nand erase 0x520000 0x3ffffc0;nand write.yaffs 0x30000000 0x520000 0x3ffffc0");
run_command(cmd_buf,0);
break;
}

case '4': //输入‘4’  下载 Linux(uImage)    内核镜像到内存并运行
{
strcpy(cmd_buf,"tftp 0x30000000 zImage_2.6.31.bin;bootm 0x30000000");
run_command(cmd_buf,0);
break;
}

case '5': //输入‘5’  重启系统
{
strcpy(cmd_buf,"boot_zImage");
run_command(cmd_buf,0);
break;
}

case '6': //输入‘6’  擦除nand flash
{
strcpy(cmd_buf,"nand erase 0x120000 0xc800000");
run_command(cmd_buf,0);
break;
}
default:break;
}
if(c=='q') { break;} //退出菜单
}
}

int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
menu_shell();
return 0;
}

U_BOOT_CMD(
menu, 3, 0, do_menu,
"menu - display a menu, to select the items to do something\n",
" - display a menu, to select the items to do something"
);


(2) 定义“menu”命令
在cmd_menu.c中使用如下的代码定义“menu”命令:
_BOOT_CMD(
menu, 3, 0, do_menu,
"menu - display a menu, to select the items to do something\n",
" - display a menu, to select the items to do something"
);
其中U_BOOT_CMD命令格式如下:
U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)
各个参数的意义如下:
name:命令名,非字符串,但在U_BOOT_CMD中用“#”符号转化为字符串
maxargs:命令的最大参数个数
rep:是否自动重复(按Enter键是否会重复执行)
cmd:该命令对应的响应函数
usage:简短的使用说明(字符串)
help:较详细的使用说明(字符串)
在内存中保存命令的help字段会占用一定的内存,通过配置U-Boot可以选择是否保存help字段。若在include/configs/tx2440.h中定义了CONFIG_SYS_LONGHELP宏,则在U-Boot中使用help命令查看某个命令的帮助信息时将显示usage和help字段的内容,否则就只显示usage字段的内容。
U_BOOT_CMD宏在include/command.h中定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
“##”与“#”都是预编译操作符,“##”有字符串连接的功能,“#”表示后面紧接着的是一个字符串。
其中的cmd_tbl_t在include/command.h中定义如下:
struct cmd_tbl_s {
char *name; /* 命令名 */
int maxargs; /* 最大参数个数 */
int repeatable; /* 是否自动重复 */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 响应函数 */
char *usage; /* 简短的帮助信息 */
#ifdef CONFIG_SYS_LONGHELP
char *help; /* 较详细的帮助信息 */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* 自动补全参数 */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s cmd_tbl_t;
一个cmd_tbl_t结构体变量包含了调用一条命令的所需要的信息。
其中Struct_Section在include/command.h中定义如下:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
凡是带有__attribute__ ((unused,section (".u_boot_cmd"))属性声明的变量都将被存放在".u_boot_cmd"段中,并且即使该变量没有在代码中显式的使用编译器也不产生警告信息。
在U-Boot连接脚本u-boot.lds中定义了".u_boot_cmd"段:
. = .;
__u_boot_cmd_start = .; /*将 __u_boot_cmd_start指定为当前地址 */
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .; /* 将__u_boot_cmd_end指定为当前地址 */
这表明带有“.u_boot_cmd”声明的函数或变量将存储在“u_boot_cmd”段。这样只要将U-Boot所有命令对应的cmd_tbl_t变量加上“.u_boot_cmd”声明,编译器就会自动将其放在“u_boot_cmd”段,查找cmd_tbl_t变量时只要在__u_boot_cmd_start与__u_boot_cmd_end之间查找就可以了。
因此“menu”命令的定义经过宏展开后如下:
cmd_tbl_t __u_boot_cmd_menu __attribute__ ((unused,section (".u_boot_cmd"))) = {menu, 3, 0, do_menu, "menu - display a menu, to select the items to do something\n", " - display a menu, to
select the items to do something"}
实质上就是用U_BOOT_CMD宏定义的信息构造了一个cmd_tbl_t类型的结构体。编译器将该结构体放在“u_boot_cmd”段,执行命令时就可以在“u_boot_cmd”段查找到对应的cmd_tbl_t类型结构体。
(3) 实现命令的函数
在cmd_menu.c中添加“menu”命令的响应函数的实现。具体的实现代码略:
int do_menu (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
/* 实现代码略 */
}
(4) 将common/cmd_menu.c编译进u-boot.bin
在common/Makefile中加入如下代码:
COBJS-$( CONFIG_CMD_MENU) += cmd_menu.o
在include/configs/tx440.h加入如代码:
#define CONFIG_CMD_MENU
重新编译下载U-Boot就可以使用menu命令了
(5)menu命令执行的过程
在U-Boot中输入“menu”命令执行时,U-Boot接收输入的字符串“menu”,传递给run_command函数。run_command函数调用common/command.c中实现的find_cmd函数在__u_boot_cmd_start与__u_boot_cmd_end间查找命令,并返回menu命令的cmd_tbl_t结构。然后run_command函数使用返回的cmd_tbl_t结构中的函数指针调用menu命令的响应函数do_menu,从而完成了命令的执行。

3.10 其他

3.10.1支持Tab补全命令和上下键调用历史命令

1、include/configs/TX2440.h中添加代码

#define CONFIG_CMDLINE_EDITING 支持上下键调用历史命令

#define CONFIG_AUTO_COMPLETE 支持Tab补全命令

2、再次编译,建议每次编译前都清除一下上次编译留下的中间代码:

#make distclean

#make tx2440_config

#make

3.10.2输出FLCK HCLK PCLK频率

在\arch\arm\lib\board.c中添加

void  print_cpuinfo(void)
{ unsigned long fclk,hclk,pclk,uclk;
fclk=get_FCLK();
hclk=get_HCLK();
pclk=get_PCLK();
uclk=get_UCLK();
printf("FCLK=%ldMHz,HCLK=%ldMHz,PCLK=%ldMHz,UCLK=%ldMHz\n",fclk/1000000,hclk/1000000,pclk/1000000,uclk/1000000);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: