Uboot移植之<一>------S3C2440平台搭建(支持Norflash和nand flash)

此文章参考天祥电子uboot移植文档和网上各位大侠优秀移植文章,在此感谢他们。

一、    环境

硬件信息软件环境
开发板:TX2440A操作系统:ubuntu 12.04
CPU:    S3C2440Uboot版本:2010.06
Nand flash:K9F2G08U0B (256M+8M)x8bit交叉编译器:arm-linux-gcc  4.4.1
Nor flash: EN29LV160AB (2M) x8bitUSB下载工具:DNW.exe
SDRAM:      H57V2562GTR-75C x 2 (32M) x 2TFTP下载工具:TFTPD32.exe
网卡:      DM9000E

二、    移植前准备

移植之前先了解uboot目录及程序流程图。
uboot目录说明:
主流程图:
start.S流程图

       

2.1获取uboot-2010.06源码

通过http://www.denx.de/wiki/U-Boot/SourceCode 获取源码,解压源码 tar zxvf u-boot-2010.06.tar.gz。

2.2交叉编译器安装

1)解压交叉工具 arm920t-eabi-4.1.2.tar.gz,生成opt/toolchains/arm920t-eab文件夹
   #tar -jxvf arm920t-eabi-4.1.2.tar.gz

2)在根目录下/usr/local/下面创建目录 arm/(注意,最好是放到这个目录,不然在以后的编译过程中可能出现一些错误)。

3)将opt/toolchains/文件夹下将arm920t-eabi 移动到 /usr/local/ arm/下面。

   # cd opt/toolchains/
   #cp -rf  /usr/local/arm
   现在交叉编译程序集都在/usr/local/arm/arm920t-eabi/bin下面了
4) 设置环境变量,把交叉编译器的路径加入到PATH。(有三种方法,强烈推荐使用方法一)
  方法一:修改/etc/bash.bashrc文件已经验证是可行的。
           #gedit~/.bashrc

            在最后加上:export PATH=/usr/local/arm/arm920t-eabi/bin:$PATH

(如果不能编辑,先修改相关文件的权限使用chmod命令。)

方法二:修改/etc/profile文件:验证后:不可行
# vim /etc/profile

增加路径设置,在末尾添加如下,保存/etc/profile文件:
export PATH=$PATH:/usr/local/arm/arm920t-eabi/bin

5)立即使新的环境变量生效,不用重启电脑:
  对应方法一:#source /root/.bashrc  已经验证是可行的。
  对应方法二:# source /etc/profile
6)检查是否将路径加入到PATH:
    # echo $PATH
   显示的内容中有/usr/local/arm/bin,说明已经将交叉编译器的路径加入PATH。至此,交叉编译环境安装完成。
  7)测试是否安装成功
   # arm-linux-gcc -v   上面的命令会显示arm-linux-gcc信息和版本
Using built-in specs.

Target: arm-angstrom-linux-gnueabi

……………………………………………………………………………………………………………………………………….

Thread model: posix

gcc version 4.1.2


2.3建立自己开发板项目

首先在源码寻找同开发板匹配的配置,从该源码中arch、board文件夹可知:只有smdk2410比较接近开发板tx2440。

1、具体修改那些地方呢??通过查找源码中与“smdk2410”匹配的文件和文件夹,来确定修改地方。

修改源码:    1、uboot-2010.06/board/Samsung/smdk2410文件夹 

          2、uboot-2010.06/board/Samsung/smdk2410/smdk2410.c

           3、uboot-2010.06/include/configs/smdk2410.h

将上述中smdk2410全部替换为tx2440,其中对应Makefile也需要修改。

具体操作:

     1、cp -rf  board/Samsung/smdk2410    board/Samsung/tx2440   

     2、cp -rf board/Samsung/smdk2410/smdk2410.c    board/Samsung/smdk2410/tx2440.c

    3、cp–rf include/configs/smdk2410.h include/configs/tx2440.h

    4、修改board/Samsung/tx2440中Makefile 第28行:

COBJS := TX2440.oflash.o<—将smdk2410.o改名为tx2440.o

2、除了上述修改,还需要增加开发板配置文件。即根目录下Makefile中增加 tx2440_config配置文件和指定交叉编译器。

操作:1、指定交叉编译器 CROSS_COMPILE ?=arm-linux-

2、增加配置文件

tx2440_config: unconfig

