一、NAND FLASH的特点
S3C6410的NAND FLASH控制器有如下特点
1、自导入模式:复位后,引导代码被送入到8KB的STEPPINGSTONE中,引导代码移动完毕,引导代码将在STEPPINGSTONE中执行。导入期间,NAND FLASH控制器不支持ECC矫正。
2、NAND FLSH 控制器I/F:支持512字节和2KB页
3、软件模式:用户可以直接访问nand flash 控制器,该特性可以用于读/檫/编程nand flash 存储器。
1)写命令寄存器=NAND FLASH存储器命令周期
2)写地址寄存器=NAND FLASH存储器地址周期
3)写数据寄存器=写数据到NAND FLASH存储器(写周期)
4)读数据寄存器=从NAND FLASH 存储器数据(读周期)
5)读主ECC寄存器和备用ECC寄存器=从NAND FLASH存储器读数据
4、接口:8位NAND FLASH存储器接口总线
5、硬件:ECC产生、检测和标志(软件纠正)
6、支持SLC和MLC的NAND FLASH控制器:1位ECC用于SLC,4位ECC用于MLC的NAND FLASH
7、特殊功能寄存器I/F:支持字节/半字/字数据访问ECC的数据寄存器,用字来访问其他寄存器
8、STEPPINGSTONE I/F:支持字节/半字/字数据的访问
9、8KB的内部SRAM的缓冲器STEPPINGSTONE,在NAND FLASH引导后可做其他用途使用
该Tiny6410 开发板使用的NAND FLASH的类型为MLC大小为2G,型号K9GA08U0E-S
二、驱动设计
第一步:设置NAND FLASH的控制寄存器
通过S3C6410数据手册可知NNAD FLASH的控制寄存器为NFCONF
寄存器 地址
NFCONF 0x70200000
在NFCONF寄存器中主要用到TACLS、TWRPH0、TWRPH1,这三个变量。这三个变量用于配置nand flash 的时序。通过时序图
由上图可知,TACLS为CLE/ALE 有效到nWE有效之间的持续时间。TWRPH0 位nWE的有效持续时间。TWRPH1为nWE无效到CLE/ALE 无效之间的持续时间。这些时间都是以HCLK为单位的。
通过K9F2G08U0E数据手册(如下图)可知tWp 和TWRPH0相对应,(tcls-twp)与TALS相对应,tCLH与TWRPH1相对应。
K9F2G08U0给出的值都是最小值。故在取值是只要满足该最小值就可以了。因此在这里我将TACLS、TWRPH0、TWRPH1分别取值为0x2、0xF和0x7。
故将NFCONF寄存器的设值为((0x2<<12)|(0xf<<8)|(0x7<<4))
第二步:使能NAND FLASH
通过S3C6410的诗句手册可以知道使能NAND FLASH的寄存器为NFCONT
寄存器 地址
NFCONT 0x7020004
由寄存器的描述可知,NFCONT的bit0位是控制NAND FLASH的。故将NFCONT的第零位设值为1即使能了NAND FLASH。
第三步:读操作(页读)
如下图可知读操作主要分五步走
1、发片选:即设置NFCONT的bit1为0
2、发命令:0x00,即往NFCMD写0x00
3、发地址:由下图知地址需要分5个时钟周期来发送的,即往NFADDR写入地址,因为NFADDR一次只能接受8bit的数据
4、发读命令:0x30,即往NFCMD写0x30
5、连续读2048个字节,即连续读NFDATA寄存器2048次。
第四步:拷贝
调用NAND FLASH的读操作,直到把.bin文件完全的从NAND FLASH中拷贝到 DRAM中。
第五步:编码运行
主要代码实现如下
//start.S .global _start _start //把外设告诉CPU ldr r0, =0x70000000 orr r0,r0,#0x13 mcr p15,0,r0,c15,c2,4 //官看门狗 ldr r0,=0x7E004000 mov r1,#0 str r1,[r0] //设置栈 ldr sp,=0x0c002000 //开启icaches #ifdef CONFIG_SYS_ICACHE_OFF bic r0,r0,#0x00001000 #else orr r0,r0,#0x00001000 mcr p15,0,r0,c1,c0,0x00001000 #endif //设置时钟 bl clock_init //初始换sdram bl sdram_init //初始化nand flash bl nand_init //重定位,把代码、数据复制到他的链接中去 adr r0,_start @_start的当前地址 ldr r1,=_star @_start的链接地址 ldr r2,=bss_start sub r2,r2,r1 cmp r0,r1 beq clean_bss bl copy2ddr cmp r0,#0 bne halt //清理bss段,把bss对应的内存清零 clean_bss: ldr r0,=bss_start ldr r1,=bss_end mov r3,#0 cmp r0,r1 beq on_ddr clean_loop: str r3,[r0],#4 cmp r0,r1 bne clean_loop //跳转 on_ddr: ldr pc,=main halt: b halt
//nand.c #include "Tiny6410Addr.h" //nand flash 的命令 #define NAND_CMD_READ0 0 #define NAND_CMD_READ1 1 #define NAND_CMD_RNDOUT 5 #define NAND_CMD_PAGEPROG 0x10 #define NAND_CMD_READOOB 0x50 #define NAND_CMD_ERASE1 0x60 #define NAND_CMD_STATUS 0x70 #define NAND_CMD_STATUS_MULTI 0x71 #define NAND_CMD_SEQIN 0x80 #define NAND_CMD_RNDIN 0x85 #define NAND_CMD_READID 0x90 #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff /* typedef struct{ void (*nand_reset)(void); void (*nand_select_chip)(void); void (*nand_deselect_chip)(void); void (*write_cmd)(int Cmd); void (*read_cmd)(int Cmd); void (*wait_idle)(void); void (*write_addr)(unsigned int addr); }NAND_CHIP; static NAND_CHIP nand_chip; */ void nand_init(void); //Tiny6410 nand Flash 的操作函数申明 static void Tiny6410_nand_reset(void); //重启 static void Tiny6410_nand_select_chip(void); //片选使能 static void Tiny6410_nand_diselect_chip(void); //关闭片选 static void Tiny6410_write_cmd(int cmd); //写命令 static void Tiny6410_read_cmd(int cmd); //读命令 static void Tiny6410_wait_idle(void); //等待 static void Tiny6410_write_addr(unsigned int addr); //写地址 ///////////////////////////////////////// //Tiny6410 nand Flash 的操作函数实现 static void Tiny6410_nand_reset(void) { Tiny6410_nand_select_chip(); Tiny6410_write_cmd(int cmd); Tiny6410_wait_idle(); Tiny6410_nand_diselect_chip(); } //片选使能 static void Tiny6410_nand_select_chip(void) { NFCONT &= ~(1 << 1); } //取消片选 static void Tiny6410_nand_diselect_chip(void) { NFCONT |= (1 << 1); } static void Tiny6410_write_cmd(int cmd) { } //等待数据 static void Tiny6410_wait_idle(void) { do { while(!(NFSTAT & (1 << 0))); } while(0) } //读命令 static void Tiny6410_read_cmd(int cmd) { NFCMD = cmd; } //写命令 static void Tiny6410_write_cmd(int cmd) { //NFCMD = cmd; } static void Tiny6410_write_addr(unsigned long addr) { NFADDR = 0; NFADDR = 0; NFADDR = (addr) & 0xff; NFADDR = (addr >> 8) & 0xff; NFADDR = (addr >> 16) & 0xff; } /////////////////////////////////////// //nand flash 初始化 void init_nand(void) { //设置NAND FLASH 控制器 NFCONF = ((0x2 << 12)|(0xf << 8)|(0x7 << 4)); NFCONT |= ((0x3 << 0)); } //读以页数据 static int nand_read_page(unsigned char *buf,unsigned long addr) { int i; //发出片选 Tiny6410_nand_select_chip(); //发送读命令 Tiny6410_read_cmd(NAND_CMD_READ0); //发送地址 Tiny6410_write_addr(addr); //发送读命令 Tiny6410_read_cmd(NAND_CMD_READSTART); //等待数据 Tiny6410_wait_idle(); //连续读取2048个字节 for(i=0; i < page_size; i++) { *buf++ = NFDATA8_REG; } //取消片选 Tiny6410_nand_diselect_chip(); return 0; } //从NAND 中拷贝到DRAM int copy2dram(unsigned int nand_start,unsigned int dram_start,unsigned int len) { unsigned char *buf = (unsigned char *)ddr_start; int i; unsigned int page_shift = 11; //发片选 Tiny6410_nand_select_chip(); // 使len为2048的整数倍 len = (len/2048+1)*2048; // 循环拷贝,每次拷贝一页数据 for (i = 0; i < (len>>page_shift); i++, buf+=(1<<page_shift)) { // 读一页,即2048byte nandll_read_page(buf, i); } return 0; }
//nand.lds SECTIONS { . = 0x50000000; .text : { start.o clock.o sdram.o nand.o * (.text) } .rodata : { * (.rodata) } .data : { * (.data) } bss_start = .; .bss ALIGN(4) : { *(.bss) *(COMMON) } bss_end = .; }
//TinyAddr.h #ifndef _Tiny6410Addr_H #define _Tiny6410Addr_H //GPK #define GPKIO_BASE (0x7F008800) #define rGPKCON0 (*((volatile unsigned long *)(GPKIO_BASE+0x00))) #define rGPKDAT (*((volatile unsigned long *)(GPKIO_BASE+0x08))) //CLOCK #define APLL_LOCK (*((volatile unsigned long *)0x7E00F000)) #define MPLL_LOCK (*((volatile unsigned long *)0x7E00F004)) #define EPLL_LOCK (*((volatile unsigned long *)0x7E00F008)) #define OTHERS (*((volatile unsigned long *)0x7e00f900)) #define CLK_DIV0 (*((volatile unsigned long *)0x7E00F020)) #define APLL_CON (*((volatile unsigned long *)0x7E00F00C)) #define MPLL_CON (*((volatile unsigned long *)0x7E00F010)) #define CLK_SRC (*((volatile unsigned long *)0x7E00F01C)) //GPA /uart #define ULCON0 (*((volatile unsigned long *)0x7F005000)) #define UCON0 (*((volatile unsigned long *)0x7F005004)) #define UFCON0 (*((volatile unsigned long *)0x7F005008)) #define UMCON0 (*((volatile unsigned long *)0x7F00500C)) #define UTRSTAT0 (*((volatile unsigned long *)0x7F005010)) #define UFSTAT0 (*((volatile unsigned long *)0x7F005018)) #define UTXH0 (*((volatile unsigned char *)0x7F005020)) #define URXH0 (*((volatile unsigned char *)0x7F005024)) #define UBRDIV0 (*((volatile unsigned short *)0x7F005028)) #define UDIVSLOT0 (*((volatile unsigned short *)0x7F00502C)) #define GPACON (*((volatile unsigned long *)0x7F008000)) //Sdram #define P1MEMSTAT (*((volatile unsigned long *)0x7e001000)) #define P1MEMCCMD (*((volatile unsigned long *)0x7e001004)) #define P1DIRECTCMD (*((volatile unsigned long *)0x7e001008)) //#define MEMCCMD (*((volatile unsigned long *)0x7e001004)) #define P1REFRESH (*((volatile unsigned long *)0x7e001010)) #define P1CASLAT (*((volatile unsigned long *)0x7e001014)) #define MEM_SYS_CFG (*((volatile unsigned long *)0x7e00f120)) #define P1MEMCFG (*((volatile unsigned long *)0x7e00100c)) #define P1T_DQSS (*((volatile unsigned long *)0x7e001018)) #define P1T_MRD (*((volatile unsigned long *)0x7e00101c)) #define P1T_RAS (*((volatile unsigned long *)0x7e001020)) #define P1T_RC (*((volatile unsigned long *)0x7e001024)) #define P1T_RCD (*((volatile unsigned long *)0x7e001028)) #define P1T_RFC (*((volatile unsigned long *)0x7e00102c)) #define P1T_RP (*((volatile unsigned long *)0x7e001030)) #define P1T_RRD (*((volatile unsigned long *)0x7e001034)) #define P1T_WR (*((volatile unsigned long *)0x7e001038)) #define P1T_WTR (*((volatile unsigned long *)0x7e00103c)) #define P1T_XP (*((volatile unsigned long *)0x7e001040)) #define P1T_XSR (*((volatile unsigned long *)0x7e001044)) #define P1T_ESR (*((volatile unsigned long *)0x7e001048)) #define P1MEMCFG2 (*((volatile unsigned long *)0X7e00104c)) #define P1_chip_0_cfg (*((volatile unsigned long *)0x7e001200)) //Nand #define NAND_BASE (0x70200000) #define NFCONF (*((volatile unsigned long *)NAND_BASE + 0x00)) #define NFCONT (*((volatile unsigned long *)NAND_BASE + 0x04)) #define NFCMMD (*((volatile unsigned long *)NAND_BASE + 0x08)) #define NFADDR (*((volatile unsigned long *)NAND_BASE + 0x0c)) #define NFDATA (*((volatile unsigned long *)NAND_BASE + 0x10)) #define NFMECCDATA0 (*((volatile unsigned long *)NAND_BASE + 0x14)) #define NFMECCDATA1 (*((volatile unsigned long *)NAND_BASE + 0x18)) #define NFSECCDATA0 (*((volatile unsigned long *)NAND_BASE + 0x1c)) #define NFSBLK (*((volatile unsigned long *)NAND_BASE + 0x20)) #define NFEBLK (*((volatile unsigned long *)NAND_BASE + 0x24)) #define NFSTAT (*((volatile unsigned long *)NAND_BASE + 0x28)) #define NFESTAT0 (*((volatile unsigned long *)NAND_BASE + 0x2c)) #define NFESTAT1 (*((volatile unsigned long *)NAND_BASE + 030)) #define NFMECC0 (*((volatile unsigned long *)NAND_BASE + 0x34)) #define NFMECC1 (*((volatile unsigned long *)NAND_BASE + 0x38)) #define NFSECC (*((volatile unsigned long *)NAND_BASE + 0x3c)) #define NFMLCBITPT (*((volatile unsigned long *)NAND_BASE + 0x40)) /*#define NF8ECCERR0 (*((volatile unsigned long *)NAND_BASE + 0x44)) #define NF8ECCERR1 (*((volatile unsigned long *)NAND_BASE + 0x48)) #define NF8ECCERR2 (*((volatile unsigned long *)NAND_BASE + 0x4c)) #define NFM8ECC0 (*((volatile unsigned long *)NAND_BASE + 0x50)) #define NFM8ECC1 (*((volatile unsigned long *)NAND_BASE + 0x54)) #define NFM8ECC2 (*((volatile unsigned long *)NAND_BASE + 0x58)) #define NFM8ECC3 (*((volatile unsigned long *)NAND_BASE + 0x5c)) #define NFMLC8BITPT0 (*((volatile unsigned long *)NAND_BASE + 0x60)) #define NFMLC8BITPT1 (*((volatile unsigned long *)NAND_BASE + 0x64)) */ #endif
总结:
综上所述主要分为两个大步。
第一步:读S3C6410数据手册,了解NANDfLASH 的操作流程。
第二步:读NAND FLASH数据手册,了解各个寄存器的具体设置。