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

接着前一节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);
}


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值