@$(MKCONFIG)$(@:_config=) arm arm920t TX2440 samsung s3c24x0

说明:

arm:CPU的架构(ARCH)

arm920t:CPU的类型

tx2440 :对应在board目录下建立新的开发板项目的目录

samsung:新开发板项目的上级目录,如直接在board下建立新的开发板项目,则这里应为NULL

s3c24x0:CPU型号

注意:编译选项格式的第二行要用Tab键开始,否则编译会出错

3、测试编译新建的TX2440开发板项目:#maketx2440_config

如果出现Configuring for TX2440 board...则表示设置正确

#make  -j4

编译后在根目录下会出现u-boot.bin文件,则u-boot移植的第一步就算完成了。其中j4可以理解成:启用4个cpu去编译。提高编译速度。

三、移植

3.1设置时钟、中断配置、存储控制器初始化

本人计划开发板的工作频率FCLK=400MHz,HCLK=100MHz,PCLK=50MHz。源码中哪些涉及时钟设置和中断配置、内存控制器?通过查询源码可知:1、在start.S、speed.c、tx2440.c涉及时钟设置 2、在start.S涉及中断配置。3、在lowlevel_init.S涉及存储控制器初始化
         1、在start.S(路径:arch/arm/cpu/arm920t/)修改处如下:
#if defined(CONFIG_S3C2400)
#define pWTCON	0x15300000
#define INTMSK	0x14400008	/* Interupt-Controller base addresses */
#define CLKDIVN	0x14800014	/* clock divisor register */
/* 1、增加s3c2440寄存器配置 */
#else if defined(CONFIG_S3C2440)
#define pWTCON		0x53000000  /*watch dog    */
#define INTMSK		0x4A000008	/* Interupt-Controller base addresses */
#define INTSUBMSK	0x4A00001C
/* clock  register */
#define MPLLCON   	0x4C000004
#define UPLLCON   	0x4C000008
#define CLKDIVN	  	0x4C000014
#define CAMDIVN   	0x4C000018
#define MDIV_400M 	(0x5c<<12)
#define PDIV_400M 	(0x01<<4)
#define SDIV_400M 	0x01
/* 通过GPF0-3的IO控制led1-4,来确认程序执行情况*/
#define GPFCON		0x56000050
#define GPFDAT		0x56000054
#define GPFUP		0x56000058
#endif
    /*disable   watch dog*/
	ldr	r0, =pWTCON
	mov	r1, #0x0
	str	r1, [r0]
	/* mask all IRQs by setting all bits in the INTMR - default*/
	mov	r1, #0xffffffff
	ldr	r0, =INTMSK
	str	r1, [r0]
#if defined(CONFIG_S3C2410)
	ldr	r1, =0x3ff
	ldr	r0, =INTSUBMSK
	str	r1, [r0]
#endif
	/*2、设置S3C2440的中断屏蔽寄存器*/
#if defined(CONFIG_S3C2440)
	ldr	r1, =0x7fff   /*s3c2440的INTSUBMSK有效位bit[14:0]*/
	ldr	r0, =INTSUBMSK
	str	r1, [r0]
#endif
#if defined(CONFIG_S3C2440)
/* 3、set  clock,
	FLCK=400MHz,FCLK:HCLK:PCLK = 1:4:8
   设置分频系数相关寄存器CLKDIVN和CAMDIVN,
   其中CAMDIVN使用默认值即可。
*/
  	ldr	r0, =CLKDIVN
	mov	r1, #5
	str	r1, [r0]
    /*当CLKDIVN[2:1]设置值非0时,CPU总线模式为快总线模式,即CPU工作频率是HCLK。
    可以通过下面三句程序使CPU工作FCLK的异步总线模式*/
    /*MMU_SetAsync BusMode*/
	mrc p15, 0, r1, c1, c0, 0
	orr r1, r1, #0xc0000000
	mcr p15, 0, r1, c1, c0, 0

  	ldr	r1, =MPLLCON
	mov	r2, #MDIV_400M
	add r2,r2,#PDIV_400M
	add r2,r2,#SDIV_400M
	str	r2, [r1] 
#else
	/* FCLK:HCLK:PCLK = 1:2:4 */
	/* default FCLK is 120 MHz ! */
	ldr	r0, =CLKDIVN
	mov	r1, #3
	str	r1, [r0]

	mrc p15, 0, r1, c1, c0, 0
	orr r1, r1, #0xc0000000
	mcr p15, 0, r1, c1, c0, 0
