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)"
區說明:
序號 分區名 分區大小 偏移地址  作用
0 bios 0x0010 0000(1M)  0x0000 0000 存放uboot
1 params 0x0002 0000(128K)  0x0010 0000 存儲環境變量
2 kernel 0x0040 0000(4M)  0x0012 0000 存放內核
3 root 0xfae0 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);
}


 

發佈了51 篇原創文章 · 獲贊 65 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章