#endif	
         由start.S中使用CONFIG_S3C2440,而配置文件tx2440.h(路径:include/configs/)未定义。所以增加定义
         #define CONFIG_S3C2440 1 /* on a TX2440 Board */

     2、板级tx2440.c(路径:board/samsung/tx2440)
       1)修改时钟寄存器设置值
#define FCLK_SPEED 1   /*设置FLCK速度模式*/
#if FCLK_SPEED==0		/* Fout = 203MHz, Fin = 12MHz for Audio */
#define M_MDIV	0xC3
#define M_PDIV	0x4
#define M_SDIV	0x1
#elif FCLK_SPEED==1		/* Fout = 400MHz */
#define M_MDIV	0x5c
#define M_PDIV	0x01
#define M_SDIV	0x01
#endif
          具体时钟设置由clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
                                     clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
      2)修改board_init (void)函数架构编码
               /* arch number of SMDK2410-Board */
               gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
   3、speed.c作用:通过读取MPLLCON、CLKDIV等寄存器中设置值,来计算FCLK、HCLK、 PCLK。
         因为该处1、默认FCLK是s3c2410计算方式,与2440计算不同;2、默认分频比1:2:4,而实际设置是1:4:8。
          1)在get_PLLCLK()函数,修改FCLK计算方式:
         static ulong get_PLLCLK(int pllreg)
        { 。。。。。。。。。。。。。。。。。。。。。。。。。。
      if (pllreg == MPLL) {  return (CONFIG_SYS_CLK_FREQ *2*m) / (p << s);         }//其中p<<s 等同 p* 2^s,即p左移s位
   
    else if (pllreg == UPLL) {return (CONFIG_SYS_CLK_FREQ * m) / (p << s);} 
       }
         其中s3c2410的FCLK=(CONFIG_SYS_CLK_FREQ *m) / (p << s)
        2)在get_HCLK()函数中,修改HCLK计算值。即增加下面程序
          ulong get_HCLK(void)
          {。。。。。。。。。。。。。。。。。。。。。。。。。
           #if defined(CONFIG_S3C2440)
               return (readl(&clk_power->CLKDIVN) & 4) ? get_FCLK() / 4 : get_FCLK(); //计算HCLK=FCLK/4=100MHz
         #endif
           }
      4、存储控制器初始化由 lowlevel_init.S完成。需要修改以下地方:
              1)位宽设置
                    #define B1_BWSCON (DW16)     bank0接16位宽的nor flash
 
               2)修改SDRAM刷新率 REFCNT
                    #define REFCNT   1267   该值计算REFCNT=2048+1 -SDRAM芯片刷新周期*HCLK=2049-7.8125*100=1267
                   
 
 

3.2 启动方式识别(Nor flash和nand flash)

nand flash启动原理:S3C2440的nand flash控制器将nand flash前4K代码数据搬运到内部SRAM中(地址:0x4000 0000),同时将这块SRAM地址映射到0x0000 0000地址。CPU从地址0x0000 0000位置开始运行程序。
nor flash启动原理:0x0000 0000就是nor flash物理地址,norflash中程序就从该处执行,不涉及数据拷贝和地址映射。
地址映射图:
利用nand flash和nor flash不同启动原理,在启动时,将0x4000 0000 -0x4000 1000某些位置零,然后读取地址0x0000 0000-0x000 1000相应位。如果读取值为0,表示nand flash启动;如果还是原来数据,表示nor flash启动。
选取什么位置的数据???。经过查看源码,选择了在start.S文件开头,全局中断向量之后第57行的变量:.balignl 16,0xdeadbeef
选取该位置的理由:1、该位置的数据确定值0xdeadbeef
2、位置固定:0x0000 003c(0x4000 003c)
具体方法:
第一步:启动时,将地址0x4000 003c开始的4个字节清0
第二步:读取并判读  地址0x0000 003c开始的4个字节中数据。如果为0,表示nand flash启动,同时需要恢复之前数据0xdeadbeef。如果为0xdeadbeef,表示nor flash启动。
注:如果是nand flash启动,前4K内容比较重要存放初始化代码(所以nand flash厂家必须保证前4K无坏块),需要验证复制到SDRAM的代码中前4K   同  内部SRAM中4K(由nand flash控制器控制)是否一致。

代码修改如下:
1、增加启动方式标志位bBootFrmNORFlash   (在strat.S的第92行)
.globl bBootFrmNORFlash
bBootFrmNORFlash:
.word 0
2、增加识别启动方式、(在sart.S第224行)
/*4、判断uboot映像位置:是否复制到SDRAM  */
				         /* relocate U-Boot to RAM	    */
	adr	r0, _start		/* r0 <- current position of code   */
	ldr	r1, _TEXT_BASE	/* test if we run from flash or RAM */
	cmp	r0, r1			/* don't reloc during debug         */
	beq	stack_setup
	
	/*LED1 ON */		
	ldr	r0, =GPFDAT   	
	mov	r1, #0x00FE	
	str	r1, [r0]

/*5、设置堆栈,为下面的nand flash的C操作函数提供环境*/
ldr sp, DW_STACK_START @ setup stack pointer  
mov fp, #0 @ no previous frame, so fp=0 


/* 6、判断启动方式 */
ldr r1, =( (4<<28)|(3<<4)|(3<<2) ) /* address of Internal SRAM 0x4000003C*/
mov r0, #0 /* r0 = 0 */
str r0, [r1]
mov r1, #0x3c /* address of 0x0000003C*/
ldr r0, [r1]
cmp r0, #0 /*判断0x0000003C所指向的4个字节是否被清0*/
bne norflash /*没有被清0,就是在Nor Flash中启动,则跳到Nor Flash的重定向代码处relocate*/

	/*LED 2 ON*/		
	ldr	r0, =GPFDAT   	
	mov	r1, #0x00FD	
	str	r1, [r0]
	
/* recovery */
ldr r0, =(0xdeadbeef) /*被清0,就是在Nand Flash中启动*/
ldr r1, =( (4<<28)|(3<<4)|(3<<2) ) /*恢复0x4000003C指向的4字节的数据0xdeadbeef*/
str r0, [r1]
 


/*7、nand flash 初始化*/
bl nandflash_init
	/*LED4 ON */		
	ldr	r0, =GPFDAT   	
	mov	r1, #0x00F7	
	str	r1, [r0]

/*8、nand flash 代码复制SDRAM,
     其中r0-r3提供nand_copy函数的3个参数*/
   
#define LENGTH_UBOOT 0x100000
ldr r0, =TEXT_BASE   
mov r1, #0x0
mov r2, #LENGTH_UBOOT

bl nand_copy

tst r0, #0x0  /*nand_copy返回值传递给r0,若返回0,表示uboot映像成功复制*/
beq ok_nand_read
          
bad_nand_read:
loop2:

	/*LED1 2 3 ON*/		
	ldr	r0, =GPFDAT   	
	mov	r1, #0x00F8
	str	r1, [r0]
	
	b loop2 
	
/*检测Boot Internal SRAM即和SDRAM中的前4K数据是否相等,以保证数据无错。*/ 
/* 因为nand flash 启动时,自动将steping的4K内容映射到Boot Internal SRAM中。*/ 
/* 而之前将uboot.bin搬运SDRAM。前4K内容比较重要,主要实现硬件初始化、代码搬运

。为第二阶段做准备。*/
/* 所以前4K内容需要校验 是否正确。如果不正确,执行notmatch。*/
ok_nand_read:

	/*LED1 2 ON*/		
	ldr	r0, =GPFDAT   	
	mov	r1, #0x00FC
	str	r1, [r0]
	

	mov r0, #0
	ldr r1, =TEXT_BASE
	mov r2, #0x1000 @ 4 bytes * 1024 = 4K-bytes
go_next:
	ldr r3, [r0], #4
	ldr r4, [r1], #4
	teq r3, r4
	bne notmatch
	subs r2, r2, #4
	beq stack_setup
	bne go_next
	
notmatch:   /* Boot Internal SRAM即和SDRAM中的前4K数据,不相等*/
	loop3:
	
	/*LED3 4 ON*/		
	ldr	r0, =GPFDAT   	
	mov	r1, #0x00F3
	str	r1, [r0]
	
	b loop3 @ infinite loop

/*9、Nor flash启动*/
norflash:
	/*LED3 ON*/		
	ldr	r0, =GPFDAT   	
	mov	r1, #0x00FB
	str	r1, [r0]

     /*检测norflash中0x0000003C地址中:是否是0xdeadbeef魔数*/
    mov r1, #0x3c /* address of 0x0000003C*/
	ldr r0, [r1]
 	ldr r1, =(0xdeadbeef) 
 	cmp r0, r1  
	bne loop3   /* 如果不是魔数值,表示异常,进入死循环   */
	
	/* uboot映像由norflash复制到SDRAM中  */

	adr r0, _start /* r0 <- current position of code */
	ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
	ldr r2, _armboot_start
	ldr r3, _bss_start
	sub r2, r3, r2 /* r2 <- size of armboot */
	add r2, r0, r2 /* r2 <- source end address */

copy_loop:
	ldmia	r0!, {r3-r10}		/* copy from source address [r0]    */
	stmia	r1!, {r3-r10}		/* copy to   target address [r1]    */
	cmp	r0, r2			/* until source end addreee [r2]    */
	ble	copy_loop

SetBootFlag:
	ldr r0, =bBootFrmNORFlash
	mov r1, #1 /*从Nor启动,将标志设置为1*/
	str r1, [r0]
3.3支持Nor flash
开发板的nor flash型号是EN29LV160AB (2M) x8bit,而配置文件tx2440未定义。所以修改tx2440.h
      1、增加EN29LV160AB配置信息(修改tx2440.h)
#if 0
#define CONFIG_AMD_LV400	1	/* uncomment this if you have a LV400 flash */
#define CONFIG_AMD_LV800	1	/* uncomment this if you have a LV800 flash */
#endif

#define CONFIG_AMD_LV160	1	/* uncomment this if you have a LV160 flash */

#define CONFIG_SYS_MAX_FLASH_BANKS	1	/* max number of memory banks */
#ifdef CONFIG_AMD_LV800
#define PHYS_FLASH_SIZE		0x00100000 /* 1MB */
#define CONFIG_SYS_MAX_FLASH_SECT	(19)	/* max number of sectors on one chip */
#define CONFIG_ENV_ADDR		(CONFIG_SYS_FLASH_BASE + 0x0F0000) /* addr of environment */
#endif
#ifdef CONFIG_AMD_LV400
#define PHYS_FLASH_SIZE		0x00080000 /* 512KB */
#define CONFIG_SYS_MAX_FLASH_SECT	(11)	/* max number of sectors on one chip */
#define CONFIG_ENV_ADDR		(CONFIG_SYS_FLASH_BASE + 0x070000) /* addr of environment */
#endif

#ifdef CONFIG_AMD_LV160
#define PHYS_FLASH_SIZE		0x00200000 /* 2Mbyte */
#define CONFIG_SYS_MAX_FLASH_SECT	(35)	/* max number of sectors on one chip */
#define CONFIG_ENV_ADDR		(CONFIG_SYS_FLASH_BASE + 0x100000 ) /* addr of environment */
#endif
2、增加flash.c中EN29LV160AB匹配信息 (路径:board/samsung/tx2440)
1)第74行获取flash ID时
#elif defined(CONFIG_AMD_LV800)
(AMD_MANUFACT & FLASH_VENDMASK) |
(AMD_ID_LV800B & FLASH_TYPEMASK);
#elif defined(CONFIG_AMD_LV160)
(AMD_MANUFACT & FLASH_VENDMASK) |
(AMD_ID_LV160B & FLASH_TYPEMASK);
2)第152行输出falsh信息
case (AMD_ID_LV800B & FLASH_TYPEMASK):
printf ("1x Amd29LV800BB (8Mbit)\n");
break;
case (AMD_ID_LV160B & FLASH_TYPEMASK):
printf ("1x Amd29LV160B (2M)\n");
break;

3.4支持Nand flash

     uboot支持nand flash需要修改 arch架构board板级driver驱动级配置文件tx2440.h链接脚本uboot.lds五个方面。

    1、Arch架构:

         1)start.S(路径:arch/arm/arm920t/ )

                增加支持nand flash启动和识别代码。其中:启动代码在board板级提供。

        2)增加 s3c2440对应nand flash控制器的寄存器,

           (路径:arch/arm/include/s3c24x0/s3c24x0.h,该头文件用来定义是s3c24x0系列的SOC上所有寄存器资源)。

             为什么不能使用 2410的nand flash定义??因为2440的nand flash的控制寄存器比2410多。

         struct s3c2440_nand {

                        u32 NFCONF;

                        u32 NFCONT;

                        u32 NFCMD;

                        u32 NFADDR;

                        u32 NFDATA;

                        u32 NFMECCD0;

                        u32 NFMECCD1;

                        u32 NFSECCD;

                        u32 NFSTAT;

                        u32 NFESTAT0;

                        u32 NFESTAT1;

                        u32 NFMECC0;

                        u32 NFMECC1;

                        u32 NFSECC;

                        u32 NFSBLK;

                        u32 NFEBLK;

                  };

          3)增加上面寄存器  地址定义和地址接口函数

            (路径:arch/arm/include/s3c24x0/s3c2410.h,该头文件用来定义寄存器地址和地址接口函数)

          #define S3C2440_NAND_BASE 0x4E000000

          static inline struct s3c2410_nand *s3c2440_get_base_nand(void)

           { return (struct s3c2410_nand *)S3C2440_NAND_BASE;

            }

     2、board板级:

           增加nand_read.c文件作用:供start.S中nand flash代码搬运。并修改对应Makefile。(路径:board/Samsung/tx2440/)

           nand_read.c源码

 #include <common.h> 
#include <linux/mtd/nand.h> 
 
 
#define __REGb(x) (*(volatile unsigned char *)(x)) 
#define __REGw(x) (*(volatile unsigned short *)(x)) 
#define __REGi(x) (*(volatile unsigned int *)(x)) 
#define NF_BASE  0x4e000000 
#define NFCONF  __REGi(NF_BASE + 0x0) 
#define NFCONT  __REGi(NF_BASE + 0x4) 
#define NFCMD  __REGb(NF_BASE + 0x8) 
#define NFADDR  __REGb(NF_BASE + 0xc) 
#define NFDATA  __REGb(NF_BASE + 0x10) 
#define NFDATA16 __REGw(NF_BASE + 0x10) 
#define NFSTAT  __REGb(NF_BASE + 0x20) 
#define NFSTAT_BUSY 1 
#define nand_select() (NFCONT &= ~(1 << 1)) 
#define nand_deselect() (NFCONT |= (1 << 1)) 
#define nand_clear_RnB() (NFSTAT |= (1 << 2)) 

#define TACLS   0x07
#define TWRPH0  0x07
#define TWRPH1  0x07

 
 
void nandflash_init(void)
{
   char i;
	// for S3C2440
	NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);	
	NFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
	  for(i=0;i<50;i++);  
}
 
static inline void nand_wait(void) 
{ 
 int i; 
 
 while (!(NFSTAT & NFSTAT_BUSY)) 
  for (i=0; i<10; i++); 
} 
 
struct boot_nand_t { 
 int page_size; 
 int block_size; 
 int bad_block_offset; 
// unsigned long size; 
}; 

static int is_bad_block(struct boot_nand_t * nand, unsigned long i) 
{ 
 unsigned char data; 
 unsigned long page_num; 
 
 nand_clear_RnB(); 
 if (nand->page_size == 512) { 
  NFCMD = NAND_CMD_READOOB; /* 0x50 */ 
  NFADDR = nand->bad_block_offset & 0xf; 
  NFADDR = (i >> 9) & 0xff; 
  NFADDR = (i >> 17) & 0xff; 
  NFADDR = (i >> 25) & 0xff; 
 } else if (nand->page_size == 2048) { 
  page_num = i >> 11; /* addr / 2048 */ 
  NFCMD = NAND_CMD_READ0; 
  NFADDR = nand->bad_block_offset & 0xff; 
  NFADDR = (nand->bad_block_offset >> 8) & 0xff; 
  NFADDR = page_num & 0xff; 
  NFADDR = (page_num >> 8) & 0xff; 
  NFADDR = (page_num >> 16) & 0xff; 
  NFCMD = NAND_CMD_READSTART; 
 } else { 
  return -1; 
 } 
 nand_wait(); 
 data = (NFDATA & 0xff); 
 if (data != 0xff) 
  return 1; 
 
 return 0; 
} 
 
/*struct boot_nand_t { 
 int page_size;   
 int block_size; 
 int bad_block_offset; 
// unsigned long size; 
}; 
/*************************************************************************
** 	参数:	1、*nand 结构体;
			2、buf U-Boot在RAM的开始地址;
			3、addr U-Boot在NAND Flash中的开始地址
**	返回值:成功页读取,返回页容量;失败返回-1。
*/ 
static int nand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned long addr)
{ 
 unsigned short *ptr16 = (unsigned short *)buf; 
 unsigned int i, page_num; 
 
 nand_clear_RnB(); 
 /*#define NAND_CMD_READ0 0 定义在u-boot-2010.06\include\linux\mtd\nand.h中 */
 NFCMD = NAND_CMD_READ0; //读命令
 //Samsung K9F2G08U0B的page_size=2048
 if (nand->page_size == 512) { 
  /* Write Address */ 
  NFADDR = addr & 0xff; 
  NFADDR = (addr >> 9) & 0xff; 
  NFADDR = (addr >> 17) & 0xff; 
  NFADDR = (addr >> 25) & 0xff; 
 } else if (nand->page_size == 2048) { 
 /*NandFlash是以页(Page)为最小单位进行读写的,所以读取时,只需提供页地址和块数即可(不需要考虑页容量内地址)。
   nand flash 物理地址=块大小×块号+页大小×页号+页内地址,在该读程序中页内地址不考虑即page_num = addr >> 11
   K9F2G08U0B的操作用5个周期来实现,具体参照K9F2G08U0B手册
   */
  page_num = addr >> 11; /* addr / 2048 */ 
  /* Write Address */ 
  NFADDR = 0;  //A[7:0]
  NFADDR = 0;  //A[11:8]
  NFADDR = page_num & 0xff; //A[19:12]其中A[17:12]表示页数,A[28:18]表示block块数即2^11=2048块
  NFADDR = (page_num >> 8) & 0xff;  //A[27:20]
  NFADDR = (page_num >> 16) & 0xff; //A28
  NFCMD = NAND_CMD_READSTART; /*#define NAND_CMD_READSTART	0x30*/
 } else { 
  return -1; 
 } 
 nand_wait(); 
 /*此处不明白为什么(nand->page_size>>1)而不是nand->page_size??
   因为每次读取到SDRAM数据是16位(相对于K9F2G08U0B由2440的nand flash控制器控制读取两次8bit数据,组成一个16位数据),
   所以读取次数减少一半(原来读取2048次,现在1024次即可).
   通过裸机的nand程序验证:read  nandflash数据时,支持以16位形式读取即data=NF_RDDATA16()。
   最多支持32位读取。,一般采用8位读取。
 */
 for (i = 0; i < (nand->page_size>>1); i++) {  
  /*#define __REGw(x) (*(volatile unsigned short *)(x)) 
	#define NFDATA16 __REGw(NF_BASE + 0x10)  即NFDATA16对应数据寄存器NFDATA 8位宽nand flash以B半字(16bit)形式访问
(具体实现通过2440的nand flash实现K9F2G08U0B的8位宽转换16位宽。因为K9F2G08U0B的默认操作是8位宽)。为什么这么设置呢?
原因我们将nand flash中代码搬运到SDRAM中,而SDRAM的设置成16位宽。所以此处需要将nand flash修改与之匹配。
  */
  *ptr16 = NFDATA16;   //				   
  ptr16++; 
 } 
 
 return nand->page_size;  
} 

static unsigned short nand_read_id() 
{ 
 unsigned short res = 0; 
 /*#define NAND_CMD_READID 0x90 定义在u-boot-2010.06\include\linux\mtd\nand.h中86行 */
 /*读取nand flash 的ID号 ,nand flash的ID号存放在u-boot-2010.06\include\linux\mtd\nand.h中433行*/
 NFCMD = NAND_CMD_READID; //读ID命令0x90
 NFADDR = 0; //写0x00地址
 res = NFDATA; //读取厂商ID:0xEC
 res = (res << 8) | NFDATA; //读取设备ID0:xDA,并与厂商ID组成一字信息 即0xECDA
 return res;  
} 
 
extern unsigned int dynpart_size[]; 
 
/* low level nand read function */
/*  参数:
        1、*buf:U-Boot在RAM的开始地址即r0 ,由start.S调用处可知:r0=TEXT_BASE
		2、start_addr:U-Boot在NAND Flash中的开始地址 即r1,由start.S调用处可知:r1=0x00
		3、size:复制的大小 即r2。由start.S调用处可知:r2=#LENGTH_UOOT=0x100000 (size=1Mbyte)
	返回值:成功返回0,失败返回-1或挂起
*/  
int nand_copy(unsigned char *buf, unsigned long start_addr, int size) 
{ 
 int i, j; 
 unsigned short nand_id; 
 struct boot_nand_t nand; 
 
 /* chip Enable */ 
 /*#define nand_select() (NFCONT &= ~(1 << 1))  发出片选信号*/
 /*#define nand_clear_RnB() (NFSTAT |= (1 << 2)) 清除RnB位*/
 nand_select(); 
 nand_clear_RnB(); 
  
 for (i = 0; i < 10; i++) 
  ; 
 nand_id = nand_read_id(); //返回的ID:0xECDA
 if (0) { /* dirty little hack to detect if nand id is misread */ 
  unsigned short * nid = (unsigned short *)0x31fffff0; 
  *nid = nand_id; 
 }  
 if (nand_id == 0xec76 ||  /* Samsung K91208 */ 
     nand_id == 0xad76 ) { /*Hynix HY27US08121A*/ 
  nand.page_size = 512; 
  
  nand.block_size = 16 * 1024; 
  nand.bad_block_offset = 5; 
 // nand.size = 0x4000000; 
 } else if (nand_id == 0xecf1 || /* Samsung K9F1G08U0B */ 
     nand_id == 0xecda || /* Samsung K9F2G08U0B */ 
     nand_id == 0xecd3 ) { /* Samsung K9K8G08 */ 
  nand.page_size = 2048;   //页容量2Kbyte
  nand.block_size = 128 * 1024; //1 block=128KByte=128*1024Byte(这里是mian区size,不包括spare区)
  nand.bad_block_offset = nand.page_size; 
 // nand.size = 0x8000000; 
 } else { 
  return -1; // hang 
 } 
 if ((start_addr & (nand.block_size-1)) || (size & ((nand.block_size-1)))) 
  return -1; /* invalid alignment 地址或容量不对齐*/ 
 
 for (i=start_addr; i < (start_addr + size);) 
 { 
	 /*未定义CONFIG_S3C2410_NAND_SKIP_BAD */
	#ifdef CONFIG_S3C2410_NAND_SKIP_BAD    
	  if (i & (nand.block_size-1)== 0) 
	  { 
	   if (is_bad_block(&nand, i) || is_bad_block(&nand, i + nand.page_size)) 
	   { 
		/* Bad block */ 
		i += nand.block_size; 
		size += nand.block_size; 
		continue; 
	   } 
	  } 
#endif 
/*nand_read_page_ll()返回值是2048的页容量。
  &nand 结构体;buf-- U-Boot在RAM的开始地址;i--U-Boot在NAND Flash中的开始地址,
*/
	j = nand_read_page_ll(&nand, buf, i);  //读取每页中内容到初始地址TEXT_BASE = 0x33F80000的SDRAM中
	i += j; //修改页地址
	buf += j; //修改SDRAM中指针地址
 } 
 
 /* chip Disable */ 
 nand_deselect(); //#define nand_deselect() (NFCONT |= (1 << 1)) 
  return 0; 
}  

         3、驱动级

              增加基于S3C2440的nand flash驱动:S3C2440_nand.c(路径:driver/mtd/nand/),并修改对应Makefile。

            Makefile修改如下:

            COBJS-y += s3c2440_nand.o

            COBJS-$(CONFIG_NAND_S3C2410) += s3c2440_nand.o

          4、配置文件

              修改tx2440.h中nand flash配置文件(路径:include/configs/tx2440.h)

            #define CONFIG_CMD_NAND

           /* NAND flash settings */

          #if defined(CONFIG_CMD_NAND)           //支持nand flash command命令?

               #define CONFIG_SYS_NAND_BASE            0x4E000000 //Nand

               #define CONFIG_SYS_MAX_NAND_DEVICE      1 

               #define CONFIG_MTD_NAND_VERIFY_WRITE    1 

         #endif

         #define CONFIG_ENV_IS_IN_NAND 1

         #define CONFIG_ENV_OFFSET   0x100000

        #define CONFIG_ENV_SIZE   0x20000 /* Total Size of Environment Sector */

     5、修改启动链接脚本u-boot.lds

          在arch/arm/cpu/arm920t/u-boot.lds,该文件决定uboot运行的入口地址、各个字段的存储位置,链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nand启动函数放到4K之后,否则是无法启动的。

      .text :

       {

          arch/arm/cpu/arm920t/start.o (.text)

          board/samsung/tx2440/lowlevel_init.o (.text)

          board/samsung/tx2440/nand_read.o (.text)

          *(.text)

      }

     编译

                     # make distclean

                     #make tx2440_config

                     #make -j4

     将编译后uboot.bin下载到nand flash运行。运行界面如下:

       


 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值